nc-photos/lib/share_handler.dart

179 lines
6 KiB
Dart
Raw Normal View History

2021-09-19 23:18:20 +02:00
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
2021-07-25 07:00:38 +02:00
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/share.dart';
import 'package:nc_photos/entity/share/data_source.dart';
import 'package:nc_photos/exception.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
2021-09-19 23:18:20 +02:00
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/share.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/create_share.dart';
2021-09-09 22:42:47 +02:00
import 'package:nc_photos/use_case/download_file.dart';
import 'package:nc_photos/widget/processing_dialog.dart';
import 'package:nc_photos/widget/share_method_dialog.dart';
import 'package:nc_photos/widget/simple_input_dialog.dart';
import 'package:tuple/tuple.dart';
/// Handle sharing to other apps
class ShareHandler {
Future<void> shareFiles(
BuildContext context, Account account, List<File> files) async {
assert(platform_k.isAndroid);
final ShareMethod? method;
if (files.length == 1) {
method = await _askShareMethod(context);
} else {
method = ShareMethod.file;
}
if (method == null) {
// user canceled
return;
} else if (method == ShareMethod.publicLink) {
return _shareAsPublicLink(context, account, files.first);
} else if (method == ShareMethod.passwordLink) {
return _shareAsPasswordLink(context, account, files.first);
} else {
return _shareAsFile(context, account, files);
}
}
Future<ShareMethod?> _askShareMethod(BuildContext context) {
return showDialog<ShareMethod>(
context: context, builder: (context) => const ShareMethodDialog());
}
Future<void> _shareAsFile(
BuildContext context, Account account, List<File> files) async {
2021-09-19 23:18:20 +02:00
final controller = StreamController<String>();
showDialog(
context: context,
2021-09-19 23:18:20 +02:00
builder: (context) => StreamBuilder(
stream: controller.stream,
builder: (context, snapshot) => ProcessingDialog(
text: L10n.global().shareDownloadingDialogContent +
(snapshot.hasData ? " ${snapshot.data}" : ""),
),
),
);
final results = <Tuple2<File, dynamic>>[];
2021-09-19 23:18:20 +02:00
for (final pair in files.withIndex()) {
final i = pair.item1, f = pair.item2;
controller.add("(${i + 1}/${files.length})");
try {
results.add(Tuple2(
f,
await DownloadFile()(
account,
f,
shouldNotify: false,
)));
} on PermissionException catch (_) {
_log.warning("[shareFiles] Permission not granted");
SnackBarManager().showSnackBar(SnackBar(
content: Text(L10n.global().downloadFailureNoPermissionNotification),
duration: k.snackBarDurationNormal,
));
// dismiss the dialog
Navigator.of(context).pop();
rethrow;
} catch (e, stacktrace) {
_log.shout("[shareFiles] Failed while downloadFile", e, stacktrace);
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(e)),
duration: k.snackBarDurationNormal,
));
}
}
// dismiss the dialog
Navigator.of(context).pop();
final share = AndroidFileShare(
results.map((e) => e.item2 as String).toList(),
results.map((e) => e.item1.contentType).toList());
share.share();
}
Future<void> _shareAsPublicLink(
BuildContext context, Account account, File file) async {
final shareRepo = ShareRepo(ShareRemoteDataSource());
try {
final share = await CreateLinkShare(shareRepo)(account, file);
await Clipboard.setData(ClipboardData(text: share.url));
SnackBarManager().showSnackBar(SnackBar(
content: Text(L10n.global().linkCopiedNotification),
duration: k.snackBarDurationNormal,
));
final textShare = AndroidTextShare(share.url!);
textShare.share();
} catch (e, stackTrace) {
_log.shout(
"[_shareAsPublicLink] Failed while CreateLinkShare", e, stackTrace);
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(e)),
duration: k.snackBarDurationNormal,
));
}
}
Future<void> _shareAsPasswordLink(
BuildContext context, Account account, File file) async {
final password = await _askPassword(context);
if (password == null) {
// user canceled
return;
}
final shareRepo = ShareRepo(ShareRemoteDataSource());
try {
final share = await CreateLinkShare(shareRepo)(
account,
file,
password: password,
);
await Clipboard.setData(ClipboardData(text: share.url));
SnackBarManager().showSnackBar(SnackBar(
content: Text(L10n.global().linkCopiedNotification),
duration: k.snackBarDurationNormal,
));
final textShare = AndroidTextShare(share.url!);
textShare.share();
} catch (e, stackTrace) {
_log.shout(
"[_shareAsPasswordLink] Failed while CreateLinkShare", e, stackTrace);
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(e)),
duration: k.snackBarDurationNormal,
));
}
}
Future<String?> _askPassword(BuildContext context) {
return showDialog<String>(
context: context,
builder: (context) => SimpleInputDialog(
hintText: L10n.global().passwordInputHint,
buttonText: MaterialLocalizations.of(context).okButtonLabel,
validator: (value) {
if (value?.isNotEmpty != true) {
return L10n.global().passwordInputInvalidEmpty;
}
return null;
},
obscureText: true,
),
);
}
static final _log = Logger("share_handler.ShareHandler");
}