From db1c836a3e53b54743f07a544e39861a487682ad Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Sat, 29 Mar 2025 20:38:36 +0800 Subject: [PATCH] opt: share/save video cover Closes #563 Signed-off-by: bggRGjQaUbCoE --- lib/common/widgets/image_save.dart | 13 ++++++- .../interactiveviewer_gallery.dart | 38 ++----------------- lib/pages/video/detail/view.dart | 12 ++++++ lib/pages/video/detail/view_v.dart | 31 +++++++++++++++ .../video/detail/widgets/header_control.dart | 34 +++++++++++++---- lib/utils/download.dart | 31 +++++++++++++++ 6 files changed, 116 insertions(+), 43 deletions(-) diff --git a/lib/common/widgets/image_save.dart b/lib/common/widgets/image_save.dart index aa5cee4f..8c0484a9 100644 --- a/lib/common/widgets/image_save.dart +++ b/lib/common/widgets/image_save.dart @@ -72,6 +72,17 @@ void imageSaveDialog({ ), ), const SizedBox(width: 4), + IconButton( + tooltip: '分享', + onPressed: () { + DownloadUtils.onShareImg(cover ?? ''); + }, + icon: Icon( + Icons.share, + size: 20, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), IconButton( tooltip: '保存封面图', onPressed: () async { @@ -89,7 +100,7 @@ void imageSaveDialog({ size: 20, color: Theme.of(context).colorScheme.onSurfaceVariant, ), - ) + ), ], ), ), diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index 4bed2fb1..ad5aa4da 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -1,21 +1,16 @@ import 'dart:io'; -import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:share_plus/share_plus.dart'; import 'interactive_viewer_boundary.dart'; import 'interactive_viewer.dart' as custom; @@ -380,7 +375,7 @@ class _InteractiveviewerGalleryState extends State itemBuilder: (context) { return [ PopupMenuItem( - onTap: () => onShareImg( + onTap: () => DownloadUtils.onShareImg( widget.sources[currentIndex.value].url), child: const Text("分享图片"), ), @@ -442,34 +437,6 @@ class _InteractiveviewerGalleryState extends State ); } - // 图片分享 - void onShareImg(String imgUrl) async { - try { - SmartDialog.showLoading(); - var response = await Request() - .get(imgUrl, options: Options(responseType: ResponseType.bytes)); - final temp = await getTemporaryDirectory(); - SmartDialog.dismiss(); - String imgName = - "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; - var path = '${temp.path}/$imgName'; - File(path).writeAsBytesSync(response.data); - - Rect? sharePositionOrigin; - if (Platform.isIOS && (await Utils.isIpad())) { - sharePositionOrigin = Rect.fromLTWH(0, 0, Get.width, Get.height / 2); - } - - Share.shareXFiles( - [XFile(path)], - subject: imgUrl, - sharePositionOrigin: sharePositionOrigin, - ); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - } - Widget _itemBuilder(index) { return Center( child: Hero( @@ -573,7 +540,8 @@ class _InteractiveviewerGalleryState extends State children: [ ListTile( onTap: () { - onShareImg(widget.sources[currentIndex.value].url); + DownloadUtils.onShareImg( + widget.sources[currentIndex.value].url); Get.back(); }, dense: true, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7378d3ce..b23bfe13 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -18,6 +18,7 @@ import 'package:PiliPlus/pages/video/detail/member/horizontal_member_page.dart'; import 'package:PiliPlus/pages/video/detail/reply_reply/view.dart'; import 'package:PiliPlus/pages/video/detail/view_point/view_points_page.dart'; import 'package:PiliPlus/pages/video/detail/widgets/ai_detail.dart'; +import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/id_utils.dart'; @@ -1047,6 +1048,12 @@ class _VideoDetailPageState extends State case 'note': videoDetailController.showNoteList(context); break; + case 'savePic': + DownloadUtils.downloadImg( + context, + [videoDetailController.videoItem['pic']], + ); + break; } }, itemBuilder: (BuildContext context) => @@ -1060,6 +1067,11 @@ class _VideoDetailPageState extends State value: 'note', child: Text('查看笔记'), ), + if (videoDetailController.videoItem['pic'] != null) + const PopupMenuItem( + value: 'savePic', + child: Text('保存封面'), + ), const PopupMenuItem( value: 'report', child: Text('举报'), diff --git a/lib/pages/video/detail/view_v.dart b/lib/pages/video/detail/view_v.dart index 9faf36df..f0f69f62 100644 --- a/lib/pages/video/detail/view_v.dart +++ b/lib/pages/video/detail/view_v.dart @@ -19,6 +19,7 @@ import 'package:PiliPlus/pages/video/detail/member/horizontal_member_page.dart'; import 'package:PiliPlus/pages/video/detail/reply_reply/view.dart'; import 'package:PiliPlus/pages/video/detail/view_point/view_points_page.dart'; import 'package:PiliPlus/pages/video/detail/widgets/ai_detail.dart'; +import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/global_data.dart'; import 'package:PiliPlus/utils/id_utils.dart'; @@ -889,6 +890,17 @@ class _VideoDetailPageVState extends State .showNoteList( context); break; + case 'savePic': + DownloadUtils + .downloadImg( + context, + [ + videoDetailController + .videoItem[ + 'pic'] + ], + ); + break; } }, itemBuilder: (BuildContext @@ -906,6 +918,14 @@ class _VideoDetailPageVState extends State value: 'note', child: Text('查看笔记'), ), + if (videoDetailController + .videoItem['pic'] != + null) + const PopupMenuItem< + String>( + value: 'savePic', + child: Text('保存封面'), + ), const PopupMenuItem( value: 'report', child: Text('举报'), @@ -1456,6 +1476,12 @@ class _VideoDetailPageVState extends State case 'note': videoDetailController.showNoteList(context); break; + case 'savePic': + DownloadUtils.downloadImg( + context, + [videoDetailController.videoItem['pic']], + ); + break; } }, itemBuilder: (BuildContext context) => @@ -1469,6 +1495,11 @@ class _VideoDetailPageVState extends State value: 'note', child: Text('查看笔记'), ), + if (videoDetailController.videoItem['pic'] != null) + const PopupMenuItem( + value: 'savePic', + child: Text('保存封面'), + ), const PopupMenuItem( value: 'report', child: Text('举报'), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index cfa5f4e9..08a6a0f7 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -9,6 +9,7 @@ import 'package:PiliPlus/models/common/super_resolution_type.dart'; import 'package:PiliPlus/pages/bangumi/introduction/controller.dart'; import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; import 'package:PiliPlus/pages/video/detail/introduction/widgets/action_item.dart'; +import 'package:PiliPlus/utils/download.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -163,6 +164,19 @@ class HeaderControlState extends State { leading: const Icon(Icons.note_alt_outlined, size: 20), title: const Text('查看笔记', style: titleStyle), ), + if (widget.videoDetailCtr.videoItem['pic'] != null) + ListTile( + dense: true, + onTap: () { + Get.back(); + DownloadUtils.downloadImg( + context, + [widget.videoDetailCtr.videoItem['pic']], + ); + }, + leading: const Icon(Icons.image_outlined, size: 20), + title: const Text('保存封面', style: titleStyle), + ), ListTile( dense: true, onTap: () => {Get.back(), scheduleExit()}, @@ -1118,13 +1132,19 @@ class HeaderControlState extends State { SmartDialog.showToast('已保存'); } } catch (e) { - Share.shareXFiles([ - XFile.fromData( - res.data, - name: name, - mimeType: 'application/json', - ) - ]); + Share.shareXFiles( + [ + XFile.fromData( + res.data, + name: name, + mimeType: 'application/json', + ), + ], + sharePositionOrigin: await Utils.isIpad() + ? Rect.fromLTWH( + 0, 0, Get.width, Get.height / 2) + : null, + ); } } } catch (e) { diff --git a/lib/utils/download.dart b/lib/utils/download.dart index 0b0502ba..c2538b5e 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -7,13 +7,44 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:live_photo_maker/live_photo_maker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:saver_gallery/saver_gallery.dart'; import 'dart:io'; +import 'package:share_plus/share_plus.dart'; + class DownloadUtils { + // 图片分享 + static void onShareImg(String imgUrl) async { + try { + SmartDialog.showLoading(); + var response = await Request() + .get(imgUrl, options: Options(responseType: ResponseType.bytes)); + final temp = await getTemporaryDirectory(); + SmartDialog.dismiss(); + String imgName = + "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; + var path = '${temp.path}/$imgName'; + File(path).writeAsBytesSync(response.data); + + Rect? sharePositionOrigin; + if (Platform.isIOS && (await Utils.isIpad())) { + sharePositionOrigin = Rect.fromLTWH(0, 0, Get.width, Get.height / 2); + } + + Share.shareXFiles( + [XFile(path)], + subject: imgUrl, + sharePositionOrigin: sharePositionOrigin, + ); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + } + // 获取存储权限 static Future requestStoragePer(BuildContext context) async { await Permission.storage.request();