mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-02-02 06:46:22 +01:00
A simpler way to share photos to other apps
This commit is contained in:
parent
a2ff2a3788
commit
61c950ecec
11 changed files with 313 additions and 10 deletions
|
@ -17,5 +17,8 @@ class MainActivity : FlutterActivity() {
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
|
||||||
SelfSignedCertChannelHandler.CHANNEL).setMethodCallHandler(
|
SelfSignedCertChannelHandler.CHANNEL).setMethodCallHandler(
|
||||||
SelfSignedCertChannelHandler(this))
|
SelfSignedCertChannelHandler(this))
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
|
||||||
|
ShareChannelHandler.CHANNEL).setMethodCallHandler(
|
||||||
|
ShareChannelHandler(this))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.nkming.nc_photos
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
|
class ShareChannelHandler(activity: Activity)
|
||||||
|
: MethodChannel.MethodCallHandler {
|
||||||
|
companion object {
|
||||||
|
const val CHANNEL = "com.nkming.nc_photos/share"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
if (call.method == "shareItems") {
|
||||||
|
try {
|
||||||
|
shareItems(call.argument<List<String>>("fileUris")!!,
|
||||||
|
call.argument<List<String?>>("mimeTypes")!!,
|
||||||
|
result)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
result.error("systemException", e.toString(), null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareItems(fileUris: List<String>,
|
||||||
|
mimeTypes: List<String?>, result: MethodChannel.Result) {
|
||||||
|
assert(fileUris.isNotEmpty())
|
||||||
|
assert(fileUris.size == mimeTypes.size)
|
||||||
|
val uris = fileUris.map { Uri.parse(it) }
|
||||||
|
|
||||||
|
val shareIntent = if (uris.size == 1) Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uris[0])
|
||||||
|
type = mimeTypes[0] ?: "*/*"
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
} else Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||||
|
type = if (mimeTypes.all { it?.startsWith("image/") == true })
|
||||||
|
"image/*" else "*/*"
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
val shareChooser = Intent.createChooser(shareIntent, _context.getString(
|
||||||
|
R.string.download_successful_notification_action_share_chooser))
|
||||||
|
_context.startActivity(shareChooser)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _activity = activity
|
||||||
|
private val _context get() = _activity
|
||||||
|
}
|
|
@ -555,6 +555,22 @@
|
||||||
"@albumAddTextTooltip": {
|
"@albumAddTextTooltip": {
|
||||||
"description": "Add some text that display between photos to an album"
|
"description": "Add some text that display between photos to an album"
|
||||||
},
|
},
|
||||||
|
"shareTooltip": "Share",
|
||||||
|
"@shareTooltip": {
|
||||||
|
"description": "Share the current item to other apps"
|
||||||
|
},
|
||||||
|
"shareSelectedTooltip": "Share selected",
|
||||||
|
"@shareSelectedTooltip": {
|
||||||
|
"description": "Share selected items to other apps"
|
||||||
|
},
|
||||||
|
"shareSelectedEmptyNotification": "Select some photos to share",
|
||||||
|
"@shareSelectedEmptyNotification": {
|
||||||
|
"description": "Shown when user pressed the share button with only non-sharable items (e.g., text labels) selected"
|
||||||
|
},
|
||||||
|
"shareDownloadingDialogContent": "Downloading",
|
||||||
|
"@shareDownloadingDialogContent": {
|
||||||
|
"description": "Downloading photos to be shared"
|
||||||
|
},
|
||||||
|
|
||||||
"changelogTitle": "Changelog",
|
"changelogTitle": "Changelog",
|
||||||
"@changelogTitle": {
|
"@changelogTitle": {
|
||||||
|
|
12
lib/mobile/android/share.dart
Normal file
12
lib/mobile/android/share.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class Share {
|
||||||
|
static Future<void> shareItems(
|
||||||
|
List<String> fileUris, List<String> mimeTypes) =>
|
||||||
|
_channel.invokeMethod("shareItems", <String, dynamic>{
|
||||||
|
"fileUris": fileUris,
|
||||||
|
"mimeTypes": mimeTypes,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const _channel = const MethodChannel("com.nkming.nc_photos/share");
|
||||||
|
}
|
14
lib/mobile/share.dart
Normal file
14
lib/mobile/share.dart
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import 'package:nc_photos/mobile/android/share.dart';
|
||||||
|
import 'package:nc_photos/platform/share.dart' as itf;
|
||||||
|
|
||||||
|
class AndroidShare extends itf.Share {
|
||||||
|
AndroidShare(this.fileUris, this.mimeTypes);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> share() {
|
||||||
|
return Share.shareItems(fileUris, mimeTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> fileUris;
|
||||||
|
final List<String> mimeTypes;
|
||||||
|
}
|
3
lib/platform/share.dart
Normal file
3
lib/platform/share.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
abstract class Share {
|
||||||
|
Future<void> share();
|
||||||
|
}
|
60
lib/share_handler.dart
Normal file
60
lib/share_handler.dart
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:nc_photos/account.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/platform.dart'
|
||||||
|
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
||||||
|
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/widget/processing_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);
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => ProcessingDialog(
|
||||||
|
text: AppLocalizations.of(context).shareDownloadingDialogContent),
|
||||||
|
);
|
||||||
|
final results = <Tuple2<File, dynamic>>[];
|
||||||
|
for (final f in files) {
|
||||||
|
try {
|
||||||
|
results.add(
|
||||||
|
Tuple2(f, await platform.Downloader().downloadFile(account, f)));
|
||||||
|
} on PermissionException catch (_) {
|
||||||
|
_log.warning("[shareFiles] Permission not granted");
|
||||||
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(context)
|
||||||
|
.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, context)),
|
||||||
|
duration: k.snackBarDurationNormal,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dismiss the dialog
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
final share = AndroidShare(results.map((e) => e.item2 as String).toList(),
|
||||||
|
results.map((e) => e.item1.contentType).toList());
|
||||||
|
share.share();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final _log = Logger("share_handler.ShareHandler");
|
||||||
|
}
|
|
@ -15,7 +15,9 @@ import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||||
import 'package:nc_photos/iterable_extension.dart';
|
import 'package:nc_photos/iterable_extension.dart';
|
||||||
import 'package:nc_photos/k.dart' as k;
|
import 'package:nc_photos/k.dart' as k;
|
||||||
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
import 'package:nc_photos/session_storage.dart';
|
import 'package:nc_photos/session_storage.dart';
|
||||||
|
import 'package:nc_photos/share_handler.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/resync_album.dart';
|
import 'package:nc_photos/use_case/resync_album.dart';
|
||||||
|
@ -222,6 +224,14 @@ class _AlbumViewerState extends State<AlbumViewer>
|
||||||
|
|
||||||
Widget _buildSelectionAppBar(BuildContext context) {
|
Widget _buildSelectionAppBar(BuildContext context) {
|
||||||
return buildSelectionAppBar(context, [
|
return buildSelectionAppBar(context, [
|
||||||
|
if (platform_k.isAndroid)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.share),
|
||||||
|
tooltip: AppLocalizations.of(context).shareSelectedTooltip,
|
||||||
|
onPressed: () {
|
||||||
|
_onSelectionAppBarSharePressed(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
tooltip: AppLocalizations.of(context).removeSelectedFromAlbumTooltip,
|
tooltip: AppLocalizations.of(context).removeSelectedFromAlbumTooltip,
|
||||||
|
@ -261,6 +271,27 @@ class _AlbumViewerState extends State<AlbumViewer>
|
||||||
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex));
|
arguments: ViewerArguments(widget.account, _backingFiles, fileIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSelectionAppBarSharePressed(BuildContext context) {
|
||||||
|
assert(platform_k.isAndroid);
|
||||||
|
final selected = selectedListItems
|
||||||
|
.whereType<_FileListItem>()
|
||||||
|
.map((e) => e.file)
|
||||||
|
.toList();
|
||||||
|
if (selected.isEmpty) {
|
||||||
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
|
content:
|
||||||
|
Text(AppLocalizations.of(context).shareSelectedEmptyNotification),
|
||||||
|
duration: k.snackBarDurationNormal,
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShareHandler().shareFiles(context, widget.account, selected).then((_) {
|
||||||
|
setState(() {
|
||||||
|
clearSelectedItems();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _onSelectionAppBarRemovePressed() {
|
void _onSelectionAppBarRemovePressed() {
|
||||||
final selectedIndexes =
|
final selectedIndexes =
|
||||||
selectedListItems.map((e) => (e as _ListItem).index).toList();
|
selectedListItems.map((e) => (e as _ListItem).index).toList();
|
||||||
|
@ -526,6 +557,7 @@ class _AlbumViewerState extends State<AlbumViewer>
|
||||||
} else if (file_util.isSupportedVideoFormat(item.file)) {
|
} else if (file_util.isSupportedVideoFormat(item.file)) {
|
||||||
yield _VideoListItem(
|
yield _VideoListItem(
|
||||||
index: i,
|
index: i,
|
||||||
|
file: item.file,
|
||||||
account: widget.account,
|
account: widget.account,
|
||||||
previewUrl: previewUrl,
|
previewUrl: previewUrl,
|
||||||
onTap: () => _onItemTap(i),
|
onTap: () => _onItemTap(i),
|
||||||
|
@ -677,10 +709,31 @@ abstract class _ListItem implements SelectableItem, DraggableItem {
|
||||||
final VoidCallback _onDragEndedAny;
|
final VoidCallback _onDragEndedAny;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ImageListItem extends _ListItem {
|
abstract class _FileListItem extends _ListItem {
|
||||||
_ImageListItem({
|
_FileListItem({
|
||||||
@required int index,
|
@required int index,
|
||||||
@required this.file,
|
@required this.file,
|
||||||
|
VoidCallback onTap,
|
||||||
|
DragTargetAccept<DraggableItem> onDropBefore,
|
||||||
|
DragTargetAccept<DraggableItem> onDropAfter,
|
||||||
|
VoidCallback onDragStarted,
|
||||||
|
VoidCallback onDragEndedAny,
|
||||||
|
}) : super(
|
||||||
|
index: index,
|
||||||
|
onTap: onTap,
|
||||||
|
onDropBefore: onDropBefore,
|
||||||
|
onDropAfter: onDropAfter,
|
||||||
|
onDragStarted: onDragStarted,
|
||||||
|
onDragEndedAny: onDragEndedAny,
|
||||||
|
);
|
||||||
|
|
||||||
|
final File file;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageListItem extends _FileListItem {
|
||||||
|
_ImageListItem({
|
||||||
|
@required int index,
|
||||||
|
@required File file,
|
||||||
@required this.account,
|
@required this.account,
|
||||||
@required this.previewUrl,
|
@required this.previewUrl,
|
||||||
VoidCallback onTap,
|
VoidCallback onTap,
|
||||||
|
@ -690,6 +743,7 @@ class _ImageListItem extends _ListItem {
|
||||||
VoidCallback onDragEndedAny,
|
VoidCallback onDragEndedAny,
|
||||||
}) : super(
|
}) : super(
|
||||||
index: index,
|
index: index,
|
||||||
|
file: file,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onDropBefore: onDropBefore,
|
onDropBefore: onDropBefore,
|
||||||
onDropAfter: onDropAfter,
|
onDropAfter: onDropAfter,
|
||||||
|
@ -706,14 +760,14 @@ class _ImageListItem extends _ListItem {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final File file;
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final String previewUrl;
|
final String previewUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VideoListItem extends _ListItem {
|
class _VideoListItem extends _FileListItem {
|
||||||
_VideoListItem({
|
_VideoListItem({
|
||||||
@required int index,
|
@required int index,
|
||||||
|
@required File file,
|
||||||
@required this.account,
|
@required this.account,
|
||||||
@required this.previewUrl,
|
@required this.previewUrl,
|
||||||
VoidCallback onTap,
|
VoidCallback onTap,
|
||||||
|
@ -723,6 +777,7 @@ class _VideoListItem extends _ListItem {
|
||||||
VoidCallback onDragEndedAny,
|
VoidCallback onDragEndedAny,
|
||||||
}) : super(
|
}) : super(
|
||||||
index: index,
|
index: index,
|
||||||
|
file: file,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onDropBefore: onDropBefore,
|
onDropBefore: onDropBefore,
|
||||||
onDropAfter: onDropAfter,
|
onDropAfter: onDropAfter,
|
||||||
|
|
|
@ -17,6 +17,8 @@ import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||||
import 'package:nc_photos/iterable_extension.dart';
|
import 'package:nc_photos/iterable_extension.dart';
|
||||||
import 'package:nc_photos/k.dart' as k;
|
import 'package:nc_photos/k.dart' as k;
|
||||||
import 'package:nc_photos/list_extension.dart';
|
import 'package:nc_photos/list_extension.dart';
|
||||||
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
|
import 'package:nc_photos/share_handler.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/populate_album.dart';
|
import 'package:nc_photos/use_case/populate_album.dart';
|
||||||
|
@ -229,6 +231,14 @@ class _DynamicAlbumViewerState extends State<DynamicAlbumViewer>
|
||||||
|
|
||||||
Widget _buildSelectionAppBar(BuildContext context) {
|
Widget _buildSelectionAppBar(BuildContext context) {
|
||||||
return buildSelectionAppBar(context, [
|
return buildSelectionAppBar(context, [
|
||||||
|
if (platform_k.isAndroid)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.share),
|
||||||
|
tooltip: AppLocalizations.of(context).shareSelectedTooltip,
|
||||||
|
onPressed: () {
|
||||||
|
_onSelectionAppBarSharePressed(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
|
@ -313,6 +323,19 @@ class _DynamicAlbumViewerState extends State<DynamicAlbumViewer>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSelectionAppBarSharePressed(BuildContext context) {
|
||||||
|
assert(platform_k.isAndroid);
|
||||||
|
final selected = selectedListItems
|
||||||
|
.whereType<_FileListItem>()
|
||||||
|
.map((e) => e.file)
|
||||||
|
.toList();
|
||||||
|
ShareHandler().shareFiles(context, widget.account, selected).then((_) {
|
||||||
|
setState(() {
|
||||||
|
clearSelectedItems();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _onSelectionAppBarDeletePressed() async {
|
void _onSelectionAppBarDeletePressed() async {
|
||||||
SnackBarManager().showSnackBar(SnackBar(
|
SnackBarManager().showSnackBar(SnackBar(
|
||||||
content: Text(AppLocalizations.of(context)
|
content: Text(AppLocalizations.of(context)
|
||||||
|
@ -392,6 +415,7 @@ class _DynamicAlbumViewerState extends State<DynamicAlbumViewer>
|
||||||
);
|
);
|
||||||
} else if (file_util.isSupportedVideoFormat(f)) {
|
} else if (file_util.isSupportedVideoFormat(f)) {
|
||||||
yield _VideoListItem(
|
yield _VideoListItem(
|
||||||
|
file: f,
|
||||||
account: widget.account,
|
account: widget.account,
|
||||||
previewUrl: previewUrl,
|
previewUrl: previewUrl,
|
||||||
onTap: () => _onItemTap(i),
|
onTap: () => _onItemTap(i),
|
||||||
|
@ -433,13 +457,27 @@ abstract class _ListItem implements SelectableItem {
|
||||||
final VoidCallback _onTap;
|
final VoidCallback _onTap;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ImageListItem extends _ListItem {
|
abstract class _FileListItem extends _ListItem {
|
||||||
_ImageListItem({
|
_FileListItem({
|
||||||
@required this.file,
|
@required this.file,
|
||||||
|
VoidCallback onTap,
|
||||||
|
}) : super(
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
|
||||||
|
final File file;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageListItem extends _FileListItem {
|
||||||
|
_ImageListItem({
|
||||||
|
@required File file,
|
||||||
@required this.account,
|
@required this.account,
|
||||||
@required this.previewUrl,
|
@required this.previewUrl,
|
||||||
VoidCallback onTap,
|
VoidCallback onTap,
|
||||||
}) : super(onTap: onTap);
|
}) : super(
|
||||||
|
file: file,
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
buildWidget(BuildContext context) {
|
buildWidget(BuildContext context) {
|
||||||
|
@ -450,17 +488,20 @@ class _ImageListItem extends _ListItem {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final File file;
|
|
||||||
final Account account;
|
final Account account;
|
||||||
final String previewUrl;
|
final String previewUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VideoListItem extends _ListItem {
|
class _VideoListItem extends _FileListItem {
|
||||||
_VideoListItem({
|
_VideoListItem({
|
||||||
|
@required File file,
|
||||||
@required this.account,
|
@required this.account,
|
||||||
@required this.previewUrl,
|
@required this.previewUrl,
|
||||||
VoidCallback onTap,
|
VoidCallback onTap,
|
||||||
}) : super(onTap: onTap);
|
}) : super(
|
||||||
|
file: file,
|
||||||
|
onTap: onTap,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
buildWidget(BuildContext context) {
|
buildWidget(BuildContext context) {
|
||||||
|
|
|
@ -21,8 +21,10 @@ import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||||
import 'package:nc_photos/iterable_extension.dart';
|
import 'package:nc_photos/iterable_extension.dart';
|
||||||
import 'package:nc_photos/k.dart' as k;
|
import 'package:nc_photos/k.dart' as k;
|
||||||
import 'package:nc_photos/metadata_task_manager.dart';
|
import 'package:nc_photos/metadata_task_manager.dart';
|
||||||
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
import 'package:nc_photos/pref.dart';
|
import 'package:nc_photos/pref.dart';
|
||||||
import 'package:nc_photos/primitive.dart';
|
import 'package:nc_photos/primitive.dart';
|
||||||
|
import 'package:nc_photos/share_handler.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/remove.dart';
|
import 'package:nc_photos/use_case/remove.dart';
|
||||||
|
@ -159,6 +161,14 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
title: Text(AppLocalizations.of(context)
|
title: Text(AppLocalizations.of(context)
|
||||||
.selectionAppBarTitle(selectedListItems.length)),
|
.selectionAppBarTitle(selectedListItems.length)),
|
||||||
actions: [
|
actions: [
|
||||||
|
if (platform_k.isAndroid)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.share),
|
||||||
|
tooltip: AppLocalizations.of(context).shareSelectedTooltip,
|
||||||
|
onPressed: () {
|
||||||
|
_onSelectionAppBarSharePressed(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.playlist_add),
|
icon: const Icon(Icons.playlist_add),
|
||||||
tooltip: AppLocalizations.of(context).addSelectedToAlbumTooltip,
|
tooltip: AppLocalizations.of(context).addSelectedToAlbumTooltip,
|
||||||
|
@ -260,6 +270,19 @@ class _HomePhotosState extends State<HomePhotos>
|
||||||
arguments: ViewerArguments(widget.account, _backingFiles, index));
|
arguments: ViewerArguments(widget.account, _backingFiles, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSelectionAppBarSharePressed(BuildContext context) {
|
||||||
|
assert(platform_k.isAndroid);
|
||||||
|
final selected = selectedListItems
|
||||||
|
.whereType<_FileListItem>()
|
||||||
|
.map((e) => e.file)
|
||||||
|
.toList();
|
||||||
|
ShareHandler().shareFiles(context, widget.account, selected).then((_) {
|
||||||
|
setState(() {
|
||||||
|
clearSelectedItems();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _onSelectionAppBarAddToAlbumPressed(BuildContext context) {
|
void _onSelectionAppBarAddToAlbumPressed(BuildContext context) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -699,4 +722,5 @@ class _VideoListItem extends _FileListItem {
|
||||||
|
|
||||||
enum _SelectionAppBarMenuOption {
|
enum _SelectionAppBarMenuOption {
|
||||||
archive,
|
archive,
|
||||||
|
delete,
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import 'package:nc_photos/mobile/notification.dart';
|
||||||
import 'package:nc_photos/mobile/platform.dart'
|
import 'package:nc_photos/mobile/platform.dart'
|
||||||
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
if (dart.library.html) 'package:nc_photos/web/platform.dart' as platform;
|
||||||
import 'package:nc_photos/platform/k.dart' as platform_k;
|
import 'package:nc_photos/platform/k.dart' as platform_k;
|
||||||
|
import 'package:nc_photos/share_handler.dart';
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/use_case/remove.dart';
|
import 'package:nc_photos/use_case/remove.dart';
|
||||||
|
@ -298,6 +299,18 @@ class _ViewerState extends State<Viewer> {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
if (platform_k.isAndroid)
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.share_outlined,
|
||||||
|
color: Colors.white.withOpacity(.87),
|
||||||
|
),
|
||||||
|
tooltip: AppLocalizations.of(context).shareTooltip,
|
||||||
|
onPressed: () => _onSharePressed(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
@ -543,6 +556,12 @@ class _ViewerState extends State<Viewer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSharePressed(BuildContext context) {
|
||||||
|
assert(platform_k.isAndroid);
|
||||||
|
final file = widget.streamFiles[_pageController.page.round()];
|
||||||
|
ShareHandler().shareFiles(context, widget.account, [file]);
|
||||||
|
}
|
||||||
|
|
||||||
void _onDownloadPressed(BuildContext context) async {
|
void _onDownloadPressed(BuildContext context) async {
|
||||||
final file = widget.streamFiles[_pageController.page.round()];
|
final file = widget.streamFiles[_pageController.page.round()];
|
||||||
_log.info("[_onDownloadPressed] Downloading file: ${file.path}");
|
_log.info("[_onDownloadPressed] Downloading file: ${file.path}");
|
||||||
|
|
Loading…
Reference in a new issue