diff --git a/lib/common/widgets/imageview.dart b/lib/common/widgets/imageview.dart index 22f6dd849..4713c8bc0 100644 --- a/lib/common/widgets/imageview.dart +++ b/lib/common/widgets/imageview.dart @@ -92,6 +92,15 @@ Widget imageview( late final enableLivePhoto = GStorage.enableLivePhoto; + int parseSize(size) { + return switch (size) { + int() => size, + double() => size.round(), + String() => int.tryParse(size) ?? 1, + _ => 1, + }; + } + return NineGridView( type: NineGridType.weiBo, margin: const EdgeInsets.only(top: 6), @@ -111,17 +120,19 @@ Widget imageview( onViewImage?.call(); context.imageView( initialPage: index, - imgList: picArr - .map( - (item) => SourceModel( - sourceType: item.isLivePhoto && enableLivePhoto - ? SourceType.livePhoto - : SourceType.networkImage, - url: item.url, - liveUrl: item.liveUrl, - ), - ) - .toList(), + imgList: picArr.map( + (item) { + bool isLive = item.isLivePhoto && enableLivePhoto; + return SourceModel( + sourceType: + isLive ? SourceType.livePhoto : SourceType.networkImage, + url: item.url, + liveUrl: isLive ? item.liveUrl : null, + width: isLive ? parseSize(item.width) : null, + height: isLive ? parseSize(item.height) : null, + ); + }, + ).toList(), onDismissed: onDismissed, ); } diff --git a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart index a9d0aa1be..eed28d1ae 100644 --- a/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart +++ b/lib/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart @@ -41,11 +41,15 @@ class SourceModel { final SourceType sourceType; final String url; final String? liveUrl; + final int? width; + final int? height; const SourceModel({ this.sourceType = SourceType.networkImage, required this.url, this.liveUrl, + this.width, + this.height, }); } @@ -387,17 +391,6 @@ class _InteractiveviewerGalleryState extends State }, child: const Text("保存图片"), ), - if (widget.sources[currentIndex.value].sourceType == - SourceType.livePhoto) - PopupMenuItem( - onTap: () { - DownloadUtils.downloadVideo( - context, - widget.sources[currentIndex.value].liveUrl!, - ); - }, - child: const Text("保存 Live"), - ), if (widget.sources.length > 1) PopupMenuItem( onTap: () { @@ -410,6 +403,23 @@ class _InteractiveviewerGalleryState extends State }, child: const Text("保存全部图片"), ), + if (widget.sources[currentIndex.value].sourceType == + SourceType.livePhoto) + PopupMenuItem( + onTap: () { + DownloadUtils.downloadLivePhoto( + context: context, + url: widget.sources[currentIndex.value].url, + liveUrl: widget + .sources[currentIndex.value].liveUrl!, + width: + widget.sources[currentIndex.value].width!, + height: widget + .sources[currentIndex.value].height!, + ); + }, + child: const Text("保存 Live Photo"), + ), ]; }, child: const Icon(Icons.more_horiz, color: Colors.white), @@ -565,22 +575,6 @@ class _InteractiveviewerGalleryState extends State dense: true, title: const Text('保存图片', style: TextStyle(fontSize: 14)), ), - if (widget.sources[currentIndex.value].sourceType == - SourceType.livePhoto) - ListTile( - onTap: () { - Get.back(); - DownloadUtils.downloadVideo( - context, - widget.sources[currentIndex.value].liveUrl!, - ); - }, - dense: true, - title: const Text( - '保存 Live', - style: TextStyle(fontSize: 14), - ), - ), if (widget.sources.length > 1) ListTile( onTap: () { @@ -593,6 +587,25 @@ class _InteractiveviewerGalleryState extends State dense: true, title: const Text('保存全部图片', style: TextStyle(fontSize: 14)), ), + if (widget.sources[currentIndex.value].sourceType == + SourceType.livePhoto) + ListTile( + onTap: () { + Get.back(); + DownloadUtils.downloadLivePhoto( + context: context, + url: widget.sources[currentIndex.value].url, + liveUrl: widget.sources[currentIndex.value].liveUrl!, + width: widget.sources[currentIndex.value].width!, + height: widget.sources[currentIndex.value].height!, + ); + }, + dense: true, + title: const Text( + '保存 Live Photo', + style: TextStyle(fontSize: 14), + ), + ), ], ), ); diff --git a/lib/utils/download.dart b/lib/utils/download.dart index 14cbf99ea..ac324c900 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -6,6 +6,7 @@ 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: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'; @@ -86,28 +87,65 @@ class DownloadUtils { return await requestStoragePer(context); } - static Future downloadVideo(BuildContext context, String url) async { + static Future downloadLivePhoto({ + required BuildContext context, + required String url, + required String liveUrl, + required int width, + required int height, + }) async { try { if (!await checkPermissionDependOnSdkInt(context)) { return; } SmartDialog.showLoading(msg: '正在下载'); + + String tmpPath = (await getTemporaryDirectory()).path; + String time = DateTime.now() + .toString() + .replaceAll(' ', '_') + .replaceAll(':', '-') + .split('.') + .first; + late String imageName = + "cover_$time.${url.split('.').lastOrNull ?? 'jpg'}"; + late String imagePath = '$tmpPath/$imageName'; String videoName = - "video_${DateTime.now().toString().replaceAll(' ', '_').replaceAll(':', '-').split('.').first}.${url.split('.').lastOrNull ?? 'mp4'}"; - String savePath = '${(await getTemporaryDirectory()).path}/$videoName'; - await Request.dio.download(url, savePath); - SmartDialog.showLoading(msg: '正在保存'); - final SaveResult result = await SaverGallery.saveFile( - filePath: savePath, - fileName: videoName, - androidRelativePath: "Pictures/PiliPlus", - skipIfExists: false, - ); - SmartDialog.dismiss(); - if (result.isSuccess) { - SmartDialog.showToast('「$videoName」已保存 '); + "video_$time.${liveUrl.split('.').lastOrNull ?? 'mp4'}"; + String videoPath = '$tmpPath/$videoName'; + + await Request.dio.download(liveUrl, videoPath); + + if (Platform.isIOS) { + await Request.dio.download(url, imagePath); + SmartDialog.showLoading(msg: '正在保存'); + bool success = await LivePhotoMaker.create( + coverImage: imagePath, + imagePath: null, + voicePath: videoPath, + width: width, + height: height, + ); + SmartDialog.dismiss(); + if (success) { + SmartDialog.showToast(' Live Photo 已保存 '); + } else { + SmartDialog.showToast('保存失败'); + } } else { - SmartDialog.showToast('保存失败,${result.errorMessage}'); + SmartDialog.showLoading(msg: '正在保存'); + final SaveResult result = await SaverGallery.saveFile( + filePath: videoPath, + fileName: videoName, + androidRelativePath: "Pictures/PiliPlus", + skipIfExists: false, + ); + SmartDialog.dismiss(); + if (result.isSuccess) { + SmartDialog.showToast(' 已保存 '); + } else { + SmartDialog.showToast('保存失败,${result.errorMessage}'); + } } return true; diff --git a/pubspec.lock b/pubspec.lock index 26e0bffef..2a7c4069e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1079,6 +1079,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + live_photo_maker: + dependency: "direct main" + description: + name: live_photo_maker + sha256: "9af3965bd9d2ab55b0d4d0a1e4041fdcc9ef6c6c44543c8412667541a054f58b" + url: "https://pub.dev" + source: hosted + version: "0.0.6" logger: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 291b6f743..56c8a692c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -180,6 +180,7 @@ dependencies: brotli: ^0.6.0 expandable: ^5.0.1 flex_seed_scheme: ^3.4.1 + live_photo_maker: ^0.0.6 dependency_overrides: screen_brightness: ^2.0.1