mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-13 18:58:53 +01:00
Download selected files
This commit is contained in:
parent
145761495c
commit
e7f510ac4f
6 changed files with 201 additions and 67 deletions
85
lib/download_handler.dart
Normal file
85
lib/download_handler.dart
Normal file
|
@ -0,0 +1,85 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/exception.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/mobile/notification.dart';
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/use_case/download_file.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class DownloadHandler {
|
||||
Future<void> downloadFiles(Account account, List<File> files) async {
|
||||
_log.info("[downloadFiles] Downloading ${files.length} file");
|
||||
var controller = SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadProcessingNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
controller?.closed.whenComplete(() {
|
||||
controller = null;
|
||||
});
|
||||
final successes = <Tuple2<File, dynamic>>[];
|
||||
for (final f in files) {
|
||||
try {
|
||||
successes.add(Tuple2(f, await DownloadFile()(account, f)));
|
||||
} on PermissionException catch (_) {
|
||||
_log.warning("[downloadFiles] Permission not granted");
|
||||
controller?.close();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadFailureNoPermissionNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
break;
|
||||
} on JobCanceledException catch (_) {
|
||||
_log.info("[downloadFiles] User canceled");
|
||||
break;
|
||||
} catch (e, stackTrace) {
|
||||
_log.shout("[downloadFiles] Failed while DownloadFile", e, stackTrace);
|
||||
controller?.close();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text("${L10n.global().downloadFailureNotification}: "
|
||||
"${exception_util.toUserString(e)}"),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
}
|
||||
}
|
||||
if (successes.isNotEmpty) {
|
||||
controller?.close();
|
||||
await _onDownloadSuccessful(successes.map((e) => e.item1).toList(),
|
||||
successes.map((e) => e.item2).toList());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDownloadSuccessful(
|
||||
List<File> files, List<dynamic> results) async {
|
||||
dynamic notif;
|
||||
if (platform_k.isAndroid) {
|
||||
notif = AndroidItemDownloadSuccessfulNotification(
|
||||
results.cast<String>(), files.map((e) => e.contentType).toList());
|
||||
}
|
||||
if (notif != null) {
|
||||
try {
|
||||
await notif.notify();
|
||||
return;
|
||||
} catch (e, stacktrace) {
|
||||
_log.shout(
|
||||
"[_onDownloadSuccessful] Failed showing platform notification",
|
||||
e,
|
||||
stacktrace);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadSuccessNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
}
|
||||
|
||||
static final _log = Logger("download_handler.DownloadHandler");
|
||||
}
|
|
@ -6,6 +6,7 @@ import 'package:logging/logging.dart';
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
|
@ -249,7 +250,17 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
icon: const Icon(Icons.remove),
|
||||
tooltip: L10n.global().removeSelectedFromAlbumTooltip,
|
||||
onPressed: _onSelectionAppBarRemovePressed,
|
||||
)
|
||||
),
|
||||
PopupMenuButton<_SelectionMenuOption>(
|
||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.download,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
],
|
||||
onSelected: (option) => _onSelectionMenuSelected(context, option),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -331,6 +342,29 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
}
|
||||
}
|
||||
|
||||
void _onSelectionMenuSelected(
|
||||
BuildContext context, _SelectionMenuOption option) {
|
||||
switch (option) {
|
||||
case _SelectionMenuOption.download:
|
||||
_onSelectionDownloadPressed();
|
||||
break;
|
||||
default:
|
||||
_log.shout("[_onSelectionMenuSelected] Unknown option: $option");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectionDownloadPressed() {
|
||||
final selected = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList();
|
||||
DownloadHandler().downloadFiles(widget.account, selected);
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
|
||||
void _onEditAppBarSortPressed() {
|
||||
final sortProvider = _editAlbum!.sortProvider;
|
||||
showDialog(
|
||||
|
@ -634,6 +668,10 @@ class _AlbumBrowserState extends State<AlbumBrowser>
|
|||
static final _log = Logger("widget.album_browser._AlbumBrowserState");
|
||||
}
|
||||
|
||||
enum _SelectionMenuOption {
|
||||
download,
|
||||
}
|
||||
|
||||
abstract class _ListItem implements SelectableItem, DraggableItem {
|
||||
_ListItem({
|
||||
required this.index,
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:nc_photos/account.dart';
|
|||
import 'package:nc_photos/api/api_util.dart' as api_util;
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/cover_provider.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
|
@ -258,19 +259,19 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
_onSelectionAppBarSharePressed(context);
|
||||
},
|
||||
),
|
||||
PopupMenuButton(
|
||||
PopupMenuButton<_SelectionMenuOption>(
|
||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.download,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.delete,
|
||||
child: Text(L10n.global().deleteTooltip),
|
||||
),
|
||||
],
|
||||
onSelected: (option) {
|
||||
if (option == _SelectionMenuOption.delete) {
|
||||
_onSelectionAppBarDeletePressed();
|
||||
}
|
||||
},
|
||||
onSelected: (option) => _onSelectionMenuSelected(context, option),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
@ -370,6 +371,21 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
});
|
||||
}
|
||||
|
||||
void _onSelectionMenuSelected(
|
||||
BuildContext context, _SelectionMenuOption option) {
|
||||
switch (option) {
|
||||
case _SelectionMenuOption.delete:
|
||||
_onSelectionAppBarDeletePressed();
|
||||
break;
|
||||
case _SelectionMenuOption.download:
|
||||
_onSelectionDownloadPressed();
|
||||
break;
|
||||
default:
|
||||
_log.shout("[_onSelectionMenuSelected] Unknown option: $option");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectionAppBarDeletePressed() async {
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global()
|
||||
|
@ -423,6 +439,17 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
}
|
||||
}
|
||||
|
||||
void _onSelectionDownloadPressed() {
|
||||
final selected = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList();
|
||||
DownloadHandler().downloadFiles(widget.account, selected);
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
|
||||
void _onEditAppBarSortPressed() {
|
||||
final sortProvider = _editAlbum!.sortProvider;
|
||||
showDialog(
|
||||
|
@ -553,6 +580,7 @@ class _DynamicAlbumBrowserState extends State<DynamicAlbumBrowser>
|
|||
|
||||
enum _SelectionMenuOption {
|
||||
delete,
|
||||
download,
|
||||
}
|
||||
|
||||
abstract class _ListItem implements SelectableItem {
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:nc_photos/api/api_util.dart' as api_util;
|
|||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/bloc/scan_dir.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
|
@ -197,6 +198,10 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
PopupMenuButton<_SelectionMenuOption>(
|
||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.download,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.archive,
|
||||
child: Text(L10n.global().archiveTooltip),
|
||||
|
@ -401,6 +406,17 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
}
|
||||
}
|
||||
|
||||
void _onDownloadPressed() {
|
||||
final selected = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
.map((e) => e.file)
|
||||
.toList();
|
||||
DownloadHandler().downloadFiles(widget.account, selected);
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onArchivePressed(BuildContext context) async {
|
||||
final selectedFiles = selectedListItems
|
||||
.whereType<_FileListItem>()
|
||||
|
@ -472,6 +488,10 @@ class _HomePhotosState extends State<HomePhotos>
|
|||
_onDeletePressed(context);
|
||||
break;
|
||||
|
||||
case _SelectionMenuOption.download:
|
||||
_onDownloadPressed();
|
||||
break;
|
||||
|
||||
default:
|
||||
_log.shout("[_onSelectionAppBarMenuSelected] Unknown option: $option");
|
||||
break;
|
||||
|
@ -859,4 +879,5 @@ class _MetadataTaskLoadingIcon extends AnimatedWidget {
|
|||
enum _SelectionMenuOption {
|
||||
archive,
|
||||
delete,
|
||||
download,
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:nc_photos/app_localizations.dart';
|
|||
import 'package:nc_photos/bloc/list_face.dart';
|
||||
import 'package:nc_photos/cache_manager_util.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/album/item.dart';
|
||||
import 'package:nc_photos/entity/album/provider.dart';
|
||||
|
@ -279,6 +280,10 @@ class _PersonBrowserState extends State<PersonBrowser>
|
|||
PopupMenuButton<_SelectionMenuOption>(
|
||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.download,
|
||||
child: Text(L10n.global().downloadTooltip),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SelectionMenuOption.archive,
|
||||
child: Text(L10n.global().archiveTooltip),
|
||||
|
@ -362,6 +367,15 @@ class _PersonBrowserState extends State<PersonBrowser>
|
|||
}
|
||||
}
|
||||
|
||||
void _onDownloadPressed() {
|
||||
final selected =
|
||||
selectedListItems.whereType<_ListItem>().map((e) => e.file).toList();
|
||||
DownloadHandler().downloadFiles(widget.account, selected);
|
||||
setState(() {
|
||||
clearSelectedItems();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onArchivePressed(BuildContext context) async {
|
||||
final selectedFiles =
|
||||
selectedListItems.whereType<_ListItem>().map((e) => e.file).toList();
|
||||
|
@ -429,6 +443,10 @@ class _PersonBrowserState extends State<PersonBrowser>
|
|||
_onDeletePressed(context);
|
||||
break;
|
||||
|
||||
case _SelectionMenuOption.download:
|
||||
_onDownloadPressed();
|
||||
break;
|
||||
|
||||
default:
|
||||
_log.shout("[_onOptionMenuSelected] Unknown option: $option");
|
||||
break;
|
||||
|
@ -561,4 +579,5 @@ class _ListItem implements SelectableItem {
|
|||
enum _SelectionMenuOption {
|
||||
archive,
|
||||
delete,
|
||||
download,
|
||||
}
|
||||
|
|
|
@ -9,20 +9,18 @@ import 'package:logging/logging.dart';
|
|||
import 'package:nc_photos/account.dart';
|
||||
import 'package:nc_photos/app_localizations.dart';
|
||||
import 'package:nc_photos/debug_util.dart';
|
||||
import 'package:nc_photos/download_handler.dart';
|
||||
import 'package:nc_photos/entity/album.dart';
|
||||
import 'package:nc_photos/entity/file.dart';
|
||||
import 'package:nc_photos/entity/file/data_source.dart';
|
||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||
import 'package:nc_photos/exception.dart';
|
||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||
import 'package:nc_photos/k.dart' as k;
|
||||
import 'package:nc_photos/mobile/notification.dart';
|
||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||
import 'package:nc_photos/pref.dart';
|
||||
import 'package:nc_photos/share_handler.dart';
|
||||
import 'package:nc_photos/snack_bar_manager.dart';
|
||||
import 'package:nc_photos/theme.dart';
|
||||
import 'package:nc_photos/use_case/download_file.dart';
|
||||
import 'package:nc_photos/use_case/remove.dart';
|
||||
import 'package:nc_photos/widget/animated_visibility.dart';
|
||||
import 'package:nc_photos/widget/disposable.dart';
|
||||
|
@ -440,65 +438,10 @@ class _ViewerState extends State<Viewer>
|
|||
ShareHandler().shareFiles(context, widget.account, [file]);
|
||||
}
|
||||
|
||||
void _onDownloadPressed() async {
|
||||
void _onDownloadPressed() {
|
||||
final file = widget.streamFiles[_viewerController.currentPage];
|
||||
_log.info("[_onDownloadPressed] Downloading file: ${file.path}");
|
||||
var controller = SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadProcessingNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
controller?.closed.whenComplete(() {
|
||||
controller = null;
|
||||
});
|
||||
dynamic result;
|
||||
try {
|
||||
result = await DownloadFile()(widget.account, file);
|
||||
controller?.close();
|
||||
await _onDownloadSuccessful(file, result);
|
||||
} on PermissionException catch (_) {
|
||||
_log.warning("[_onDownloadPressed] Permission not granted");
|
||||
controller?.close();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadFailureNoPermissionNotification),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
} on JobCanceledException catch (_) {
|
||||
_log.info("[_onDownloadPressed] Canceled");
|
||||
} catch (e, stacktrace) {
|
||||
_log.shout(
|
||||
"[_onDownloadPressed] Failed while downloadFile", e, stacktrace);
|
||||
controller?.close();
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text("${L10n.global().downloadFailureNotification}: "
|
||||
"${exception_util.toUserString(e)}"),
|
||||
duration: k.snackBarDurationNormal,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDownloadSuccessful(File file, dynamic result) async {
|
||||
dynamic notif;
|
||||
if (platform_k.isAndroid) {
|
||||
notif = AndroidItemDownloadSuccessfulNotification(
|
||||
[result], [file.contentType]);
|
||||
}
|
||||
if (notif != null) {
|
||||
try {
|
||||
await notif.notify();
|
||||
return;
|
||||
} catch (e, stacktrace) {
|
||||
_log.shout(
|
||||
"[_onDownloadSuccessful] Failed showing platform notification",
|
||||
e,
|
||||
stacktrace);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
SnackBarManager().showSnackBar(SnackBar(
|
||||
content: Text(L10n.global().downloadSuccessNotification),
|
||||
duration: k.snackBarDurationShort,
|
||||
));
|
||||
DownloadHandler().downloadFiles(widget.account, [file]);
|
||||
}
|
||||
|
||||
void _onDeletePressed(BuildContext context) async {
|
||||
|
|
Loading…
Reference in a new issue