mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
Add support to copy image and url in viewer
This commit is contained in:
@@ -28,7 +28,8 @@ class DioImage extends ImageProvider<DioImage> {
|
||||
///
|
||||
/// The arguments [url] and [scale] must not be null.
|
||||
/// [dio] will be the default [Dio] if not set.
|
||||
DioImage.string(String url, {this.scale = 1.0, this.headers, Dio? dio})
|
||||
DioImage.string(String url,
|
||||
{this.scale = 1.0, this.headers, this.onData, Dio? dio})
|
||||
: dio = dio ?? defaultDio,
|
||||
url = Uri.parse(url);
|
||||
|
||||
@@ -36,7 +37,7 @@ class DioImage extends ImageProvider<DioImage> {
|
||||
///
|
||||
/// The arguments [url] and [scale] must not be null.
|
||||
/// [dio] will be the default [Dio] if not set.
|
||||
DioImage(this.url, {this.scale = 1.0, this.headers, Dio? dio})
|
||||
DioImage(this.url, {this.scale = 1.0, this.headers, this.onData, Dio? dio})
|
||||
: dio = dio ?? defaultDio;
|
||||
|
||||
/// The URL from which the image will be fetched.
|
||||
@@ -53,6 +54,8 @@ class DioImage extends ImageProvider<DioImage> {
|
||||
/// [dio] will be the default [Dio] if not set.
|
||||
final Dio dio;
|
||||
|
||||
final void Function(Uint8List, Headers, String)? onData;
|
||||
|
||||
@override
|
||||
Future<DioImage> obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture<DioImage>(this);
|
||||
@@ -89,6 +92,10 @@ class DioImage extends ImageProvider<DioImage> {
|
||||
try {
|
||||
final cache = await imageCaches.getCache(url.toString());
|
||||
if (cache != null) {
|
||||
if (onData != null) {
|
||||
onData!(cache!.$1, Headers.fromMap(cache!.$2),
|
||||
cache!.$3 ?? url.toString());
|
||||
}
|
||||
final buffer = await ui.ImmutableBuffer.fromUint8List(cache!.$1);
|
||||
return decode(buffer);
|
||||
}
|
||||
@@ -124,6 +131,10 @@ class DioImage extends ImageProvider<DioImage> {
|
||||
);
|
||||
}
|
||||
|
||||
if (onData != null) {
|
||||
onData!(bytes, response.headers, response.realUri.toString());
|
||||
}
|
||||
|
||||
if (isImageCacheEnabled) {
|
||||
try {
|
||||
await imageCaches.putCache(url.toString(), bytes,
|
||||
|
||||
@@ -20,6 +20,20 @@ enum ImageFmt {
|
||||
return "image/gif";
|
||||
}
|
||||
}
|
||||
|
||||
static ImageFmt? fromMimeType(String? mime) {
|
||||
if (mime == null) return null;
|
||||
switch (mime) {
|
||||
case "image/jpeg":
|
||||
return ImageFmt.jpg;
|
||||
case "image/png":
|
||||
return ImageFmt.png;
|
||||
case "image/gif":
|
||||
return ImageFmt.gif;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> copyImageToClipboard(Uint8List data, ImageFmt fmt) async {
|
||||
|
||||
@@ -10,12 +10,15 @@ import 'package:keymap/keymap.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:photo_view/photo_view_gallery.dart';
|
||||
import 'package:quiver/collection.dart';
|
||||
import 'package:super_context_menu/super_context_menu.dart';
|
||||
import '../api/file.dart';
|
||||
import '../api/gallery.dart';
|
||||
import '../components/fit_text.dart';
|
||||
import '../globals.dart';
|
||||
import '../platform/media_query.dart';
|
||||
import '../provider/dio_image_provider.dart';
|
||||
import '../utils/clipboard.dart';
|
||||
|
||||
final _log = Logger("SinglePageViewer");
|
||||
|
||||
@@ -57,6 +60,8 @@ class _SinglePageViewer extends State<SinglePageViewer>
|
||||
bool _inited = false;
|
||||
bool _showMenu = false;
|
||||
late PhotoViewController _photoViewController;
|
||||
final LruMap<int, (Uint8List, String?, String)> _imgData =
|
||||
LruMap(maximumSize: 20);
|
||||
void _updatePages() {
|
||||
if (_data == null) return;
|
||||
final displayAd = prefs.getBool("displayAd") ?? false;
|
||||
@@ -138,10 +143,10 @@ class _SinglePageViewer extends State<SinglePageViewer>
|
||||
_photoViewController.reset();
|
||||
}
|
||||
return PhotoViewGalleryPageOptions(
|
||||
imageProvider: DioImage.string(
|
||||
api.getFileUrl(f.id),
|
||||
dio: dio,
|
||||
),
|
||||
imageProvider: DioImage.string(api.getFileUrl(f.id), dio: dio,
|
||||
onData: (data, headers, url) {
|
||||
_imgData[index] = (data, headers.value("content-type"), url);
|
||||
}),
|
||||
initialScale: PhotoViewComputedScale.contained,
|
||||
heroAttributes: PhotoViewHeroAttributes(
|
||||
tag: data.token,
|
||||
@@ -216,11 +221,44 @@ class _SinglePageViewer extends State<SinglePageViewer>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWithContextMenu(BuildContext context, {required Widget child}) {
|
||||
final i18n = AppLocalizations.of(context)!;
|
||||
return ContextMenuWidget(
|
||||
menuProvider: (_) {
|
||||
var list = <MenuElement>[];
|
||||
final url = _imgData[_index]?.$3;
|
||||
if (url != null) {
|
||||
list.add(MenuAction(
|
||||
title: i18n.copyImgUrl,
|
||||
callback: () {
|
||||
copyTextToClipboard(url!).catchError((err) {
|
||||
_log.warning("Failed to copy image to clipboard:", err);
|
||||
});
|
||||
}));
|
||||
}
|
||||
final data = _imgData[_index]?.$1;
|
||||
if (data != null) {
|
||||
final fmt =
|
||||
ImageFmt.fromMimeType(_imgData[_index]?.$2) ?? ImageFmt.jpg;
|
||||
list.add(MenuAction(
|
||||
title: i18n.copyImage,
|
||||
callback: () {
|
||||
copyImageToClipboard(data!, fmt).catchError((err) {
|
||||
_log.warning("Failed to copy image to clipboard:", err);
|
||||
});
|
||||
}));
|
||||
}
|
||||
return Menu(children: list);
|
||||
},
|
||||
child: child);
|
||||
}
|
||||
|
||||
Widget _buildViewer(BuildContext context) {
|
||||
return _buildWithTap(context,
|
||||
child: _buildWithKeyboardSupport(context,
|
||||
child: _buildWithScrollSupport(context,
|
||||
child: _buildGallery(context))));
|
||||
child: _buildWithContextMenu(context,
|
||||
child: _buildGallery(context)))));
|
||||
}
|
||||
|
||||
Widget _buildTopAppBar(BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user