From 382d1140923194b0ebf7d1bb4ac64a479db29044 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sun, 1 Sep 2024 18:36:17 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Reorg=20input=20streams=20?= =?UTF-8?q?when=20saving=20asset=20on=20Android=20(#1178)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 + MIGRATION_GUIDE.md | 32 ++++++- .../photo_manager/core/PhotoManager.kt | 17 ++-- .../photo_manager/core/PhotoManagerPlugin.kt | 16 ++-- .../photo_manager/core/entity/AssetEntity.kt | 19 ++-- .../photo_manager/core/utils/CommonExt.kt | 10 -- .../photo_manager/core/utils/IDBUtils.kt | 92 ++++++++++--------- .../page/developer/develop_index_page.dart | 2 +- example/lib/page/save_image_example.dart | 2 +- ios/Classes/PMPlugin.m | 16 ++-- ios/Classes/core/PMManager.h | 13 ++- ios/Classes/core/PMManager.m | 38 ++++---- lib/src/internal/editor.dart | 23 +++-- lib/src/internal/plugin.dart | 30 +++--- 14 files changed, 174 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0877f681..6b7cf671 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,14 @@ To know more about breaking changes, see the [Migration Guide][]. ## Unreleased +### Breaking changes + +`saveImage` now requires `filename` rather than `title` other save methods do not require `title` anymore. + ### Improvements - Allows saving assets with a given orientation value. +- Improves the reading sequence when saving assets, which likely fixes issues with wrong orientation value. - Reads image size from EXIF rather than decoding from the bitmap factory. - Upgrades Android EXIF library. diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 4f6e4d21..cf460051 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -5,20 +5,23 @@ If you want to see the new feature support, please refer to [readme][] and [chan * [Migration Guide](#migration-guide) - * [3.0.x to 3.1](#30x-to-31) + * [3.x to 3.3](#3x-to-33) * [Overall](#overall) + * [`saveImage`](#saveimage) + * [3.0.x to 3.1](#30x-to-31) + * [Overall](#overall-1) * [`containsLivePhotos`](#containslivephotos) * [`AlbumType`](#albumtype) * [2.x to 3.0](#2x-to-30) - * [Overall](#overall-1) + * [Overall](#overall-2) * [`AssetEntityImage` and `AssetEntityImageProvider`](#assetentityimage-and-assetentityimageprovider) * [2.x to 2.8](#2x-to-28) - * [Overall](#overall-2) - * [2.x to 2.2](#2x-to-22) * [Overall](#overall-3) + * [2.x to 2.2](#2x-to-22) + * [Overall](#overall-4) * [`assetCount`](#assetcount) * [1.x to 2.0](#1x-to-20) - * [Overall](#overall-4) + * [Overall](#overall-5) * [API migrations](#api-migrations) * [`getAssetListPaged`](#getassetlistpaged) * [Filtering only videos](#filtering-only-videos) @@ -28,6 +31,25 @@ If you want to see the new feature support, please refer to [readme][] and [chan * [0.5 To 0.6](#05-to-06) +## 3.x to 3.3 + +### Overall + +In order to let developers write the most precise API usage, +the `title` of `saveImage` has migrated to `filename`. + +#### `saveImage` + +Before: +```dart +final entity = await PhotoManager.editor.saveImage(bytes, title: 'new.jpg'); +``` + +After: +```dart +final entity = await PhotoManager.editor.saveImage(bytes, filename: 'new.jpg'); +``` + ## 3.0.x to 3.1 ### Overall diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt index 9829c5e1..67d2ed22 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManager.kt @@ -18,7 +18,6 @@ import com.fluttercandies.photo_manager.core.utils.IDBUtils import com.fluttercandies.photo_manager.thumb.ThumbnailUtil import com.fluttercandies.photo_manager.util.LogUtils import com.fluttercandies.photo_manager.util.ResultHandler -import java.io.File import java.util.concurrent.Executors class PhotoManager(private val context: Context) { @@ -167,36 +166,34 @@ class PhotoManager(private val context: Context) { } fun saveImage( - image: ByteArray, + bytes: ByteArray, + filename: String, title: String, description: String, relativePath: String, orientation: Int? ): AssetEntity? { - return dbUtils.saveImage(context, image, title, description, relativePath, orientation) + return dbUtils.saveImage(context, bytes, filename, title, description, relativePath, orientation) } fun saveImage( - path: String, + filePath: String, title: String, description: String, relativePath: String, orientation: Int? ): AssetEntity? { - return dbUtils.saveImage(context, path, title, description, relativePath, orientation) + return dbUtils.saveImage(context, filePath, title, description, relativePath, orientation) } fun saveVideo( - path: String, + filePath: String, title: String, desc: String, relativePath: String, orientation: Int? ): AssetEntity? { - if (!File(path).exists()) { - return null - } - return dbUtils.saveVideo(context, path, title, desc, relativePath, orientation) + return dbUtils.saveVideo(context, filePath, title, desc, relativePath, orientation) } fun assetExists(id: String, resultHandler: ResultHandler) { diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt index d706b08b..a9e1ff56 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/PhotoManagerPlugin.kt @@ -465,13 +465,15 @@ class PhotoManagerPlugin( Methods.saveImage -> { try { - val image = call.argument("image")!! + val bytes = call.argument("image")!! + val filename = call.argument("filename") ?: "" val title = call.argument("title") ?: "" val desc = call.argument("desc") ?: "" val relativePath = call.argument("relativePath") ?: "" val orientation = call.argument("orientation") val entity = photoManager.saveImage( - image, + bytes, + filename, title, desc, relativePath, @@ -491,13 +493,13 @@ class PhotoManagerPlugin( Methods.saveImageWithPath -> { try { - val imagePath = call.argument("path")!! + val filePath = call.argument("path")!! val title = call.argument("title") ?: "" val desc = call.argument("desc") ?: "" val relativePath = call.argument("relativePath") ?: "" val orientation = call.argument("orientation") val entity = photoManager.saveImage( - imagePath, + filePath, title, desc, relativePath, @@ -517,13 +519,13 @@ class PhotoManagerPlugin( Methods.saveVideo -> { try { - val videoPath = call.argument("path")!! + val filePath = call.argument("path")!! val title = call.argument("title")!! val desc = call.argument("desc") ?: "" val relativePath = call.argument("relativePath") ?: "" val orientation = call.argument("orientation") val entity = photoManager.saveVideo( - videoPath, + filePath, title, desc, relativePath, @@ -634,4 +636,4 @@ class PhotoManagerPlugin( val arguments = argument>("option")!! return ConvertUtils.convertToFilterOptions(arguments) } -} \ No newline at end of file +} diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/AssetEntity.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/AssetEntity.kt index 5aa0e09c..b74c2d9c 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/AssetEntity.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/entity/AssetEntity.kt @@ -7,7 +7,7 @@ import java.io.File data class AssetEntity( val id: Long, - var path: String, + val path: String, val duration: Long, val createDt: Long, val width: Int, @@ -15,9 +15,9 @@ data class AssetEntity( val type: Int, val displayName: String, val modifiedDate: Long, - var orientation: Int, - var lat: Double? = null, - var lng: Double? = null, + val orientation: Int, + val lat: Double? = null, + val lng: Double? = null, val androidQRelativePath: String? = null, val mimeType: String? = null ) { @@ -26,10 +26,9 @@ data class AssetEntity( MediaStoreUtils.convertTypeToMediaType(type) ) - val relativePath: String? - get() = if (isAboveAndroidQ) { - androidQRelativePath - } else { - File(path).parent - } + val relativePath: String? = if (isAboveAndroidQ) { + androidQRelativePath + } else { + File(path).parent + } } diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/CommonExt.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/CommonExt.kt index 599f95e8..b594df39 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/CommonExt.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/CommonExt.kt @@ -13,13 +13,3 @@ fun String.checkDirs() { targetFile.parentFile!!.mkdirs() } } - -fun InputStream.getOrientationDegrees(): Int { - return try { - this.use { - return@use ExifInterface(it).rotationDegrees - } - } catch (ignored: Throwable) { - 0 - } -} diff --git a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt index 835c166e..84c50de4 100644 --- a/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt +++ b/android/src/main/kotlin/com/fluttercandies/photo_manager/core/utils/IDBUtils.kt @@ -5,7 +5,6 @@ import android.content.ContentUris import android.content.ContentValues import android.content.Context import android.database.Cursor -import android.graphics.BitmapFactory import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build @@ -251,14 +250,23 @@ interface IDBUtils { fun saveImage( context: Context, bytes: ByteArray, + filename: String, title: String, desc: String, relativePath: String, orientation: Int? ): AssetEntity? { var inputStream = ByteArrayInputStream(bytes) - val typeFromStream: String = URLConnection.guessContentTypeFromName(title) - ?: URLConnection.guessContentTypeFromStream(inputStream) + fun refreshStream() { + inputStream = ByteArrayInputStream(bytes) + } + + val typeFromStream: String = URLConnection.guessContentTypeFromName(filename) + ?: inputStream.let { + val type = URLConnection.guessContentTypeFromStream(inputStream) + refreshStream() + type + } ?: "image/*" val exif = ExifInterface(inputStream) @@ -270,6 +278,7 @@ interface IDBUtils { orientation ?: if (isAboveAndroidQ) exif.rotationDegrees else 0, if (isAboveAndroidQ) null else exif.latLong ) + refreshStream() val timestamp = System.currentTimeMillis() / 1000 val values = ContentValues().apply { @@ -298,33 +307,36 @@ interface IDBUtils { } } - inputStream = ByteArrayInputStream(bytes) - val entity = insertUri( + return insertUri( context, inputStream, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values, - ) - entity?.let { - it.orientation = orientation ?: it.orientation - } - return entity + )?.copy(orientation = orientation ?: rotationDegrees) } fun saveImage( context: Context, - fromPath: String, + filePath: String, title: String, desc: String, relativePath: String, orientation: Int? ): AssetEntity? { - fromPath.checkDirs() - val file = File(fromPath) + filePath.checkDirs() + val file = File(filePath) var inputStream = FileInputStream(file) + fun refreshStream() { + inputStream = FileInputStream(file) + } + val typeFromStream: String = URLConnection.guessContentTypeFromName(title) - ?: URLConnection.guessContentTypeFromName(fromPath) - ?: URLConnection.guessContentTypeFromStream(inputStream) + ?: URLConnection.guessContentTypeFromName(filePath) + ?: inputStream.let { + val type = URLConnection.guessContentTypeFromStream(inputStream) + refreshStream() + type + } ?: "image/*" val exif = ExifInterface(inputStream) @@ -336,6 +348,7 @@ interface IDBUtils { orientation ?: if (isAboveAndroidQ) exif.rotationDegrees else 0, if (isAboveAndroidQ) null else exif.latLong ) + refreshStream() val shouldKeepPath = if (!isAboveAndroidQ) { val dir = Environment.getExternalStorageDirectory() @@ -368,60 +381,59 @@ interface IDBUtils { put(MediaStore.Images.ImageColumns.LONGITUDE, latLong.last()) } if (shouldKeepPath) { - put(DATA, fromPath) + put(DATA, filePath) } } - inputStream = FileInputStream(file) - val entity = insertUri( + return insertUri( context, inputStream, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values, shouldKeepPath - ) - entity?.let { - it.orientation = orientation ?: it.orientation - } - return entity + )?.copy(orientation = orientation ?: rotationDegrees) } fun saveVideo( context: Context, - fromPath: String, + filePath: String, title: String, desc: String, relativePath: String, orientation: Int? ): AssetEntity? { - fromPath.checkDirs() - val file = File(fromPath) + filePath.checkDirs() + val file = File(filePath) var inputStream = FileInputStream(file) - fun refreshInputStream() { + fun refreshStream() { inputStream = FileInputStream(file) } - val timestamp = System.currentTimeMillis() / 1000 - val info = VideoUtils.getPropertiesUseMediaPlayer(fromPath) val typeFromStream = URLConnection.guessContentTypeFromName(title) - ?: URLConnection.guessContentTypeFromName(fromPath) + ?: URLConnection.guessContentTypeFromName(filePath) + ?: inputStream.let { + val type = URLConnection.guessContentTypeFromStream(inputStream) + refreshStream() + type + } ?: "video/*" - val (rotationDegrees, latLong) = try { - val exif = ExifInterface(inputStream) + + val info = VideoUtils.getPropertiesUseMediaPlayer(filePath) + + val (rotationDegrees, latLong) = ExifInterface(inputStream).let { exif -> Pair( orientation ?: if (isAboveAndroidQ) exif.rotationDegrees else 0, if (isAboveAndroidQ) null else exif.latLong ) - } catch (e: Exception) { - Pair(orientation ?: 0, null) } - refreshInputStream() + refreshStream() val shouldKeepPath = if (!isAboveAndroidQ) { val dir = Environment.getExternalStorageDirectory() file.absolutePath.startsWith(dir.path) } else false + val timestamp = System.currentTimeMillis() / 1000 val values = ContentValues().apply { put( MediaStore.Files.FileColumns.MEDIA_TYPE, @@ -448,21 +460,17 @@ interface IDBUtils { put(MediaStore.Video.VideoColumns.LONGITUDE, latLong.last()) } if (shouldKeepPath) { - put(DATA, fromPath) + put(DATA, filePath) } } - val entity = insertUri( + return insertUri( context, inputStream, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values, shouldKeepPath - ) - entity?.let { - it.orientation = orientation ?: it.orientation - } - return entity + )?.copy(orientation = orientation ?: rotationDegrees) } private fun insertUri( diff --git a/example/lib/page/developer/develop_index_page.dart b/example/lib/page/developer/develop_index_page.dart index bd32ba1c..cfb7cbb1 100644 --- a/example/lib/page/developer/develop_index_page.dart +++ b/example/lib/page/developer/develop_index_page.dart @@ -262,7 +262,7 @@ class _DeveloperIndexPageState extends State { final assets = await PhotoManager.editor.darwin.saveLivePhoto( imageFile: imgFile, videoFile: videoFile, - title: 'preview_0', + filename: 'preview_0', ); print('save live photo result : ${assets?.id}'); } finally { diff --git a/example/lib/page/save_image_example.dart b/example/lib/page/save_image_example.dart index d396241b..4900ed46 100644 --- a/example/lib/page/save_image_example.dart +++ b/example/lib/page/save_image_example.dart @@ -195,7 +195,7 @@ class _SaveMediaExampleState extends State { await checkRequest(() async { final AssetEntity? asset = await PhotoManager.editor.saveImage( uint8List, - title: '${DateTime.now().millisecondsSinceEpoch}.jpg', + filename: '${DateTime.now().millisecondsSinceEpoch}.jpg', ); Log.d('saved asset: $asset'); }); diff --git a/ios/Classes/PMPlugin.m b/ios/Classes/PMPlugin.m index 0eeb32b9..23fabf3d 100644 --- a/ios/Classes/PMPlugin.m +++ b/ios/Classes/PMPlugin.m @@ -403,10 +403,10 @@ - (void)handleMethodResultHandler:(ResultHandler *)handler manager:(PMManager *) }]; } else if ([call.method isEqualToString:@"saveImage"]) { NSData *data = [call.arguments[@"image"] data]; - NSString *title = call.arguments[@"title"]; + NSString *filename = call.arguments[@"filename"]; NSString *desc = call.arguments[@"desc"]; [manager saveImage:data - title:title + filename:filename desc:desc block:^(PMAssetEntity *asset) { if (!asset) { @@ -417,10 +417,10 @@ - (void)handleMethodResultHandler:(ResultHandler *)handler manager:(PMManager *) }]; } else if ([call.method isEqualToString:@"saveImageWithPath"]) { NSString *path = call.arguments[@"path"]; - NSString *title = call.arguments[@"title"]; + NSString *filename = call.arguments[@"title"]; NSString *desc = call.arguments[@"desc"]; [manager saveImageWithPath:path - title:title + filename:filename desc:desc block:^(PMAssetEntity *asset) { if (!asset) { @@ -431,10 +431,10 @@ - (void)handleMethodResultHandler:(ResultHandler *)handler manager:(PMManager *) }]; } else if ([call.method isEqualToString:@"saveVideo"]) { NSString *videoPath = call.arguments[@"path"]; - NSString *title = call.arguments[@"title"]; + NSString *filename = call.arguments[@"title"]; NSString *desc = call.arguments[@"desc"]; [manager saveVideo:videoPath - title:title + filename:filename desc:desc block:^(PMAssetEntity *asset) { if (!asset) { @@ -446,11 +446,11 @@ - (void)handleMethodResultHandler:(ResultHandler *)handler manager:(PMManager *) } else if ([call.method isEqualToString:@"saveLivePhoto"]) { NSString *videoPath = call.arguments[@"videoPath"]; NSString *imagePath = call.arguments[@"imagePath"]; - NSString *title = call.arguments[@"title"]; + NSString *filename = call.arguments[@"filename"]; NSString *desc = call.arguments[@"desc"]; [manager saveLivePhoto:imagePath videoPath:videoPath - title:title + filename:filename desc:desc block:^(PMAssetEntity *asset) { if (!asset) { diff --git a/ios/Classes/core/PMManager.h b/ios/Classes/core/PMManager.h index 8ab11ac9..ffb105a4 100644 --- a/ios/Classes/core/PMManager.h +++ b/ios/Classes/core/PMManager.h @@ -53,18 +53,23 @@ typedef void (^AssetResult)(PMAssetEntity *); - (void)deleteWithIds:(NSArray *)ids changedBlock:(ChangeIds)block; - (void)saveImage:(NSData *)data - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block; +- (void)saveImageWithPath:(NSString *)path + filename:(NSString *)filename + desc:(NSString *)desc + block:(void (^)(PMAssetEntity *))block; + - (void)saveVideo:(NSString *)path - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block; - (void)saveLivePhoto:(NSString *)imagePath videoPath:(NSString *)videoPath - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block; @@ -80,8 +85,6 @@ typedef void (^AssetResult)(PMAssetEntity *); - (NSArray *)getSubPathWithId:(NSString *)id type:(int)type albumType:(int)albumType option:(NSObject *)option; -- (void)saveImageWithPath:(NSString *)path title:(NSString *)title desc:(NSString *)desc block:(void (^)(PMAssetEntity *))block; - - (void)copyAssetWithId:(NSString *)id toGallery:(NSString *)gallery block:(void (^)(PMAssetEntity *entity, NSString *msg))block; - (void)createFolderWithName:(NSString *)name parentId:(NSString *)id block:(void (^)(NSString *, NSString *))block; diff --git a/ios/Classes/core/PMManager.m b/ios/Classes/core/PMManager.m index 65495300..ebcc1f1a 100644 --- a/ios/Classes/core/PMManager.m +++ b/ios/Classes/core/PMManager.m @@ -126,14 +126,14 @@ - (NSUInteger)getAssetCountWithType:(int)type option:(NSObject *)f - (NSArray *)getAssetsWithType:(int)type option:(NSObject *)option start:(int)start end:(int)end { PHFetchOptions *options = [option getFetchOptions:type]; PHFetchResult *result = [PHAsset fetchAssetsWithOptions:options]; - + NSUInteger endOffset = end; if (endOffset > result.count) { endOffset = result.count; } - + NSMutableArray* array = [NSMutableArray new]; - + for (NSUInteger i = start; i < endOffset; i++){ if (i >= result.count) { break; @@ -142,7 +142,7 @@ - (NSUInteger)getAssetCountWithType:(int)type option:(NSObject *)f PMAssetEntity *pmAsset = [self convertPHAssetToAssetEntity:asset needTitle:[option needTitle]]; [array addObject: pmAsset]; } - + return array; } @@ -934,11 +934,11 @@ - (void)deleteWithIds:(NSArray *)ids changedBlock:(ChangeIds)block { } - (void)saveImage:(NSData *)data - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block { __block NSString *assetId = nil; - [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save image with data, length: %lu, title:%@, desc: %@", (unsigned long)data.length, title, desc]]; + [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save image with data, length: %lu, filename:%@, desc: %@", (unsigned long)data.length, filename, desc]]; [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ @@ -946,7 +946,7 @@ - (void)saveImage:(NSData *)data [PHAssetCreationRequest creationRequestForAsset]; PHAssetResourceCreationOptions *options = [PHAssetResourceCreationOptions new]; - [options setOriginalFilename:title]; + [options setOriginalFilename:filename]; [request addResourceWithType:PHAssetResourceTypePhoto data:data options:options]; @@ -963,8 +963,11 @@ - (void)saveImage:(NSData *)data }]; } -- (void)saveImageWithPath:(NSString *)path title:(NSString *)title desc:(NSString *)desc block:(void (^)(PMAssetEntity *))block { - [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save image with path: %@ title:%@, desc: %@", path, title, desc]]; +- (void)saveImageWithPath:(NSString *)path + filename:(NSString *)filename + desc:(NSString *)desc + block:(void (^)(PMAssetEntity *))block { + [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save image with path: %@ filename:%@, desc: %@", path, filename, desc]]; __block NSString *assetId = nil; [[PHPhotoLibrary sharedPhotoLibrary] @@ -973,7 +976,7 @@ - (void)saveImageWithPath:(NSString *)path title:(NSString *)title desc:(NSStrin [PHAssetCreationRequest creationRequestForAsset]; PHAssetResourceCreationOptions *options = [PHAssetResourceCreationOptions new]; - [options setOriginalFilename:title]; + [options setOriginalFilename:filename]; NSData *data = [NSData dataWithContentsOfFile:path]; [request addResourceWithType:PHAssetResourceTypePhoto data:data @@ -992,18 +995,17 @@ - (void)saveImageWithPath:(NSString *)path title:(NSString *)title desc:(NSStrin } - (void)saveVideo:(NSString *)path - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block { - [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save video with path: %@, title: %@, desc %@", - path, title, desc]]; + [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save video with path: %@, filename: %@, desc %@", path, filename, desc]]; NSURL *fileURL = [NSURL fileURLWithPath:path]; __block NSString *assetId = nil; [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; PHAssetResourceCreationOptions *options = [PHAssetResourceCreationOptions new]; - [options setOriginalFilename:title]; + [options setOriginalFilename:filename]; [request addResourceWithType:PHAssetResourceTypeVideo fileURL:fileURL options:options]; assetId = request.placeholderForCreatedAsset.localIdentifier; } @@ -1020,11 +1022,11 @@ - (void)saveVideo:(NSString *)path - (void)saveLivePhoto:(NSString *)imagePath videoPath:(NSString *)videoPath - title:(NSString *)title + filename:(NSString *)filename desc:(NSString *)desc block:(AssetResult)block { - [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save LivePhoto with imagePath: %@, videoPath: %@, title: %@, desc %@", - imagePath, videoPath, title, desc]]; + [PMLogUtils.sharedInstance info:[NSString stringWithFormat:@"save LivePhoto with imagePath: %@, videoPath: %@, filename: %@, desc %@", + imagePath, videoPath, filename, desc]]; NSURL *videoURL = [NSURL fileURLWithPath:videoPath]; NSURL *imageURL = [NSURL fileURLWithPath:imagePath]; __block NSString *assetId = nil; @@ -1032,7 +1034,7 @@ - (void)saveLivePhoto:(NSString *)imagePath performChanges:^{ PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; PHAssetResourceCreationOptions *options = [PHAssetResourceCreationOptions new]; - [options setOriginalFilename:title]; + [options setOriginalFilename:filename]; [request addResourceWithType:PHAssetResourceTypePhoto fileURL:imageURL options:options]; [request addResourceWithType:PHAssetResourceTypePairedVideo fileURL:videoURL options:options]; assetId = request.placeholderForCreatedAsset.localIdentifier; diff --git a/lib/src/internal/editor.dart b/lib/src/internal/editor.dart index a8b19bdc..cd74c0cf 100644 --- a/lib/src/internal/editor.dart +++ b/lib/src/internal/editor.dart @@ -50,9 +50,10 @@ class Editor { /// Save image to gallery from the given [data]. /// + /// [filename] will be helpful to evaluate the MIME type from the [data]. + /// /// {@template photo_manager.Editor.TitleWhenSaving} - /// [title] typically means the filename of the saving entity, which can be - /// obtained by `basename(file.path)`. + /// [title] is the title field on Android, and the original filename on iOS. /// {@endtemplate} /// /// {@template photo_manager.Editor.DescriptionWhenSaving} @@ -72,13 +73,15 @@ class Editor { /// {@endtemplate} Future saveImage( typed_data.Uint8List data, { - required String title, + required String filename, + String? title, String? desc, String? relativePath, int? orientation, }) { return plugin.saveImage( data, + filename: filename, title: title, desc: desc, relativePath: relativePath, @@ -86,7 +89,7 @@ class Editor { ); } - /// Save image to gallery from the given [path]. + /// Save image to gallery from the given [filePath]. /// /// {@macro photo_manager.Editor.TitleWhenSaving} /// @@ -96,14 +99,14 @@ class Editor { /// /// {@macro photo_manager.Editor.OrientationWhenSaving} Future saveImageWithPath( - String path, { - required String title, + String filePath, { + String? title, String? desc, String? relativePath, int? orientation, }) { return plugin.saveImageWithPath( - path, + filePath, title: title, desc: desc, relativePath: relativePath, @@ -122,7 +125,7 @@ class Editor { /// {@macro photo_manager.Editor.OrientationWhenSaving} Future saveVideo( File file, { - required String title, + String? title, String? desc, String? relativePath, int? orientation, @@ -280,14 +283,14 @@ class DarwinEditor { Future saveLivePhoto({ required File imageFile, required File videoFile, - required String title, + required String filename, String? desc, String? relativePath, }) { return plugin.saveLivePhoto( imageFile: imageFile, videoFile: videoFile, - title: title, + filename: filename, desc: desc, relativePath: relativePath, ); diff --git a/lib/src/internal/plugin.dart b/lib/src/internal/plugin.dart index ac3002e6..1c1d0e58 100644 --- a/lib/src/internal/plugin.dart +++ b/lib/src/internal/plugin.dart @@ -347,7 +347,8 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin { Future saveImage( typed_data.Uint8List data, { - required String? title, + required String filename, + String? title, String? desc, String? relativePath, int? orientation, @@ -357,8 +358,9 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin { PMConstants.mSaveImage, { 'image': data, + 'filename': filename, 'title': title, - 'desc': desc ?? '', + 'desc': desc, 'relativePath': relativePath, 'orientation': orientation, ...onlyAddPermission, @@ -369,28 +371,28 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin { } return ConvertUtils.convertMapToAsset( result.cast(), - title: title, + title: filename, ); } Future saveImageWithPath( - String path, { - required String title, + String filePath, { + String? title, String? desc, String? relativePath, int? orientation, }) async { _throwIfOrientationInvalid(orientation); - final File file = File(path); + final File file = File(filePath); if (!file.existsSync()) { - throw ArgumentError('$path does not exists'); + throw ArgumentError('$filePath does not exists'); } final Map? result = await _channel.invokeMethod( PMConstants.mSaveImageWithPath, { - 'path': path, + 'path': file.absolute.path, 'title': title, - 'desc': desc ?? '', + 'desc': desc, 'relativePath': relativePath, 'orientation': orientation, ...onlyAddPermission, @@ -407,7 +409,7 @@ class PhotoManagerPlugin with BasePlugin, IosPlugin, AndroidPlugin, OhosPlugin { Future saveVideo( File file, { - required String? title, + String? title, String? desc, String? relativePath, int? orientation, @@ -669,7 +671,7 @@ mixin IosPlugin on BasePlugin { Future saveLivePhoto({ required File imageFile, required File videoFile, - required String? title, + required String? filename, String? desc, String? relativePath, }) async { @@ -685,13 +687,13 @@ mixin IosPlugin on BasePlugin { { 'imagePath': imageFile.absolute.path, 'videoPath': videoFile.absolute.path, - 'title': title, - 'desc': desc ?? '', + 'filename': filename, + 'desc': desc, 'relativePath': relativePath, ...onlyAddPermission, }, ); - return ConvertUtils.convertMapToAsset(result.cast(), title: title); + return ConvertUtils.convertMapToAsset(result.cast(), title: filename); } Future iosCreateAlbum(