Share enhanced photos

This commit is contained in:
Ming Ming 2022-05-07 18:27:26 +08:00
parent 65d8825b6b
commit e088b1dbaa
6 changed files with 151 additions and 13 deletions

View file

@ -102,6 +102,13 @@ class LocalFileRepo {
}) =>
dataSrc.deleteFiles(files, onFailure: onFailure);
/// See [LocalFileDataSource.shareFiles]
Future<void> shareFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) =>
dataSrc.shareFiles(files, onFailure: onFailure);
final LocalFileDataSource dataSrc;
}
@ -114,4 +121,10 @@ abstract class LocalFileDataSource {
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
});
/// Share files
Future<void> shareFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
});
}

View file

@ -5,6 +5,7 @@ import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/mobile/android/android_info.dart';
import 'package:nc_photos/mobile/android/k.dart' as android;
import 'package:nc_photos/mobile/share.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/stream_extension.dart';
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
@ -28,19 +29,9 @@ class LocalFileMediaStoreDataSource implements LocalFileDataSource {
LocalFileOnFailureListener? onFailure,
}) async {
_log.info("[deleteFiles] ${files.map((f) => f.logTag).toReadableString()}");
final uriFiles = files
.where((f) {
if (f is! LocalUriFile) {
_log.warning(
"[deleteFiles] Can't remove file not returned by this data source: $f");
final uriFiles = _filterUriFiles(files, (f) {
onFailure?.call(f, ArgumentError("File not supported"), null);
return false;
} else {
return true;
}
})
.cast<LocalUriFile>()
.toList();
});
if (AndroidInfo().sdkInt >= AndroidVersion.R) {
await _deleteFiles30(uriFiles, onFailure);
} else {
@ -48,6 +39,27 @@ class LocalFileMediaStoreDataSource implements LocalFileDataSource {
}
}
@override
shareFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) async {
_log.info("[shareFiles] ${files.map((f) => f.logTag).toReadableString()}");
final uriFiles = _filterUriFiles(files, (f) {
onFailure?.call(f, ArgumentError("File not supported"), null);
});
final share = AndroidFileShare(uriFiles.map((e) => e.uri).toList(),
uriFiles.map((e) => e.mime).toList());
try {
await share.share();
} catch (e, stackTrace) {
for (final f in uriFiles) {
onFailure?.call(f, e, stackTrace);
}
}
}
Future<void> _deleteFiles30(
List<LocalUriFile> files, LocalFileOnFailureListener? onFailure) async {
assert(AndroidInfo().sdkInt >= AndroidVersion.R);
@ -79,6 +91,25 @@ class LocalFileMediaStoreDataSource implements LocalFileDataSource {
}
}
List<LocalUriFile> _filterUriFiles(
List<LocalFile> files, [
void Function(LocalFile)? nonUriFileCallback,
]) {
return files
.where((f) {
if (f is! LocalUriFile) {
_log.warning(
"[deleteFiles] Can't remove file not returned by this data source: $f");
nonUriFileCallback?.call(f);
return false;
} else {
return true;
}
})
.cast<LocalUriFile>()
.toList();
}
static LocalFile _toLocalFile(MediaStoreQueryResult r) => LocalUriFile(
uri: r.uri,
displayName: r.displayName,

View file

@ -3,12 +3,16 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_db.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/debug_util.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file/data_source.dart';
import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/entity/share.dart';
import 'package:nc_photos/entity/share/data_source.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
@ -22,6 +26,7 @@ import 'package:nc_photos/use_case/copy.dart';
import 'package:nc_photos/use_case/create_dir.dart';
import 'package:nc_photos/use_case/create_share.dart';
import 'package:nc_photos/use_case/download_file.dart';
import 'package:nc_photos/use_case/share_local.dart';
import 'package:nc_photos/widget/processing_dialog.dart';
import 'package:nc_photos/widget/share_link_multiple_files_dialog.dart';
import 'package:nc_photos/widget/share_method_dialog.dart';
@ -36,6 +41,32 @@ class ShareHandler {
this.clearSelection,
});
Future<void> shareLocalFiles(List<LocalFile> files) async {
if (!isSelectionCleared) {
clearSelection?.call();
}
final c = KiwiContainer().resolve<DiContainer>();
var hasShownError = false;
await ShareLocal(c)(
files,
onFailure: (f, e, stackTrace) {
if (e != null) {
_log.shout(
"[shareLocalFiles] Failed while sharing file: ${logFilename(f.logTag)}",
e,
stackTrace);
if (!hasShownError) {
SnackBarManager().showSnackBar(SnackBar(
content: Text(exception_util.toUserString(e)),
duration: k.snackBarDurationNormal,
));
hasShownError = true;
}
}
},
);
}
Future<void> shareFiles(Account account, List<File> files) async {
try {
final method = await _askShareMethod();

View file

@ -0,0 +1,26 @@
import 'package:logging/logging.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/local_file.dart';
class ShareLocal {
ShareLocal(this._c) : assert(require(_c));
static bool require(DiContainer c) =>
DiContainer.has(c, DiType.localFileRepo);
Future<void> call(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) async {
var count = files.length;
await _c.localFileRepo.shareFiles(files, onFailure: (f, e, stackTrace) {
--count;
onFailure?.call(f, e, stackTrace);
});
_log.info("[call] Shared $count files successfully");
}
final DiContainer _c;
static final _log = Logger("use_case.share_local.ShareLocal");
}

View file

@ -11,6 +11,7 @@ import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/content_uri_image_provider.dart';
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/widget/empty_list_indicator.dart';
@ -160,6 +161,13 @@ class _EnhancedPhotoBrowserState extends State<EnhancedPhotoBrowser>
});
},
actions: [
IconButton(
icon: const Icon(Icons.share),
tooltip: L10n.global().shareTooltip,
onPressed: () {
_onSelectionSharePressed(context);
},
),
PopupMenuButton<_SelectionMenuOption>(
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
itemBuilder: (context) => [
@ -198,6 +206,21 @@ class _EnhancedPhotoBrowserState extends State<EnhancedPhotoBrowser>
}
}
Future<void> _onSelectionSharePressed(BuildContext context) async {
final selected = selectedListItems
.whereType<_FileListItem>()
.map((e) => e.file)
.toList();
await ShareHandler(
context: context,
clearSelection: () {
setState(() {
clearSelectedItems();
});
},
).shareLocalFiles(selected);
}
void _onSelectionMenuSelected(
BuildContext context, _SelectionMenuOption option) {
switch (option) {

View file

@ -3,6 +3,7 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/share_handler.dart';
import 'package:nc_photos/theme.dart';
import 'package:nc_photos/widget/handler/delete_local_selection_handler.dart';
import 'package:nc_photos/widget/horizontal_page_viewer.dart';
@ -108,6 +109,13 @@ class _LocalFileViewerState extends State<LocalFileViewer> {
shadowColor: Colors.transparent,
foregroundColor: Colors.white.withOpacity(.87),
actions: [
IconButton(
icon: const Icon(Icons.share),
tooltip: L10n.global().shareTooltip,
onPressed: () {
_onSharePressed(context);
},
),
PopupMenuButton<_AppBarMenuOption>(
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
itemBuilder: (context) => [
@ -126,6 +134,12 @@ class _LocalFileViewerState extends State<LocalFileViewer> {
);
}
Future<void> _onSharePressed(BuildContext context) async {
final file = widget.streamFiles[_viewerController.currentPage];
_log.info("[_onSharePressed] Sharing file: ${file.logTag}");
await ShareHandler(context: context).shareLocalFiles([file]);
}
void _onMenuSelected(BuildContext context, _AppBarMenuOption option) {
switch (option) {
case _AppBarMenuOption.delete: