Fix some known issues and add some new functions
Android APK Release / Build Android APK (push) Failing after 52m53s

This commit is contained in:
2026-05-25 20:03:59 +08:00
parent 74abb4a1c5
commit 39d8361080
8 changed files with 587 additions and 221 deletions
+204 -69
View File
@@ -56,7 +56,10 @@ class _FilesPageState extends State<FilesPage> {
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted) {
final fileManager = Provider.of<FileManagerProvider>(context, listen: false);
final fileManager = Provider.of<FileManagerProvider>(
context,
listen: false,
);
final screenWidth = MediaQuery.of(context).size.width;
if (screenWidth >= 1000) {
fileManager.setViewType(FileViewType.grid);
@@ -67,7 +70,10 @@ class _FilesPageState extends State<FilesPage> {
fileManager.loadFiles();
_isFirstLoad = false;
}
final downloadManager = Provider.of<DownloadManagerProvider>(context, listen: false);
final downloadManager = Provider.of<DownloadManagerProvider>(
context,
listen: false,
);
downloadManager.initialize();
}
});
@@ -75,8 +81,13 @@ class _FilesPageState extends State<FilesPage> {
// 上传完成 → 自动刷新当前目录文件列表
UploadService.instance.onUploadCompleted = (targetPath, fileName) {
if (!mounted) return;
final fileManager = Provider.of<FileManagerProvider>(context, listen: false);
final normalizedCurrent = FileUtils.toCloudreveUri(fileManager.currentPath);
final fileManager = Provider.of<FileManagerProvider>(
context,
listen: false,
);
final normalizedCurrent = FileUtils.toCloudreveUri(
fileManager.currentPath,
);
if (targetPath == normalizedCurrent) {
final fileUri = targetPath.endsWith('/')
? '$targetPath$fileName'
@@ -168,8 +179,14 @@ class _FilesPageState extends State<FilesPage> {
if (fileManager.currentPath == '/') {
return const Text('文件');
}
final segments = fileManager.currentPath.split('/').where((s) => s.isNotEmpty).toList();
return Text(segments.isNotEmpty ? segments.last : '文件');
final segments = FileUtils.toRelativePath(
fileManager.currentPath,
).split('/').where((s) => s.isNotEmpty).toList();
return Text(
segments.isNotEmpty
? FileUtils.decodePathSegment(segments.last)
: '文件',
);
}
return _buildMobileBreadcrumb(context, fileManager);
},
@@ -178,10 +195,15 @@ class _FilesPageState extends State<FilesPage> {
);
}
Widget _buildMobileBreadcrumb(BuildContext context, FileManagerProvider fileManager) {
Widget _buildMobileBreadcrumb(
BuildContext context,
FileManagerProvider fileManager,
) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final pathParts = fileManager.currentPath.split('/');
final pathParts = FileUtils.toRelativePath(
fileManager.currentPath,
).split('/');
pathParts.removeWhere((part) => part.isEmpty);
return SizedBox(
@@ -194,22 +216,30 @@ class _FilesPageState extends State<FilesPage> {
label: '文件',
icon: LucideIcons.home,
color: colorScheme.primary,
onTap: () => fileManager.currentPath != '/' ? fileManager.enterFolder('/') : null,
onTap: () => fileManager.currentPath != '/'
? fileManager.enterFolder('/')
: null,
),
for (int i = 0; i < pathParts.length; i++) ...[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: Icon(LucideIcons.chevronRight, size: 14, color: theme.hintColor.withValues(alpha: 0.5)),
child: Icon(
LucideIcons.chevronRight,
size: 14,
color: theme.hintColor.withValues(alpha: 0.5),
),
),
_buildBreadcrumbChip(
context,
label: pathParts[i],
label: FileUtils.decodePathSegment(pathParts[i]),
icon: null,
color: colorScheme.primary,
isLast: i == pathParts.length - 1,
onTap: () {
final targetPath = '/${pathParts.sublist(0, i + 1).join('/')}';
if (targetPath != fileManager.currentPath) fileManager.enterFolder(targetPath);
if (targetPath != fileManager.currentPath) {
fileManager.enterFolder(targetPath);
}
},
),
],
@@ -233,7 +263,9 @@ class _FilesPageState extends State<FilesPage> {
height: 28,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: isLast ? color.withValues(alpha: 0.15) : color.withValues(alpha: 0.06),
color: isLast
? color.withValues(alpha: 0.15)
: color.withValues(alpha: 0.06),
borderRadius: BorderRadius.circular(14),
),
child: Row(
@@ -267,7 +299,9 @@ class _FilesPageState extends State<FilesPage> {
Consumer<FileManagerProvider>(
builder: (context, fileManager, child) {
return IconButton(
icon: Icon(fileManager.isLoading ? Icons.hourglass_empty : Icons.refresh),
icon: Icon(
fileManager.isLoading ? Icons.hourglass_empty : Icons.refresh,
),
onPressed: () => fileManager.refreshFiles(),
tooltip: '刷新',
);
@@ -287,14 +321,19 @@ class _FilesPageState extends State<FilesPage> {
: FileViewType.list,
);
},
tooltip: fileManager.viewType == FileViewType.list ? '网格视图' : '列表视图',
tooltip: fileManager.viewType == FileViewType.list
? '网格视图'
: '列表视图',
);
},
),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
final fileManager = Provider.of<FileManagerProvider>(context, listen: false);
final fileManager = Provider.of<FileManagerProvider>(
context,
listen: false,
);
FileOperationDialogs.showCreateDialog(context, fileManager);
},
tooltip: '新建',
@@ -306,7 +345,8 @@ class _FilesPageState extends State<FilesPage> {
),
IconButton(
icon: const Icon(Icons.cloud_download),
onPressed: () => Provider.of<NavigationProvider>(context, listen: false).setIndex(2),
onPressed: () =>
Provider.of<NavigationProvider>(context, listen: false).setIndex(2),
tooltip: '下载',
),
];
@@ -328,7 +368,9 @@ class _FilesPageState extends State<FilesPage> {
: FileViewType.list,
);
},
tooltip: fileManager.viewType == FileViewType.list ? '网格视图' : '列表视图',
tooltip: fileManager.viewType == FileViewType.list
? '网格视图'
: '列表视图',
);
},
),
@@ -380,8 +422,16 @@ class _FilesPageState extends State<FilesPage> {
isDark: isDark,
colorScheme: colorScheme,
onTap: () {
final fileManager = Provider.of<FileManagerProvider>(context, listen: false);
_onFabSubAction(() => FileOperationDialogs.showCreateDialog(context, fileManager));
final fileManager = Provider.of<FileManagerProvider>(
context,
listen: false,
);
_onFabSubAction(
() => FileOperationDialogs.showCreateDialog(
context,
fileManager,
),
);
},
),
_buildFabSubItem(
@@ -391,7 +441,10 @@ class _FilesPageState extends State<FilesPage> {
label: '离线下载',
isDark: isDark,
colorScheme: colorScheme,
onTap: () => _onFabSubAction(() => Navigator.of(context).pushNamed(RouteNames.remoteDownload)),
onTap: () => _onFabSubAction(
() =>
Navigator.of(context).pushNamed(RouteNames.remoteDownload),
),
),
Consumer<FileManagerProvider>(
builder: (context, fileManager, _) {
@@ -477,7 +530,10 @@ class _FilesPageState extends State<FilesPage> {
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 7,
),
decoration: BoxDecoration(
color: isDark
? Colors.white.withValues(alpha: 0.12)
@@ -558,7 +614,8 @@ class _FilesPageState extends State<FilesPage> {
final isDesktop = MediaQuery.of(context).size.width >= 1000;
final child = _buildFileList(context);
if (!isDesktop || !Platform.isWindows && !Platform.isLinux && !Platform.isMacOS) {
if (!isDesktop ||
!Platform.isWindows && !Platform.isLinux && !Platform.isMacOS) {
return child;
}
@@ -582,13 +639,19 @@ class _FilesPageState extends State<FilesPage> {
strokeAlign: BorderSide.strokeAlignOutside,
),
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.surface.withValues(alpha: 0.85),
color: Theme.of(
context,
).colorScheme.surface.withValues(alpha: 0.85),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(LucideIcons.upload, size: 48, color: Theme.of(context).colorScheme.primary),
Icon(
LucideIcons.upload,
size: 48,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 12),
Text(
'释放文件以上传到当前目录',
@@ -618,8 +681,14 @@ class _FilesPageState extends State<FilesPage> {
}
if (files.isEmpty) return;
final uploadManager = Provider.of<UploadManagerProvider>(context, listen: false);
final fileManager = Provider.of<FileManagerProvider>(context, listen: false);
final uploadManager = Provider.of<UploadManagerProvider>(
context,
listen: false,
);
final fileManager = Provider.of<FileManagerProvider>(
context,
listen: false,
);
uploadManager.markShouldShowDialog();
uploadManager.startUpload(files, fileManager.currentPath);
ToastHelper.info('已添加 ${files.length} 个文件到上传队列');
@@ -649,12 +718,19 @@ class _FilesPageState extends State<FilesPage> {
);
}
Widget _buildErrorView(BuildContext context, FileManagerProvider fileManager) {
Widget _buildErrorView(
BuildContext context,
FileManagerProvider fileManager,
) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Theme.of(context).colorScheme.error),
Icon(
Icons.error_outline,
size: 64,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(height: 16),
Text(
fileManager.errorMessage!,
@@ -705,7 +781,9 @@ class _FilesPageState extends State<FilesPage> {
itemCount: fileManager.files.length,
itemBuilder: (context, index) {
final file = fileManager.files[index];
final isSelected = fileManager.selectedFiles.contains(file.path);
final isSelected = fileManager.selectedFiles.contains(
file.path,
);
return FileListItem(
key: ValueKey('file_${file.id}'),
@@ -727,13 +805,37 @@ class _FilesPageState extends State<FilesPage> {
}
},
onSelect: () => fileManager.toggleSelection(file.path),
onDownload: !file.isFolder ? () => _downloadFile(context, fileManager, file) : null,
onOpenInBrowser: !file.isFolder ? () => _openInBrowser(context, file) : null,
onRename: () => FileOperationDialogs.showRenameDialog(context, fileManager, file),
onMove: () => FileOperationDialogs.showMoveDialog(context, fileManager, file, false),
onCopy: () => FileOperationDialogs.showMoveDialog(context, fileManager, file, true),
onShare: () => FileOperationDialogs.showShareDialog(context, file),
onDelete: () => FileOperationDialogs.showDeleteSingleConfirmation(context, fileManager, file),
onDownload: !file.isFolder
? () => _downloadFile(context, fileManager, file)
: null,
onOpenInBrowser: !file.isFolder
? () => _openInBrowser(context, file)
: null,
onRename: () => FileOperationDialogs.showRenameDialog(
context,
fileManager,
file,
),
onMove: () => FileOperationDialogs.showMoveDialog(
context,
fileManager,
file,
false,
),
onCopy: () => FileOperationDialogs.showMoveDialog(
context,
fileManager,
file,
true,
),
onShare: () =>
FileOperationDialogs.showShareDialog(context, file),
onDelete: () =>
FileOperationDialogs.showDeleteSingleConfirmation(
context,
fileManager,
file,
),
onInfo: () => _showFileInfo(file),
);
},
@@ -762,7 +864,8 @@ class _FilesPageState extends State<FilesPage> {
crossAxisCount = 5;
}
final itemWidth = (availableWidth - spacing * (crossAxisCount - 1)) / crossAxisCount;
final itemWidth =
(availableWidth - spacing * (crossAxisCount - 1)) / crossAxisCount;
final childAspectRatio = itemWidth / 160;
final showCheckbox = fileManager.hasSelection;
@@ -802,13 +905,36 @@ class _FilesPageState extends State<FilesPage> {
}
},
onSelect: () => fileManager.toggleSelection(file.path),
onDownload: !file.isFolder ? () => _downloadFile(context, fileManager, file) : null,
onOpenInBrowser: !file.isFolder ? () => _openInBrowser(context, file) : null,
onRename: () => FileOperationDialogs.showRenameDialog(context, fileManager, file),
onMove: () => FileOperationDialogs.showMoveDialog(context, fileManager, file, false),
onCopy: () => FileOperationDialogs.showMoveDialog(context, fileManager, file, true),
onShare: () => FileOperationDialogs.showShareDialog(context, file),
onDelete: () => FileOperationDialogs.showDeleteSingleConfirmation(context, fileManager, file),
onDownload: !file.isFolder
? () => _downloadFile(context, fileManager, file)
: null,
onOpenInBrowser: !file.isFolder
? () => _openInBrowser(context, file)
: null,
onRename: () => FileOperationDialogs.showRenameDialog(
context,
fileManager,
file,
),
onMove: () => FileOperationDialogs.showMoveDialog(
context,
fileManager,
file,
false,
),
onCopy: () => FileOperationDialogs.showMoveDialog(
context,
fileManager,
file,
true,
),
onShare: () =>
FileOperationDialogs.showShareDialog(context, file),
onDelete: () => FileOperationDialogs.showDeleteSingleConfirmation(
context,
fileManager,
file,
),
onInfo: () => _showFileInfo(file),
);
},
@@ -829,30 +955,30 @@ class _FilesPageState extends State<FilesPage> {
onCancel: () => fileManager.clearSelection(),
onRename: fileManager.selectedFiles.length == 1
? () => FileOperationDialogs.showRenameDialog(
context,
fileManager,
fileManager.files.firstWhere(
(f) => f.path == fileManager.selectedFiles.first,
),
)
context,
fileManager,
fileManager.files.firstWhere(
(f) => f.path == fileManager.selectedFiles.first,
),
)
: null,
onMove: () => FileOperationDialogs.showBatchMoveDialog(
context,
fileManager,
fileManager.selectedFiles,
false,
),
context,
fileManager,
fileManager.selectedFiles,
false,
),
onCopy: () => FileOperationDialogs.showBatchMoveDialog(
context,
fileManager,
fileManager.selectedFiles,
true,
),
context,
fileManager,
fileManager.selectedFiles,
true,
),
onDelete: () => FileOperationDialogs.showDeleteConfirmation(
context,
fileManager,
fileManager.selectedFiles,
),
context,
fileManager,
fileManager.selectedFiles,
),
);
}
@@ -876,11 +1002,17 @@ class _FilesPageState extends State<FilesPage> {
} else if (FileTypeUtils.isAudio(file.name)) {
Navigator.of(context).pushNamed(RouteNames.audioPreview, arguments: file);
} else if (FileTypeUtils.isMarkdown(file.name)) {
Navigator.of(context).pushNamed(RouteNames.markdownPreview, arguments: file);
Navigator.of(
context,
).pushNamed(RouteNames.markdownPreview, arguments: file);
} else if (FileTypeUtils.isTextCode(file.name)) {
Navigator.of(context).pushNamed(RouteNames.documentPreview, arguments: file);
Navigator.of(
context,
).pushNamed(RouteNames.documentPreview, arguments: file);
} else {
ToastHelper.info('暂不支持预览 ${FileTypeUtils.getFileTypeDescription(file.name)}');
ToastHelper.info(
'暂不支持预览 ${FileTypeUtils.getFileTypeDescription(file.name)}',
);
}
}
@@ -889,7 +1021,10 @@ class _FilesPageState extends State<FilesPage> {
FileManagerProvider fileManager,
FileModel file,
) async {
final downloadManager = Provider.of<DownloadManagerProvider>(context, listen: false);
final downloadManager = Provider.of<DownloadManagerProvider>(
context,
listen: false,
);
final task = await downloadManager.addDownloadTask(
fileName: file.name,
fileUri: file.relativePath,