diff --git a/android/src/main/kotlin/com/example/imagegallerysaver/ImageGallerySaverPlugin.kt b/android/src/main/kotlin/com/example/imagegallerysaver/ImageGallerySaverPlugin.kt index c88c80f..c760f22 100644 --- a/android/src/main/kotlin/com/example/imagegallerysaver/ImageGallerySaverPlugin.kt +++ b/android/src/main/kotlin/com/example/imagegallerysaver/ImageGallerySaverPlugin.kt @@ -42,6 +42,7 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { val image = call.argument("imageBytes") val quality = call.argument("quality") val name = call.argument("name") + val albumName = call.argument("albumName") result.success( saveImageToGallery( @@ -49,7 +50,7 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { image ?: ByteArray(0), 0, image?.size ?: 0 - ), quality, name + ), quality, name, albumName ) ) } @@ -69,8 +70,9 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { methodChannel.setMethodCallHandler(null); } - private fun generateUri(extension: String = "", name: String? = null): Uri? { + private fun generateUri(extension: String = "", name: String? = null, albumName: String? = null): Uri? { var fileName = name ?: System.currentTimeMillis().toString() + var albumName = if (albumName == null) Environment.DIRECTORY_PICTURES else "${Environment.DIRECTORY_PICTURES}/$albumName" val mimeType = getMIMEType(extension) val isVideo = mimeType?.startsWith("video")==true @@ -86,7 +88,7 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { put( MediaStore.MediaColumns.RELATIVE_PATH, when { isVideo -> Environment.DIRECTORY_MOVIES - else -> Environment.DIRECTORY_PICTURES + else -> albumName } ) if (!TextUtils.isEmpty(mimeType)) { @@ -148,7 +150,8 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { private fun saveImageToGallery( bmp: Bitmap?, quality: Int?, - name: String? + name: String?, + albumName: String? ): HashMap { // check parameters if (bmp == null || quality == null) { @@ -161,7 +164,7 @@ class ImageGallerySaverPlugin : FlutterPlugin, MethodCallHandler { var fos: OutputStream? = null var success = false try { - fileUri = generateUri("jpg", name = name) + fileUri = generateUri("jpg", name = name , albumName = albumName) if (fileUri != null) { fos = context.contentResolver.openOutputStream(fileUri) if (fos != null) { diff --git a/ios/Classes/SwiftImageGallerySaverPlugin.swift b/ios/Classes/SwiftImageGallerySaverPlugin.swift index 876c318..df932d2 100644 --- a/ios/Classes/SwiftImageGallerySaverPlugin.swift +++ b/ios/Classes/SwiftImageGallerySaverPlugin.swift @@ -21,10 +21,11 @@ public class SwiftImageGallerySaverPlugin: NSObject, FlutterPlugin { let image = UIImage(data: imageData), let quality = arguments["quality"] as? Int, let _ = arguments["name"], + let albumName = arguments["albumName"] as? String, let isReturnImagePath = arguments["isReturnImagePathOfIOS"] as? Bool else { return } let newImage = image.jpegData(compressionQuality: CGFloat(quality / 100))! - saveImage(UIImage(data: newImage) ?? image, isReturnImagePath: isReturnImagePath) + saveImage(UIImage(data: newImage) ?? image, isReturnImagePath: isReturnImagePath , customAlbumName: albumName) } else if (call.method == "saveFileToGallery") { guard let arguments = call.arguments as? [String: Any], let path = arguments["file"] as? String, @@ -75,8 +76,8 @@ public class SwiftImageGallerySaverPlugin: NSObject, FlutterPlugin { }) } - func saveImage(_ image: UIImage, isReturnImagePath: Bool) { - if !isReturnImagePath { + func saveImage(_ image: UIImage, isReturnImagePath: Bool , customAlbumName: String) { + if !isReturnImagePath && customAlbumName == "" { UIImageWriteToSavedPhotosAlbum(image, self, #selector(didFinishSavingImage(image:error:contextInfo:)), nil) return } @@ -84,8 +85,20 @@ public class SwiftImageGallerySaverPlugin: NSObject, FlutterPlugin { var imageIds: [String] = [] PHPhotoLibrary.shared().performChanges( { + var assetCollectionChangeRequest: PHAssetCollectionChangeRequest? + let assetCollection = self.fetchAssetCollectionForAlbum(customAlbumName) + + if assetCollection == nil { + assetCollectionChangeRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: customAlbumName) + } else { + assetCollectionChangeRequest = PHAssetCollectionChangeRequest(for: assetCollection!) + } + let req = PHAssetChangeRequest.creationRequestForAsset(from: image) - if let imageId = req.placeholderForCreatedAsset?.localIdentifier { + let assetPlaceholder = req.placeholderForCreatedAsset + assetCollectionChangeRequest?.addAssets([assetPlaceholder!] as NSFastEnumeration) + + if let imageId = assetPlaceholder?.localIdentifier { imageIds.append(imageId) } }, completionHandler: { [unowned self] (success, error) in @@ -100,15 +113,23 @@ public class SwiftImageGallerySaverPlugin: NSObject, FlutterPlugin { imageAsset.requestContentEditingInput(with: options) { [unowned self] (contentEditingInput, info) in if let urlStr = contentEditingInput?.fullSizeImageURL?.absoluteString { self.saveResult(isSuccess: true, filePath: urlStr) - } - } + } } + } } else { self.saveResult(isSuccess: false, error: self.errorMessage) } } }) } + + func fetchAssetCollectionForAlbum(_ albumName: String) -> PHAssetCollection? { + let fetchOptions = PHFetchOptions() + fetchOptions.predicate = NSPredicate(format: "title = %@", albumName) + let fetchResult: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions) + + return fetchResult.firstObject + } func saveImageAtFileUrl(_ url: String, isReturnImagePath: Bool) { if !isReturnImagePath { diff --git a/lib/image_gallery_saver.dart b/lib/image_gallery_saver.dart index ffb12f2..80073c5 100644 --- a/lib/image_gallery_saver.dart +++ b/lib/image_gallery_saver.dart @@ -13,12 +13,14 @@ class ImageGallerySaver { static FutureOr saveImage(Uint8List imageBytes, {int quality = 80, String? name, + String? albumName = '', bool isReturnImagePathOfIOS = false}) async { final result = await _channel.invokeMethod('saveImageToGallery', { 'imageBytes': imageBytes, 'quality': quality, 'name': name, + 'albumName': albumName, 'isReturnImagePathOfIOS': isReturnImagePathOfIOS }); return result;