Download album

This commit is contained in:
Ming Ming 2021-09-29 22:34:56 +08:00
parent e7f510ac4f
commit 4c222a239a
7 changed files with 88 additions and 15 deletions

View file

@ -14,7 +14,11 @@ import 'package:nc_photos/use_case/download_file.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
class DownloadHandler { class DownloadHandler {
Future<void> downloadFiles(Account account, List<File> files) async { Future<void> downloadFiles(
Account account,
List<File> files, {
String? parentDir,
}) async {
_log.info("[downloadFiles] Downloading ${files.length} file"); _log.info("[downloadFiles] Downloading ${files.length} file");
var controller = SnackBarManager().showSnackBar(SnackBar( var controller = SnackBarManager().showSnackBar(SnackBar(
content: Text(L10n.global().downloadProcessingNotification), content: Text(L10n.global().downloadProcessingNotification),
@ -26,7 +30,12 @@ class DownloadHandler {
final successes = <Tuple2<File, dynamic>>[]; final successes = <Tuple2<File, dynamic>>[];
for (final f in files) { for (final f in files) {
try { 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 (_) { } on PermissionException catch (_) {
_log.warning("[downloadFiles] Permission not granted"); _log.warning("[downloadFiles] Permission not granted");
controller?.close(); controller?.close();

View file

@ -15,6 +15,7 @@ class FileDownloader extends itf.FileDownloader {
Map<String, String>? headers, Map<String, String>? headers,
String? mimeType, String? mimeType,
required String filename, required String filename,
String? parentDir,
bool? shouldNotify, bool? shouldNotify,
}) { }) {
if (platform_k.isAndroid) { if (platform_k.isAndroid) {
@ -23,6 +24,7 @@ class FileDownloader extends itf.FileDownloader {
headers: headers, headers: headers,
mimeType: mimeType, mimeType: mimeType,
filename: filename, filename: filename,
parentDir: parentDir,
shouldNotify: shouldNotify, shouldNotify: shouldNotify,
); );
} else { } else {
@ -35,15 +37,23 @@ class FileDownloader extends itf.FileDownloader {
Map<String, String>? headers, Map<String, String>? headers,
String? mimeType, String? mimeType,
required String filename, required String filename,
String? parentDir,
bool? shouldNotify, bool? shouldNotify,
}) async { }) async {
final String path;
if (parentDir?.isNotEmpty == true) {
path = "$parentDir/$filename";
} else {
path = filename;
}
try { try {
_log.info("[_downloadUrlAndroid] Start downloading '$url'"); _log.info("[_downloadUrlAndroid] Start downloading '$url'");
final id = await Download.downloadUrl( final id = await Download.downloadUrl(
url: url, url: url,
headers: headers, headers: headers,
mimeType: mimeType, mimeType: mimeType,
filename: filename, filename: path,
shouldNotify: shouldNotify, shouldNotify: shouldNotify,
); );
late final String uri; late final String uri;

View file

@ -5,6 +5,9 @@ abstract class FileDownloader {
/// - web: null /// - web: null
/// - android: Uri to the downloaded file /// - 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 /// [shouldNotify] is a hint that suggest whether to notify user about the
/// progress. The actual decision is made by the underlying platform code and /// progress. The actual decision is made by the underlying platform code and
/// is not guaranteed to respect this flag /// is not guaranteed to respect this flag
@ -13,6 +16,7 @@ abstract class FileDownloader {
Map<String, String>? headers, Map<String, String>? headers,
String? mimeType, String? mimeType,
required String filename, required String filename,
String? parentDir,
bool? shouldNotify, bool? shouldNotify,
}); });
} }

View file

@ -6,14 +6,13 @@ import 'package:nc_photos/mobile/platform.dart'
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
class DownloadFile { class DownloadFile {
DownloadFile();
/// Download [file] /// Download [file]
/// ///
/// See [FileDownloader.downloadUrl] /// See [FileDownloader.downloadUrl]
Future<dynamic> call( Future<dynamic> call(
Account account, Account account,
File file, { File file, {
String? parentDir,
bool? shouldNotify, bool? shouldNotify,
}) { }) {
final downloader = platform.FileDownloader(); final downloader = platform.FileDownloader();
@ -25,6 +24,7 @@ class DownloadFile {
}, },
mimeType: file.contentType, mimeType: file.contentType,
filename: path.basename(file.path), filename: path.basename(file.path),
parentDir: parentDir,
shouldNotify: shouldNotify, shouldNotify: shouldNotify,
); );
} }

View file

@ -10,6 +10,7 @@ class FileDownloader extends itf.FileDownloader {
Map<String, String>? headers, Map<String, String>? headers,
String? mimeType, String? mimeType,
required String filename, required String filename,
String? parentDir,
bool? shouldNotify, bool? shouldNotify,
}) async { }) async {
final uri = Uri.parse(url); final uri = Uri.parse(url);

View file

@ -232,7 +232,18 @@ class _AlbumBrowserState extends State<AlbumBrowser>
} else if (isSelectionMode) { } else if (isSelectionMode) {
return _buildSelectionAppBar(context); return _buildSelectionAppBar(context);
} else { } 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<AlbumBrowser>
album: _album)); 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<AlbumFileItem>().map((e) => e.file).toList(),
parentDir: _album!.name,
);
}
void _onSelectionAppBarSharePressed(BuildContext context) { void _onSelectionAppBarSharePressed(BuildContext context) {
assert(platform_k.isAndroid); assert(platform_k.isAndroid);
final selected = selectedListItems final selected = selectedListItems
@ -666,6 +696,8 @@ class _AlbumBrowserState extends State<AlbumBrowser>
late AppEventListener<AlbumUpdatedEvent> _albumUpdatedListener; late AppEventListener<AlbumUpdatedEvent> _albumUpdatedListener;
static final _log = Logger("widget.album_browser._AlbumBrowserState"); static final _log = Logger("widget.album_browser._AlbumBrowserState");
static const _menuValueDownload = 0;
} }
enum _SelectionMenuOption { enum _SelectionMenuOption {

View file

@ -223,24 +223,32 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
} }
Widget _buildNormalAppBar(BuildContext context) { Widget _buildNormalAppBar(BuildContext context) {
final menuItems = <PopupMenuEntry<int>>[
PopupMenuItem(
value: _menuValueDownload,
child: Text(L10n.global().downloadTooltip),
),
];
if (canEdit) {
menuItems.add(PopupMenuItem(
value: _menuValueConvertBasic,
child: Text(L10n.global().convertBasicAlbumMenuLabel),
));
}
return buildNormalAppBar( return buildNormalAppBar(
context, context,
widget.account, widget.account,
_album!, _album!,
menuItemBuilder: canEdit menuItemBuilder: (_) => menuItems,
? (context) => [
PopupMenuItem(
value: _menuValueConvertBasic,
child: Text(L10n.global().convertBasicAlbumMenuLabel),
),
]
: null,
onSelectedMenuItem: (option) { onSelectedMenuItem: (option) {
switch (option) { switch (option) {
case _menuValueConvertBasic: case _menuValueConvertBasic:
_onAppBarConvertBasicPressed(context); _onAppBarConvertBasicPressed(context);
break; break;
case _menuValueDownload:
_onDownloadPressed();
break;
default: default:
_log.shout("[_buildNormalAppBar] Unknown value: $option"); _log.shout("[_buildNormalAppBar] Unknown value: $option");
break; break;
@ -358,6 +366,14 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
} }
} }
void _onDownloadPressed() {
DownloadHandler().downloadFiles(
widget.account,
_sortedItems.whereType<AlbumFileItem>().map((e) => e.file).toList(),
parentDir: _album!.name,
);
}
void _onSelectionAppBarSharePressed(BuildContext context) { void _onSelectionAppBarSharePressed(BuildContext context) {
assert(platform_k.isAndroid); assert(platform_k.isAndroid);
final selected = selectedListItems final selected = selectedListItems
@ -576,6 +592,7 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
static final _log = static final _log =
Logger("widget.dynamic_album_browser._DynamicAlbumBrowserState"); Logger("widget.dynamic_album_browser._DynamicAlbumBrowserState");
static const _menuValueConvertBasic = 0; static const _menuValueConvertBasic = 0;
static const _menuValueDownload = 1;
} }
enum _SelectionMenuOption { enum _SelectionMenuOption {