photo_gallery/ios/Classes/PhotoGalleryPlugin.swift

654 lines
22 KiB
Swift
Raw Permalink Normal View History

2020-08-14 18:07:27 +08:00
import Foundation
import MobileCoreServices
import Flutter
import UIKit
import Photos
public class PhotoGalleryPlugin: NSObject, FlutterPlugin {
2020-08-14 18:07:27 +08:00
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "photo_gallery", binaryMessenger: registrar.messenger())
let instance = PhotoGalleryPlugin()
2020-08-14 18:07:27 +08:00
registrar.addMethodCallDelegate(instance, channel: channel)
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if(call.method == "listAlbums") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let mediumType = arguments["mediumType"] as? String
let hideIfEmpty = arguments["hideIfEmpty"] as? Bool
result(listAlbums(mediumType: mediumType, hideIfEmpty: hideIfEmpty))
2020-08-14 18:07:27 +08:00
}
else if(call.method == "listMedia") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let albumId = arguments["albumId"] as! String
let mediumType = arguments["mediumType"] as? String
let newest = arguments["newest"] as! Bool
2020-08-14 18:07:27 +08:00
let skip = arguments["skip"] as? NSNumber
let take = arguments["take"] as? NSNumber
2023-06-25 22:41:36 -07:00
let lightWeight = arguments["lightWeight"] as? Bool
2023-07-27 20:28:38 +08:00
result(listMedia(
albumId: albumId,
mediumType: mediumType,
newest: newest,
skip: skip,
take: take,
lightWeight: lightWeight
))
2020-08-14 18:07:27 +08:00
}
else if(call.method == "getMedium") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let mediumId = arguments["mediumId"] as! String
2023-07-27 16:16:15 +08:00
do {
let medium = try getMedium(mediumId: mediumId)
result(medium)
} catch {
result(nil)
}
2020-08-14 18:07:27 +08:00
}
else if(call.method == "getThumbnail") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let mediumId = arguments["mediumId"] as! String
let width = arguments["width"] as? NSNumber
let height = arguments["height"] as? NSNumber
let highQuality = arguments["highQuality"] as? Bool
getThumbnail(
mediumId: mediumId,
width: width,
height: height,
highQuality: highQuality,
completion: { (data: Data?, error: Error?) -> Void in
result(data)
2023-05-14 02:23:53 +08:00
}
)
2020-08-14 18:07:27 +08:00
}
else if(call.method == "getAlbumThumbnail") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let albumId = arguments["albumId"] as! String
let mediumType = arguments["mediumType"] as? String
let newest = arguments["newest"] as! Bool
2020-08-14 18:07:27 +08:00
let width = arguments["width"] as? Int
let height = arguments["height"] as? Int
let highQuality = arguments["highQuality"] as? Bool
getAlbumThumbnail(
albumId: albumId,
mediumType: mediumType,
newest: newest,
2020-08-14 18:07:27 +08:00
width: width,
height: height,
highQuality: highQuality,
completion: { (data: Data?, error: Error?) -> Void in
result(data)
2023-05-14 02:23:53 +08:00
}
)
2020-08-14 18:07:27 +08:00
}
else if(call.method == "getFile") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let mediumId = arguments["mediumId"] as! String
let mimeType = arguments["mimeType"] as? String
2020-08-14 18:07:27 +08:00
getFile(
mediumId: mediumId,
mimeType: mimeType,
2020-08-14 18:07:27 +08:00
completion: { (filepath: String?, error: Error?) -> Void in
result(filepath?.replacingOccurrences(of: "file://", with: ""))
2023-05-14 02:23:53 +08:00
}
)
2020-08-14 18:07:27 +08:00
}
2023-07-24 00:42:07 +08:00
else if(call.method == "deleteMedium") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let mediumId = arguments["mediumId"] as! String
deleteMedium(
mediumId: mediumId,
completion: { (success: Bool, error: Error?) -> Void in
result(success)
}
)
}
2021-01-10 21:09:25 +08:00
else if(call.method == "cleanCache") {
cleanCache()
result(nil)
}
2020-08-14 18:07:27 +08:00
else {
result(FlutterMethodNotImplemented)
}
}
2023-07-27 20:28:38 +08:00
private var assetCollections: [PHAssetCollection] = []
private func listAlbums(mediumType: String?, hideIfEmpty: Bool? = true) -> [[String: Any?]] {
2020-08-14 18:07:27 +08:00
self.assetCollections = []
let fetchOptions = PHFetchOptions()
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
var albums = [[String: Any?]]()
2020-08-14 18:07:27 +08:00
var albumIds = Set<String>()
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
func addCollection (collection: PHAssetCollection, hideIfEmpty: Bool) -> Void {
let kRecentlyDeletedCollectionSubtype = PHAssetCollectionSubtype(rawValue: 1000000201)
guard collection.assetCollectionSubtype != kRecentlyDeletedCollectionSubtype else { return }
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
// De-duplicate by id.
let albumId = collection.localIdentifier
guard !albumIds.contains(albumId) else { return }
albumIds.insert(albumId)
2023-07-27 20:28:38 +08:00
let count = countMedia(collection: collection, mediumType: mediumType)
2020-08-14 18:07:27 +08:00
if(count > 0 || !hideIfEmpty) {
self.assetCollections.append(collection)
albums.append([
"id": collection.localIdentifier,
"name": collection.localizedTitle ?? "Unknown",
"count": count,
])
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
func processPHAssetCollections(fetchResult: PHFetchResult<PHAssetCollection>, hideIfEmpty: Bool) -> Void {
fetchResult.enumerateObjects { (assetCollection, _, _) in
addCollection(collection: assetCollection, hideIfEmpty: hideIfEmpty)
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
func processPHCollections (fetchResult: PHFetchResult<PHCollection>, hideIfEmpty: Bool) -> Void {
fetchResult.enumerateObjects { (collection, _, _) in
if let assetCollection = collection as? PHAssetCollection {
addCollection(collection: assetCollection, hideIfEmpty: hideIfEmpty)
} else if let collectionList = collection as? PHCollectionList {
2023-07-27 20:28:38 +08:00
processPHCollections(
fetchResult: PHCollectionList.fetchCollections(in: collectionList, options: nil),
hideIfEmpty: hideIfEmpty
)
2020-08-14 18:07:27 +08:00
}
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
// Smart Albums.
processPHAssetCollections(
2023-07-27 20:28:38 +08:00
fetchResult: PHAssetCollection.fetchAssetCollections(
with: .smartAlbum,
subtype: .albumRegular,
options: fetchOptions
),
hideIfEmpty: hideIfEmpty ?? true
2020-08-14 18:07:27 +08:00
)
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
// User-created collections.
processPHCollections(
fetchResult: PHAssetCollection.fetchTopLevelUserCollections(with: fetchOptions),
hideIfEmpty: hideIfEmpty ?? true
2020-08-14 18:07:27 +08:00
)
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
albums.insert([
"id": "__ALL__",
"name": "All",
2023-07-27 20:28:38 +08:00
"count": countMedia(collection: nil, mediumType: mediumType),
2020-08-14 18:07:27 +08:00
], at: 0)
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
return albums
}
2023-07-27 20:28:38 +08:00
private func countMedia(collection: PHAssetCollection?, mediumType: String?) -> Int {
2020-08-14 18:07:27 +08:00
let options = PHFetchOptions()
options.predicate = self.predicateFromMediumType(mediumType: mediumType)
2020-08-14 18:07:27 +08:00
if(collection == nil) {
return PHAsset.fetchAssets(with: options).count
}
2023-07-27 20:28:38 +08:00
2020-11-24 15:54:33 +07:00
return PHAsset.fetchAssets(in: collection ?? PHAssetCollection.init(), options: options).count
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
private func listMedia(
albumId: String,
mediumType: String?,
newest: Bool,
skip: NSNumber?,
take: NSNumber?,
lightWeight: Bool? = false
) -> NSDictionary {
2020-08-14 18:07:27 +08:00
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = predicateFromMediumType(mediumType: mediumType)
fetchOptions.sortDescriptors = [
NSSortDescriptor(key: "creationDate", ascending: newest ? false : true),
NSSortDescriptor(key: "modificationDate", ascending: newest ? false : true)
]
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let collection = self.assetCollections.first(where: { (collection) -> Bool in
collection.localIdentifier == albumId
})
2023-07-27 20:28:38 +08:00
let fetchResult: PHFetchResult<PHAsset>
if(albumId == "__ALL__") {
fetchResult = PHAsset.fetchAssets(with: fetchOptions)
} else {
fetchResult = PHAsset.fetchAssets(
in: collection ?? PHAssetCollection.init(),
options: fetchOptions
)
}
2020-08-14 18:07:27 +08:00
let start = skip?.intValue ?? 0
let total = fetchResult.count
let end = take == nil ? total : min(start + take!.intValue, total)
var items = [[String: Any?]]()
for index in start..<end {
let asset = fetchResult.object(at: index) as PHAsset
2023-06-25 22:41:36 -07:00
if(lightWeight == true) {
items.append(getMediumFromAssetLightWeight(asset: asset))
} else {
items.append(getMediumFromAsset(asset: asset))
}
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
return [
"start": start,
"items": items,
]
}
2023-07-27 20:28:38 +08:00
2023-07-27 16:16:15 +08:00
private func getMedium(mediumId: String) throws -> [String: Any?] {
2020-08-14 18:07:27 +08:00
let fetchOptions = PHFetchOptions()
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
let assets: PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [mediumId], options: fetchOptions)
2023-07-27 20:28:38 +08:00
if (assets.count <= 0) {
2023-07-27 16:16:15 +08:00
throw NSError(domain: "photo_gallery", code: 404)
} else {
2020-08-14 18:07:27 +08:00
let asset: PHAsset = assets[0]
2023-07-27 16:16:15 +08:00
return getMediumFromAsset(asset: asset)
2020-08-14 18:07:27 +08:00
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func getThumbnail(
mediumId: String,
width: NSNumber?,
height: NSNumber?,
highQuality: Bool?,
completion: @escaping (Data?, Error?) -> Void
) {
let manager = PHImageManager.default()
let fetchOptions = PHFetchOptions()
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
let assets: PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [mediumId], options: fetchOptions)
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
if (assets.count > 0) {
let asset: PHAsset = assets[0]
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let options = PHImageRequestOptions()
options.isSynchronous = false
options.version = .current
2020-09-20 16:03:29 +08:00
options.deliveryMode = (highQuality ?? false) ? .highQualityFormat : .fastFormat
options.isNetworkAccessAllowed = true
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let imageSize = CGSize(width: width?.intValue ?? 128, height: height?.intValue ?? 128)
manager.requestImage(
for: asset,
2023-07-27 20:28:38 +08:00
targetSize: CGSize(
width: imageSize.width * UIScreen.main.scale,
height: imageSize.height * UIScreen.main.scale
),
2020-08-14 18:07:27 +08:00
contentMode: PHImageContentMode.aspectFill,
options: options,
2023-07-27 20:28:38 +08:00
resultHandler: { (uiImage: UIImage?, info) in
2020-08-14 18:07:27 +08:00
guard let image = uiImage else {
2023-07-27 20:28:38 +08:00
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
2020-08-14 18:07:27 +08:00
return
}
let bytes = image.jpegData(compressionQuality: CGFloat(70))
completion(bytes, nil)
2023-05-14 02:23:53 +08:00
}
)
2020-08-14 18:07:27 +08:00
return
}
2023-07-27 20:28:38 +08:00
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func getAlbumThumbnail(
albumId: String,
mediumType: String?,
newest: Bool,
2020-08-14 18:07:27 +08:00
width: Int?,
height: Int?,
highQuality: Bool?,
completion: @escaping (Data?, Error?) -> Void
) {
let manager = PHImageManager.default()
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = self.predicateFromMediumType(mediumType: mediumType)
fetchOptions.sortDescriptors = [
NSSortDescriptor(key: "creationDate", ascending: !newest),
NSSortDescriptor(key: "modificationDate", ascending: !newest)
]
2020-08-14 18:07:27 +08:00
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
2023-07-27 20:28:38 +08:00
2024-02-19 13:16:04 +05:00
var assets: PHFetchResult<PHAsset>
if albumId == "__ALL__" {
assets = PHAsset.fetchAssets(with: fetchOptions)
} else if let collection = self.assetCollections.first(where: { $0.localIdentifier == albumId }) {
assets = PHAsset.fetchAssets(in: collection, options: fetchOptions)
} else {
// Handle the case where the collection is nil
print("Error: Collection with ID \(albumId) not found")
// You might want to provide a default value or handle this case according to your app's logic
// For now, let's just assign an empty fetch result
assets = PHAsset.fetchAssets(with: fetchOptions)
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
if (assets.count > 0) {
let asset: PHAsset = assets[0]
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let options = PHImageRequestOptions()
options.isSynchronous = false
options.version = .current
2020-09-20 16:03:29 +08:00
options.deliveryMode = (highQuality ?? false) ? .highQualityFormat : .fastFormat
options.isNetworkAccessAllowed = true
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let imageSize = CGSize(width: width ?? 128, height: height ?? 128)
manager.requestImage(
for: asset,
targetSize: CGSize(
2023-07-27 20:28:38 +08:00
width: imageSize.width * UIScreen.main.scale,
height: imageSize.height * UIScreen.main.scale
2020-08-14 18:07:27 +08:00
),
contentMode: PHImageContentMode.aspectFill,
options: options,
2023-07-27 20:28:38 +08:00
resultHandler: { (uiImage: UIImage?, info) in
2020-08-14 18:07:27 +08:00
guard let image = uiImage else {
2023-07-27 20:28:38 +08:00
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
2020-08-14 18:07:27 +08:00
return
}
let bytes = image.jpegData(compressionQuality: CGFloat(80))
completion(bytes, nil)
2023-05-14 02:23:53 +08:00
}
)
2020-08-14 18:07:27 +08:00
return
}
2023-07-27 20:28:38 +08:00
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
private func getFile(mediumId: String, mimeType: String?, completion: @escaping (String?, Error?) -> Void) {
2020-08-14 18:07:27 +08:00
let manager = PHImageManager.default()
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
let fetchOptions = PHFetchOptions()
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
let assets: PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [mediumId], options: fetchOptions)
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
if (assets.count > 0) {
let asset: PHAsset = assets[0]
if(asset.mediaType == PHAssetMediaType.image) {
let options = PHImageRequestOptions()
options.isSynchronous = false
options.version = .current
2020-09-20 16:03:29 +08:00
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
manager.requestImageData(
for: asset,
options: options,
resultHandler: { (data: Data?, uti: String?, orientation, info) in
DispatchQueue.main.async(execute: {
2021-01-10 14:57:40 +08:00
guard let imageData = data else {
2020-08-14 18:07:27 +08:00
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
return
}
2021-01-10 14:57:40 +08:00
guard let assetUTI = uti else {
completion(nil, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
2020-08-14 18:07:27 +08:00
return
}
if mimeType != nil {
let type = self.extractMimeTypeFromUTI(uti: assetUTI)
if type != mimeType {
let path = self.cacheImage(asset: asset, data: imageData, mimeType: mimeType!)
completion(path, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
return
}
}
2021-01-10 17:34:37 +08:00
let fileExt = self.extractFileExtensionFromUTI(uti: assetUTI)
2021-01-10 14:57:40 +08:00
let filepath = self.exportPathForAsset(asset: asset, ext: fileExt)
try! imageData.write(to: filepath, options: .atomic)
2020-08-14 18:07:27 +08:00
completion(filepath.absoluteString, nil)
})
2021-01-10 17:34:37 +08:00
}
)
2023-07-27 20:28:38 +08:00
} else if(asset.mediaType == PHAssetMediaType.video || asset.mediaType == PHAssetMediaType.audio) {
2020-08-14 18:07:27 +08:00
let options = PHVideoRequestOptions()
options.version = .current
2020-09-20 16:03:29 +08:00
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
2023-07-27 20:28:38 +08:00
2023-05-14 02:23:53 +08:00
manager.requestAVAsset(
forVideo: asset,
options: options,
resultHandler: { (avAsset, avAudioMix, info) in
DispatchQueue.main.async(execute: {
do {
let avAsset = avAsset as? AVURLAsset
let data = try Data(contentsOf: avAsset!.url)
let fileExt = self.extractFileExtensionFromAsset(asset: asset)
let filepath = self.exportPathForAsset(asset: asset, ext: fileExt)
try! data.write(to: filepath, options: .atomic)
completion(filepath.absoluteString, nil)
} catch {
completion(nil, NSError(domain: "photo_gallery", code: 500, userInfo: nil))
}
})
2021-01-10 17:34:37 +08:00
}
)
2020-08-14 18:07:27 +08:00
}
}
}
2023-07-27 20:28:38 +08:00
private func cacheImage(asset: PHAsset, data: Data, mimeType: String) -> String? {
if mimeType == "image/jpeg" {
let filepath = self.exportPathForAsset(asset: asset, ext: ".jpeg")
let uiImage = UIImage(data: data)
try! uiImage?.jpegData(compressionQuality: 100)?.write(to: filepath, options: .atomic)
return filepath.absoluteString
} else if mimeType == "image/png" {
let filepath = self.exportPathForAsset(asset: asset, ext: ".png")
let uiImage = UIImage(data: data)
try! uiImage?.pngData()?.write(to: filepath, options: .atomic)
return filepath.absoluteString
} else {
return nil
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func getMediumFromAsset(asset: PHAsset) -> [String: Any?] {
let filename = self.extractFilenameFromAsset(asset: asset)
2023-07-26 22:56:30 +08:00
let mimeType = self.extractMimeTypeFromAsset(asset: asset)
let resource = self.extractResourceFromAsset(asset: asset)
let size = self.extractSizeFromResource(resource: resource)
let orientation = self.toOrientationValue(orientation: asset.value(forKey: "orientation") as? UIImage.Orientation)
2020-08-14 18:07:27 +08:00
return [
"id": asset.localIdentifier,
"filename": filename,
"title": self.extractTitleFromFilename(filename: filename),
2020-08-14 18:07:27 +08:00
"mediumType": toDartMediumType(value: asset.mediaType),
2021-01-10 21:43:18 +08:00
"mimeType": mimeType,
2020-08-14 18:07:27 +08:00
"height": asset.pixelHeight,
"width": asset.pixelWidth,
2023-05-14 02:23:53 +08:00
"size": size,
"orientation": orientation,
2020-08-18 16:12:06 +08:00
"duration": NSInteger(asset.duration * 1000),
"creationDate": (asset.creationDate != nil) ? NSInteger(asset.creationDate!.timeIntervalSince1970 * 1000) : nil,
"modifiedDate": (asset.modificationDate != nil) ? NSInteger(asset.modificationDate!.timeIntervalSince1970 * 1000) : nil
2020-08-14 18:07:27 +08:00
]
}
2023-07-27 20:28:38 +08:00
2023-06-25 22:41:36 -07:00
private func getMediumFromAssetLightWeight(asset: PHAsset) -> [String: Any?] {
return [
"id": asset.localIdentifier,
"mediumType": toDartMediumType(value: asset.mediaType),
"height": asset.pixelHeight,
"width": asset.pixelWidth,
"duration": NSInteger(asset.duration * 1000),
"creationDate": (asset.creationDate != nil) ? NSInteger(asset.creationDate!.timeIntervalSince1970 * 1000) : nil,
"modifiedDate": (asset.modificationDate != nil) ? NSInteger(asset.modificationDate!.timeIntervalSince1970 * 1000) : nil
]
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func exportPathForAsset(asset: PHAsset, ext: String) -> URL {
let mediumId = asset.localIdentifier
.replacingOccurrences(of: "/", with: "__")
.replacingOccurrences(of: "\\", with: "__")
2021-01-10 21:09:25 +08:00
let cachePath = self.cachePath()
return cachePath.appendingPathComponent(mediumId + ext)
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func toSwiftMediumType(value: String) -> PHAssetMediaType? {
switch value {
case "image": return PHAssetMediaType.image
case "video": return PHAssetMediaType.video
case "audio": return PHAssetMediaType.audio
default: return nil
}
}
2023-07-27 20:28:38 +08:00
2020-08-14 18:07:27 +08:00
private func toDartMediumType(value: PHAssetMediaType) -> String? {
switch value {
case PHAssetMediaType.image: return "image"
case PHAssetMediaType.video: return "video"
case PHAssetMediaType.audio: return "audio"
default: return nil
}
}
2023-07-27 20:28:38 +08:00
private func toOrientationValue(orientation: UIImage.Orientation?) -> Int {
guard let orientation = orientation else {
return 0
}
switch orientation {
2023-07-27 20:28:38 +08:00
case UIImage.Orientation.up:
return 1
case UIImage.Orientation.down:
return 3
case UIImage.Orientation.left:
return 6
case UIImage.Orientation.right:
return 8
case UIImage.Orientation.upMirrored:
return 2
case UIImage.Orientation.downMirrored:
return 4
case UIImage.Orientation.leftMirrored:
return 5
case UIImage.Orientation.rightMirrored:
return 7
@unknown default:
return 0
}
}
2023-07-27 20:28:38 +08:00
private func predicateFromMediumType(mediumType: String?) -> NSPredicate? {
guard let type = mediumType else {
return nil
2020-08-14 18:07:27 +08:00
}
guard let swiftType = toSwiftMediumType(value: type) else {
return nil
}
return NSPredicate(format: "mediaType = %d", swiftType.rawValue)
2020-08-14 18:07:27 +08:00
}
2023-07-27 20:28:38 +08:00
2021-01-10 17:34:37 +08:00
private func extractFileExtensionFromUTI(uti: String?) -> String {
guard let assetUTI = uti else {
2021-01-10 14:57:40 +08:00
return ""
}
2023-07-27 20:28:38 +08:00
guard let ext = UTTypeCopyPreferredTagWithClass(
assetUTI as CFString,
kUTTagClassFilenameExtension as CFString
)?.takeRetainedValue() as String? else {
2021-01-10 17:34:37 +08:00
return ""
}
return "." + ext
}
2023-07-27 20:28:38 +08:00
2021-01-10 21:43:18 +08:00
private func extractMimeTypeFromUTI(uti: String?) -> String? {
guard let assetUTI = uti else {
return nil
}
2023-07-27 20:28:38 +08:00
guard let mimeType = UTTypeCopyPreferredTagWithClass(
assetUTI as CFString,
kUTTagClassMIMEType as CFString
)?.takeRetainedValue() as String? else {
2021-01-10 21:43:18 +08:00
return nil
}
return mimeType
}
2023-07-27 20:28:38 +08:00
2021-01-10 17:34:37 +08:00
private func extractFileExtensionFromAsset(asset: PHAsset) -> String {
2023-07-26 22:56:30 +08:00
let uti = asset.value(forKey: "uniformTypeIdentifier") as? String
2021-01-10 17:34:37 +08:00
return self.extractFileExtensionFromUTI(uti: uti)
2021-01-10 14:57:40 +08:00
}
2023-07-27 20:28:38 +08:00
2021-01-10 21:43:18 +08:00
private func extractMimeTypeFromAsset(asset: PHAsset) -> String? {
2023-07-26 22:56:30 +08:00
let uti = asset.value(forKey: "uniformTypeIdentifier") as? String
2021-01-10 21:43:18 +08:00
return self.extractMimeTypeFromUTI(uti: uti)
}
2023-07-27 20:28:38 +08:00
private func extractFilenameFromAsset(asset: PHAsset) -> String? {
2023-07-26 22:56:30 +08:00
return asset.value(forKey: "originalFilename") as? String
}
2023-07-27 20:28:38 +08:00
2023-07-26 22:56:30 +08:00
private func extractTitleFromFilename(filename: String?) -> String? {
if let name = filename {
return (name as NSString).deletingPathExtension
}
return nil
}
private func extractResourceFromAsset(asset: PHAsset) -> PHAssetResource? {
if #available(iOS 9, *) {
let resourceList = PHAssetResource.assetResources(for: asset)
if let resource = resourceList.first {
return resource
2023-07-24 00:40:57 +08:00
}
2023-05-14 02:23:53 +08:00
}
2023-07-26 22:56:30 +08:00
return nil
2023-05-06 00:06:55 +08:00
}
2023-07-27 20:28:38 +08:00
2023-07-26 22:56:30 +08:00
private func extractSizeFromResource(resource: PHAssetResource?) -> Int64? {
if let assetResource = resource {
return assetResource.value(forKey: "fileSize") as? Int64
}
return nil
}
2023-07-27 20:28:38 +08:00
2021-01-10 21:09:25 +08:00
private func cachePath() -> URL {
let paths = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
let cacheFolder = paths[0].appendingPathComponent("photo_gallery")
try! FileManager.default.createDirectory(at: cacheFolder, withIntermediateDirectories: true, attributes: nil)
return cacheFolder
}
2023-07-27 20:28:38 +08:00
2023-07-24 00:42:07 +08:00
private func deleteMedium(mediumId: String, completion: @escaping (Bool, Error?) -> Void) {
let fetchOptions = PHFetchOptions()
if #available(iOS 9, *) {
fetchOptions.fetchLimit = 1
}
let assets: PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [mediumId], options: fetchOptions)
2023-07-27 20:28:38 +08:00
2023-07-24 00:42:07 +08:00
if assets.count <= 0 {
completion(false, NSError(domain: "photo_gallery", code: 404, userInfo: nil))
} else {
let asset: PHAsset = assets[0]
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets([asset] as NSFastEnumeration)
}, completionHandler: completion)
}
}
2023-07-27 20:28:38 +08:00
2021-01-10 21:09:25 +08:00
private func cleanCache() {
try? FileManager.default.removeItem(at: self.cachePath())
}
2020-08-14 18:07:27 +08:00
}