From 6976ab8d3b6658029939591815eda4e573c5869d Mon Sep 17 00:00:00 2001 From: Wenqi Li Date: Thu, 11 May 2023 21:56:25 +0800 Subject: [PATCH] update "mediumType" to be optional parameter of "listAlbums" to allow fetch both type of media --- .../morbit/photogallery/PhotoGalleryPlugin.kt | 100 ++++++++++++------ example/lib/main.dart | 3 +- ios/Classes/SwiftPhotoGalleryPlugin.swift | 50 ++++----- lib/photo_gallery.dart | 2 +- 4 files changed, 92 insertions(+), 63 deletions(-) diff --git a/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt b/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt index e5a7e58..b83a31e 100644 --- a/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt +++ b/android/src/main/kotlin/com/morbit/photogallery/PhotoGalleryPlugin.kt @@ -98,7 +98,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { val mediumType = call.argument("mediumType") executor.submit { result.success( - listAlbums(mediumType!!) + listAlbums(mediumType) ) } } @@ -111,11 +111,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { val take = call.argument("take") executor.submit { result.success( - when (mediumType) { - imageType -> listImages(albumId!!, newest!!, total!!, skip, take) - videoType -> listVideos(albumId!!, newest!!, total!!, skip, take) - else -> null - } + listMedia(mediumType, albumId!!, newest!!, total!!, skip, take) ) } } @@ -173,24 +169,24 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { } } - private fun listAlbums(mediumType: String): List> { + private fun listAlbums(mediumType: String?): List> { return when (mediumType) { imageType -> { - listImageAlbums() + listImageAlbums().values.toList() } videoType -> { - listVideoAlbums() + listVideoAlbums().values.toList() } else -> { - listOf() + listAllAlbums().values.toList() } } } - private fun listImageAlbums(): List> { + private fun listImageAlbums(): Map> { this.context.run { var total = 0 - val albumHashMap = mutableMapOf>() + val albumHashMap = hashMapOf>() val imageProjection = arrayOf( MediaStore.Images.Media.BUCKET_DISPLAY_NAME, @@ -214,7 +210,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { val album = albumHashMap[bucketId] if (album == null) { val folderName = cursor.getString(bucketColumn) - albumHashMap[bucketId] = mutableMapOf( + albumHashMap[bucketId] = hashMapOf( "id" to bucketId, "mediumType" to imageType, "name" to folderName, @@ -228,25 +224,25 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { } } - val albumList = mutableListOf>() - albumList.add( - mapOf( + val albumLinkedMap = linkedMapOf>() + albumLinkedMap.put( + allAlbumId, + hashMapOf( "id" to allAlbumId, "mediumType" to imageType, "name" to allAlbumName, "count" to total ) ) - albumList.addAll(albumHashMap.values) - return albumList + albumLinkedMap.putAll(albumHashMap) + return albumLinkedMap } - return listOf() } - private fun listVideoAlbums(): List> { + private fun listVideoAlbums(): Map> { this.context.run { var total = 0 - val albumHashMap = mutableMapOf>() + val albumHashMap = hashMapOf>() val videoProjection = arrayOf( MediaStore.Video.Media.BUCKET_DISPLAY_NAME, @@ -270,7 +266,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { val album = albumHashMap[bucketId] if (album == null) { val folderName = cursor.getString(bucketColumn) - albumHashMap[bucketId] = mutableMapOf( + albumHashMap[bucketId] = hashMapOf( "id" to bucketId, "mediumType" to videoType, "name" to folderName, @@ -284,16 +280,58 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler { } } - val albumList = mutableListOf>() - albumList.add(mapOf( - "id" to allAlbumId, - "mediumType" to videoType, - "name" to allAlbumName, - "count" to total)) - albumList.addAll(albumHashMap.values) - return albumList + val albumLinkedMap = linkedMapOf>() + albumLinkedMap.put( + allAlbumId, + hashMapOf( + "id" to allAlbumId, + "mediumType" to videoType, + "name" to allAlbumName, + "count" to total + ) + ) + albumLinkedMap.putAll(albumHashMap) + return albumLinkedMap + } + } + + private fun listAllAlbums(): Map> { + val imageMap = this.listImageAlbums() + val videoMap = this.listVideoAlbums() + val albumMap = (imageMap.keys + videoMap.keys).associateWith { + mapOf( + "id" to it, + "mediumType" to null, + "name" to imageMap[it]?.get("name"), + "count" to (imageMap[it]?.get("count") ?: 0) as Int + (videoMap[it]?.get("count") ?: 0) as Int, + ) + } + return albumMap + } + + private fun listMedia(mediumType: String?, albumId: String, newest: Boolean, total: Int, skip: Int?, take: Int?): Map { + return when (mediumType) { + imageType -> { + listImages(albumId, newest, total, skip, take) + } + videoType -> { + listVideos(albumId, newest, total, skip, take) + } + else -> { + val images = listImages(albumId, newest, total, skip, take).get("items") as List> + val videos = listVideos(albumId, newest, total, skip, take).get("items") as List> + var items = (images +videos).sortedWith(compareBy> {it.get("creationDate") as Long}.thenBy { it.get("modifiedDate") as Long }) + if (newest) { + items = items.reversed() + } + mapOf( + "newest" to newest, + "start" to (skip ?: 0), + "total" to total, + "items" to items + ) + } } - return listOf() } private fun listImages(albumId: String, newest: Boolean, total: Int, skip: Int?, take: Int?): Map { diff --git a/example/lib/main.dart b/example/lib/main.dart index 7252b5e..74dc9b8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -29,8 +29,7 @@ class _MyAppState extends State { Future initAsync() async { if (await _promptPermissionSetting()) { - List albums = - await PhotoGallery.listAlbums(mediumType: MediumType.image); + List albums = await PhotoGallery.listAlbums(); setState(() { _albums = albums; _loading = false; diff --git a/ios/Classes/SwiftPhotoGalleryPlugin.swift b/ios/Classes/SwiftPhotoGalleryPlugin.swift index 623fca6..cb1f830 100644 --- a/ios/Classes/SwiftPhotoGalleryPlugin.swift +++ b/ios/Classes/SwiftPhotoGalleryPlugin.swift @@ -14,14 +14,14 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { if(call.method == "listAlbums") { let arguments = call.arguments as! Dictionary - let mediumType = arguments["mediumType"] as! String + let mediumType = arguments["mediumType"] as? String let hideIfEmpty = arguments["hideIfEmpty"] as? Bool result(listAlbums(mediumType: mediumType, hideIfEmpty: hideIfEmpty)) } else if(call.method == "listMedia") { let arguments = call.arguments as! Dictionary let albumId = arguments["albumId"] as! String - let mediumType = arguments["mediumType"] as! String + let mediumType = arguments["mediumType"] as? String let newest = arguments["newest"] as! Bool let skip = arguments["skip"] as? NSNumber let take = arguments["take"] as? NSNumber @@ -90,11 +90,13 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { private var assetCollections : [PHAssetCollection] = [] - private func listAlbums(mediumType: String, hideIfEmpty: Bool? = true) -> [NSDictionary] { + private func listAlbums(mediumType: String?, hideIfEmpty: Bool? = true) -> [[String: Any?]] { self.assetCollections = [] let fetchOptions = PHFetchOptions() - var total = 0 - var albums = [NSDictionary]() + if #available(iOS 9, *) { + fetchOptions.fetchLimit = 1 + } + var albums = [[String: Any?]]() var albumIds = Set() func addCollection (collection: PHAssetCollection, hideIfEmpty: Bool) -> Void { @@ -106,14 +108,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { guard !albumIds.contains(albumId) else { return } albumIds.insert(albumId) - let options = PHFetchOptions() - options.predicate = self.predicateFromMediumType(mediumType: mediumType) - if #available(iOS 9, *) { - fetchOptions.fetchLimit = 1 - } - let count = PHAsset.fetchAssets(in: collection, options: options).count + let count = countMedia(collection: collection, mediumType: mediumType) if(count > 0 || !hideIfEmpty) { - total+=count self.assetCollections.append(collection) albums.append([ "id": collection.localIdentifier, @@ -156,15 +152,15 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { "id": "__ALL__", "mediumType": mediumType, "name": "All", - "count" : countMedia(collection: nil, mediumTypes: [mediumType]), + "count" : countMedia(collection: nil, mediumType: mediumType), ], at: 0) return albums } - private func countMedia(collection: PHAssetCollection?, mediumTypes: [String]) -> Int { + private func countMedia(collection: PHAssetCollection?, mediumType: String?) -> Int { let options = PHFetchOptions() - options.predicate = self.predicateFromMediumTypes(mediumTypes: mediumTypes) + options.predicate = self.predicateFromMediumType(mediumType: mediumType) if(collection == nil) { return PHAsset.fetchAssets(with: options).count } @@ -172,13 +168,13 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { return PHAsset.fetchAssets(in: collection ?? PHAssetCollection.init(), options: options).count } - private func listMedia(albumId: String, mediumType: String, newest: Bool, skip: NSNumber?, take: NSNumber?) -> NSDictionary { + private func listMedia(albumId: String, mediumType: String?, newest: Bool, skip: NSNumber?, take: NSNumber?) -> NSDictionary { let fetchOptions = PHFetchOptions() + fetchOptions.predicate = predicateFromMediumType(mediumType: mediumType) fetchOptions.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: newest ? false : true), NSSortDescriptor(key: "modificationDate", ascending: newest ? false : true) ] - fetchOptions.predicate = predicateFromMediumType(mediumType: mediumType) let collection = self.assetCollections.first(where: { (collection) -> Bool in collection.localIdentifier == albumId @@ -277,9 +273,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { ) { let manager = PHImageManager.default() let fetchOptions = PHFetchOptions() - if (mediumType != nil) { - fetchOptions.predicate = self.predicateFromMediumType(mediumType: mediumType!) - } + fetchOptions.predicate = self.predicateFromMediumType(mediumType: mediumType) fetchOptions.sortDescriptors = [ NSSortDescriptor(key: "creationDate", ascending: false), NSSortDescriptor(key: "modificationDate", ascending: false) @@ -509,16 +503,14 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin { } } - private func predicateFromMediumTypes(mediumTypes: [String]) -> NSPredicate { - let predicates = mediumTypes.map { (dartValue) -> NSPredicate in - return predicateFromMediumType(mediumType: dartValue) + private func predicateFromMediumType(mediumType: String?) -> NSPredicate? { + guard let type = mediumType else { + return nil } - return NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: predicates) - } - - private func predicateFromMediumType(mediumType: String) -> NSPredicate { - let swiftType = toSwiftMediumType(value: mediumType) - return NSPredicate(format: "mediaType = %d", swiftType!.rawValue) + guard let swiftType = toSwiftMediumType(value: type) else { + return nil + } + return NSPredicate(format: "mediaType = %d", swiftType.rawValue) } private func extractFileExtensionFromUTI(uti: String?) -> String { diff --git a/lib/photo_gallery.dart b/lib/photo_gallery.dart index d10db28..70f8ed8 100644 --- a/lib/photo_gallery.dart +++ b/lib/photo_gallery.dart @@ -22,7 +22,7 @@ class PhotoGallery { /// List all available gallery albums and counts number of items of [MediumType]. static Future> listAlbums({ - required MediumType mediumType, + MediumType? mediumType, bool? hideIfEmpty = true, }) async { final json = await _channel.invokeMethod('listAlbums', {