From 4c222a239ab108e5bdc3ad4e122e0be17fda14cd Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Wed, 29 Sep 2021 22:34:56 +0800 Subject: [PATCH] Download album --- lib/download_handler.dart | 13 ++++++++-- lib/mobile/file_downloader.dart | 12 ++++++++- lib/platform/file_downloader.dart | 4 +++ lib/use_case/download_file.dart | 4 +-- lib/web/file_downloader.dart | 1 + lib/widget/album_browser.dart | 34 +++++++++++++++++++++++++- lib/widget/dynamic_album_browser.dart | 35 ++++++++++++++++++++------- 7 files changed, 88 insertions(+), 15 deletions(-) diff --git a/lib/download_handler.dart b/lib/download_handler.dart index 139159af..d1b78413 100644 --- a/lib/download_handler.dart +++ b/lib/download_handler.dart @@ -14,7 +14,11 @@ import 'package:nc_photos/use_case/download_file.dart'; import 'package:tuple/tuple.dart'; class DownloadHandler { - Future downloadFiles(Account account, List files) async { + Future downloadFiles( + Account account, + List files, { + String? parentDir, + }) async { _log.info("[downloadFiles] Downloading ${files.length} file"); var controller = SnackBarManager().showSnackBar(SnackBar( content: Text(L10n.global().downloadProcessingNotification), @@ -26,7 +30,12 @@ class DownloadHandler { final successes = >[]; for (final f in files) { try { - successes.add(Tuple2(f, await DownloadFile()(account, f))); + final result = await DownloadFile()( + account, + f, + parentDir: parentDir, + ); + successes.add(Tuple2(f, result)); } on PermissionException catch (_) { _log.warning("[downloadFiles] Permission not granted"); controller?.close(); diff --git a/lib/mobile/file_downloader.dart b/lib/mobile/file_downloader.dart index f22ec9c6..06397674 100644 --- a/lib/mobile/file_downloader.dart +++ b/lib/mobile/file_downloader.dart @@ -15,6 +15,7 @@ class FileDownloader extends itf.FileDownloader { Map? headers, String? mimeType, required String filename, + String? parentDir, bool? shouldNotify, }) { if (platform_k.isAndroid) { @@ -23,6 +24,7 @@ class FileDownloader extends itf.FileDownloader { headers: headers, mimeType: mimeType, filename: filename, + parentDir: parentDir, shouldNotify: shouldNotify, ); } else { @@ -35,15 +37,23 @@ class FileDownloader extends itf.FileDownloader { Map? headers, String? mimeType, required String filename, + String? parentDir, bool? shouldNotify, }) async { + final String path; + if (parentDir?.isNotEmpty == true) { + path = "$parentDir/$filename"; + } else { + path = filename; + } + try { _log.info("[_downloadUrlAndroid] Start downloading '$url'"); final id = await Download.downloadUrl( url: url, headers: headers, mimeType: mimeType, - filename: filename, + filename: path, shouldNotify: shouldNotify, ); late final String uri; diff --git a/lib/platform/file_downloader.dart b/lib/platform/file_downloader.dart index 20b5be6f..19b6eab7 100644 --- a/lib/platform/file_downloader.dart +++ b/lib/platform/file_downloader.dart @@ -5,6 +5,9 @@ abstract class FileDownloader { /// - web: null /// - android: Uri to the downloaded file /// + /// [parentDir] is a hint that set the parent directory where the files are + /// saved. Whether this is supported or not is implementation specific + /// /// [shouldNotify] is a hint that suggest whether to notify user about the /// progress. The actual decision is made by the underlying platform code and /// is not guaranteed to respect this flag @@ -13,6 +16,7 @@ abstract class FileDownloader { Map? headers, String? mimeType, required String filename, + String? parentDir, bool? shouldNotify, }); } diff --git a/lib/use_case/download_file.dart b/lib/use_case/download_file.dart index f3c0b598..7fa9d94f 100644 --- a/lib/use_case/download_file.dart +++ b/lib/use_case/download_file.dart @@ -6,14 +6,13 @@ import 'package:nc_photos/mobile/platform.dart' import 'package:path/path.dart' as path; class DownloadFile { - DownloadFile(); - /// Download [file] /// /// See [FileDownloader.downloadUrl] Future call( Account account, File file, { + String? parentDir, bool? shouldNotify, }) { final downloader = platform.FileDownloader(); @@ -25,6 +24,7 @@ class DownloadFile { }, mimeType: file.contentType, filename: path.basename(file.path), + parentDir: parentDir, shouldNotify: shouldNotify, ); } diff --git a/lib/web/file_downloader.dart b/lib/web/file_downloader.dart index 322bcba0..c96ddf49 100644 --- a/lib/web/file_downloader.dart +++ b/lib/web/file_downloader.dart @@ -10,6 +10,7 @@ class FileDownloader extends itf.FileDownloader { Map? headers, String? mimeType, required String filename, + String? parentDir, bool? shouldNotify, }) async { final uri = Uri.parse(url); diff --git a/lib/widget/album_browser.dart b/lib/widget/album_browser.dart index e852ca35..3d38ce2b 100644 --- a/lib/widget/album_browser.dart +++ b/lib/widget/album_browser.dart @@ -232,7 +232,18 @@ class _AlbumBrowserState extends State } else if (isSelectionMode) { return _buildSelectionAppBar(context); } else { - return buildNormalAppBar(context, widget.account, _album!); + return buildNormalAppBar( + context, + widget.account, + _album!, + menuItemBuilder: (_) => [ + PopupMenuItem( + value: _menuValueDownload, + child: Text(L10n.global().downloadTooltip), + ), + ], + onSelectedMenuItem: (option) => _onMenuSelected(context, option), + ); } } @@ -294,6 +305,25 @@ class _AlbumBrowserState extends State album: _album)); } + void _onMenuSelected(BuildContext context, int option) { + switch (option) { + case _menuValueDownload: + _onDownloadPressed(); + break; + default: + _log.shout("[_onMenuSelected] Unknown option: $option"); + break; + } + } + + void _onDownloadPressed() { + DownloadHandler().downloadFiles( + widget.account, + _sortedItems.whereType().map((e) => e.file).toList(), + parentDir: _album!.name, + ); + } + void _onSelectionAppBarSharePressed(BuildContext context) { assert(platform_k.isAndroid); final selected = selectedListItems @@ -666,6 +696,8 @@ class _AlbumBrowserState extends State late AppEventListener _albumUpdatedListener; static final _log = Logger("widget.album_browser._AlbumBrowserState"); + + static const _menuValueDownload = 0; } enum _SelectionMenuOption { diff --git a/lib/widget/dynamic_album_browser.dart b/lib/widget/dynamic_album_browser.dart index 696ba510..cd885878 100644 --- a/lib/widget/dynamic_album_browser.dart +++ b/lib/widget/dynamic_album_browser.dart @@ -223,24 +223,32 @@ class _DynamicAlbumBrowserState extends State } Widget _buildNormalAppBar(BuildContext context) { + final menuItems = >[ + PopupMenuItem( + value: _menuValueDownload, + child: Text(L10n.global().downloadTooltip), + ), + ]; + if (canEdit) { + menuItems.add(PopupMenuItem( + value: _menuValueConvertBasic, + child: Text(L10n.global().convertBasicAlbumMenuLabel), + )); + } + return buildNormalAppBar( context, widget.account, _album!, - menuItemBuilder: canEdit - ? (context) => [ - PopupMenuItem( - value: _menuValueConvertBasic, - child: Text(L10n.global().convertBasicAlbumMenuLabel), - ), - ] - : null, + menuItemBuilder: (_) => menuItems, onSelectedMenuItem: (option) { switch (option) { case _menuValueConvertBasic: _onAppBarConvertBasicPressed(context); break; - + case _menuValueDownload: + _onDownloadPressed(); + break; default: _log.shout("[_buildNormalAppBar] Unknown value: $option"); break; @@ -358,6 +366,14 @@ class _DynamicAlbumBrowserState extends State } } + void _onDownloadPressed() { + DownloadHandler().downloadFiles( + widget.account, + _sortedItems.whereType().map((e) => e.file).toList(), + parentDir: _album!.name, + ); + } + void _onSelectionAppBarSharePressed(BuildContext context) { assert(platform_k.isAndroid); final selected = selectedListItems @@ -576,6 +592,7 @@ class _DynamicAlbumBrowserState extends State static final _log = Logger("widget.dynamic_album_browser._DynamicAlbumBrowserState"); static const _menuValueConvertBasic = 0; + static const _menuValueDownload = 1; } enum _SelectionMenuOption {