2021-10-02 11:12:54 +02:00
|
|
|
import 'dart:async';
|
|
|
|
|
2021-12-26 20:11:31 +01:00
|
|
|
import 'package:collection/collection.dart';
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:nc_photos/account.dart';
|
|
|
|
import 'package:nc_photos/app_localizations.dart';
|
2022-10-15 16:29:18 +02:00
|
|
|
import 'package:nc_photos/di_container.dart';
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:nc_photos/entity/file.dart';
|
2022-10-15 16:29:18 +02:00
|
|
|
import 'package:nc_photos/entity/file_descriptor.dart';
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:nc_photos/exception.dart';
|
|
|
|
import 'package:nc_photos/exception_util.dart' as exception_util;
|
|
|
|
import 'package:nc_photos/k.dart' as k;
|
2021-10-02 11:12:54 +02:00
|
|
|
import 'package:nc_photos/mobile/android/download.dart';
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:nc_photos/mobile/notification.dart';
|
2022-04-08 21:16:10 +02:00
|
|
|
import 'package:nc_photos/mobile/platform.dart'
|
|
|
|
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:nc_photos/snack_bar_manager.dart';
|
|
|
|
import 'package:nc_photos/use_case/download_file.dart';
|
2022-10-15 16:29:18 +02:00
|
|
|
import 'package:nc_photos/use_case/inflate_file_descriptor.dart';
|
2022-05-03 12:44:48 +02:00
|
|
|
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
2022-12-16 16:01:04 +01:00
|
|
|
import 'package:np_codegen/np_codegen.dart';
|
2023-08-27 12:58:05 +02:00
|
|
|
import 'package:np_platform_util/np_platform_util.dart';
|
2021-09-28 22:56:44 +02:00
|
|
|
import 'package:tuple/tuple.dart';
|
|
|
|
|
2022-12-16 16:01:04 +01:00
|
|
|
part 'download_handler.g.dart';
|
|
|
|
|
2021-09-28 22:56:44 +02:00
|
|
|
class DownloadHandler {
|
2022-10-15 16:29:18 +02:00
|
|
|
DownloadHandler(this._c)
|
|
|
|
: assert(require(_c)),
|
|
|
|
assert(InflateFileDescriptor.require(_c));
|
|
|
|
|
|
|
|
static bool require(DiContainer c) => true;
|
|
|
|
|
2021-09-29 16:34:56 +02:00
|
|
|
Future<void> downloadFiles(
|
|
|
|
Account account,
|
2022-10-15 16:29:18 +02:00
|
|
|
List<FileDescriptor> fds, {
|
2021-09-29 16:34:56 +02:00
|
|
|
String? parentDir,
|
2022-10-15 16:29:18 +02:00
|
|
|
}) async {
|
|
|
|
final files = await InflateFileDescriptor(_c)(account, fds);
|
2021-10-02 11:12:54 +02:00
|
|
|
final _DownloadHandlerBase handler;
|
2023-08-27 12:58:05 +02:00
|
|
|
if (getRawPlatform() == NpPlatform.android) {
|
2021-10-02 11:12:54 +02:00
|
|
|
handler = _DownlaodHandlerAndroid();
|
|
|
|
} else {
|
|
|
|
handler = _DownloadHandlerWeb();
|
|
|
|
}
|
|
|
|
return handler.downloadFiles(
|
|
|
|
account,
|
|
|
|
files,
|
|
|
|
parentDir: parentDir,
|
|
|
|
);
|
|
|
|
}
|
2022-10-15 16:29:18 +02:00
|
|
|
|
|
|
|
final DiContainer _c;
|
2021-10-02 11:12:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
abstract class _DownloadHandlerBase {
|
|
|
|
Future<void> downloadFiles(
|
|
|
|
Account account,
|
|
|
|
List<File> files, {
|
|
|
|
String? parentDir,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-12-16 16:01:04 +01:00
|
|
|
@npLog
|
2021-10-02 11:12:54 +02:00
|
|
|
class _DownlaodHandlerAndroid extends _DownloadHandlerBase {
|
|
|
|
@override
|
|
|
|
downloadFiles(
|
|
|
|
Account account,
|
|
|
|
List<File> files, {
|
|
|
|
String? parentDir,
|
|
|
|
}) async {
|
|
|
|
_log.info("[downloadFiles] Downloading ${files.length} file");
|
2022-04-08 21:16:10 +02:00
|
|
|
final nm = platform.NotificationManager();
|
2021-10-02 11:12:54 +02:00
|
|
|
final notif = AndroidDownloadProgressNotification(
|
|
|
|
0,
|
|
|
|
files.length,
|
|
|
|
currentItemTitle: files.firstOrNull?.filename,
|
|
|
|
);
|
2022-04-08 21:16:10 +02:00
|
|
|
final id = await nm.notify(notif);
|
2021-10-02 11:12:54 +02:00
|
|
|
|
|
|
|
final successes = <Tuple2<File, dynamic>>[];
|
|
|
|
StreamSubscription<DownloadCancelEvent>? subscription;
|
|
|
|
try {
|
|
|
|
bool isCancel = false;
|
2023-07-25 19:41:14 +02:00
|
|
|
subscription = DownloadEvent.downloadCancelStream().listen((data) {
|
|
|
|
if (data.notificationId == id) {
|
|
|
|
isCancel = true;
|
|
|
|
}
|
|
|
|
});
|
2021-10-02 11:12:54 +02:00
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
for (final f in files) {
|
|
|
|
if (isCancel == true) {
|
|
|
|
_log.info("[downloadFiles] User canceled remaining files");
|
|
|
|
break;
|
|
|
|
}
|
2022-04-08 21:16:10 +02:00
|
|
|
await nm.notify(notif.copyWith(
|
|
|
|
progress: count++,
|
2021-10-02 11:12:54 +02:00
|
|
|
currentItemTitle: f.filename,
|
2022-04-08 21:16:10 +02:00
|
|
|
notificationId: id,
|
|
|
|
));
|
2021-10-02 11:12:54 +02:00
|
|
|
|
|
|
|
StreamSubscription<DownloadCancelEvent>? itemSubscription;
|
|
|
|
try {
|
|
|
|
final download = DownloadFile().build(
|
|
|
|
account,
|
|
|
|
f,
|
|
|
|
parentDir: parentDir,
|
|
|
|
shouldNotify: false,
|
|
|
|
);
|
2023-07-25 19:41:14 +02:00
|
|
|
itemSubscription =
|
|
|
|
DownloadEvent.downloadCancelStream().listen((data) {
|
|
|
|
if (data.notificationId == id) {
|
|
|
|
_log.info("[downloadFiles] Cancel requested");
|
|
|
|
download.cancel();
|
|
|
|
}
|
|
|
|
});
|
2021-10-02 11:12:54 +02:00
|
|
|
final result = await download();
|
|
|
|
successes.add(Tuple2(f, result));
|
|
|
|
} on PermissionException catch (_) {
|
|
|
|
_log.warning("[downloadFiles] Permission not granted");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
2022-05-03 07:27:20 +02:00
|
|
|
content: Text(L10n.global().errorNoStoragePermission),
|
2021-10-02 11:12:54 +02:00
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
break;
|
|
|
|
} on JobCanceledException catch (_) {
|
|
|
|
_log.info("[downloadFiles] User canceled");
|
|
|
|
break;
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout(
|
|
|
|
"[downloadFiles] Failed while DownloadFile", e, stackTrace);
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text("${L10n.global().downloadFailureNotification}: "
|
|
|
|
"${exception_util.toUserString(e)}"),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
} finally {
|
2022-07-28 18:59:26 +02:00
|
|
|
unawaited(itemSubscription?.cancel());
|
2021-10-02 11:12:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
2022-07-28 18:59:26 +02:00
|
|
|
unawaited(subscription?.cancel());
|
2021-10-02 11:12:54 +02:00
|
|
|
if (successes.isNotEmpty) {
|
|
|
|
await _onDownloadSuccessful(successes.map((e) => e.item1).toList(),
|
2022-04-08 21:16:10 +02:00
|
|
|
successes.map((e) => e.item2).toList(), id);
|
2021-10-02 11:12:54 +02:00
|
|
|
} else {
|
2022-04-08 21:16:10 +02:00
|
|
|
await nm.dismiss(id);
|
2021-10-02 11:12:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _onDownloadSuccessful(
|
|
|
|
List<File> files, List<dynamic> results, int? notificationId) async {
|
2022-04-08 21:16:10 +02:00
|
|
|
final nm = platform.NotificationManager();
|
|
|
|
await nm.notify(AndroidDownloadSuccessfulNotification(
|
2021-10-02 11:12:54 +02:00
|
|
|
results.cast<String>(),
|
|
|
|
files.map((e) => e.contentType).toList(),
|
|
|
|
notificationId: notificationId,
|
2022-04-08 21:16:10 +02:00
|
|
|
));
|
2021-10-02 11:12:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-16 16:01:04 +01:00
|
|
|
@npLog
|
2021-10-02 11:12:54 +02:00
|
|
|
class _DownloadHandlerWeb extends _DownloadHandlerBase {
|
|
|
|
@override
|
|
|
|
downloadFiles(
|
|
|
|
Account account,
|
|
|
|
List<File> files, {
|
|
|
|
String? parentDir,
|
2021-09-29 16:34:56 +02:00
|
|
|
}) async {
|
2021-09-28 22:56:44 +02:00
|
|
|
_log.info("[downloadFiles] Downloading ${files.length} file");
|
2022-07-25 07:04:22 +02:00
|
|
|
SnackBarManager().showSnackBar(
|
|
|
|
SnackBar(
|
|
|
|
content: Text(L10n.global().downloadProcessingNotification),
|
|
|
|
duration: k.snackBarDurationShort,
|
|
|
|
),
|
|
|
|
canBeReplaced: true,
|
|
|
|
);
|
2021-10-02 11:12:54 +02:00
|
|
|
int successCount = 0;
|
2021-09-28 22:56:44 +02:00
|
|
|
for (final f in files) {
|
|
|
|
try {
|
2021-10-02 11:12:54 +02:00
|
|
|
await DownloadFile()(
|
2021-09-29 16:34:56 +02:00
|
|
|
account,
|
|
|
|
f,
|
|
|
|
parentDir: parentDir,
|
|
|
|
);
|
2021-10-02 11:12:54 +02:00
|
|
|
++successCount;
|
2021-09-28 22:56:44 +02:00
|
|
|
} on PermissionException catch (_) {
|
|
|
|
_log.warning("[downloadFiles] Permission not granted");
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
2022-05-03 07:27:20 +02:00
|
|
|
content: Text(L10n.global().errorNoStoragePermission),
|
2021-09-28 22:56:44 +02:00
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
break;
|
|
|
|
} on JobCanceledException catch (_) {
|
|
|
|
_log.info("[downloadFiles] User canceled");
|
|
|
|
break;
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
_log.shout("[downloadFiles] Failed while DownloadFile", e, stackTrace);
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text("${L10n.global().downloadFailureNotification}: "
|
|
|
|
"${exception_util.toUserString(e)}"),
|
|
|
|
duration: k.snackBarDurationNormal,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2021-10-02 11:12:54 +02:00
|
|
|
if (successCount > 0) {
|
|
|
|
SnackBarManager().showSnackBar(SnackBar(
|
|
|
|
content: Text(L10n.global().downloadSuccessNotification),
|
|
|
|
duration: k.snackBarDurationShort,
|
|
|
|
));
|
2021-09-28 22:56:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|