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'; 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; 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'; 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 shareFiles( BuildContext context, Account account, List 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 _askShareMethod(BuildContext context) { return showDialog( context: context, builder: (context) => const ShareMethodDialog()); } Future _shareAsFile( BuildContext context, Account account, List files) async { final controller = StreamController(); showDialog( context: context, builder: (context) => StreamBuilder( stream: controller.stream, builder: (context, snapshot) => ProcessingDialog( text: L10n.global().shareDownloadingDialogContent + (snapshot.hasData ? " ${snapshot.data}" : ""), ), ), ); final results = >[]; 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 _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 _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 _askPassword(BuildContext context) { return showDialog( 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"); }