optimize code

This commit is contained in:
Wenqi Li 2023-05-14 02:23:53 +08:00
parent eafedcb225
commit 4a2b9dcf1c
3 changed files with 180 additions and 106 deletions

View File

@ -10,7 +10,6 @@ import android.graphics.ImageDecoder
import android.os.Build
import android.provider.MediaStore
import android.util.Size
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
@ -81,18 +80,18 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "photo_gallery")
val plugin = PhotoGalleryPlugin()
plugin.context = flutterPluginBinding.applicationContext
channel.setMethodCallHandler(plugin)
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"listAlbums" -> {
val mediumType = call.argument<String>("mediumType")
@ -102,6 +101,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"listMedia" -> {
val albumId = call.argument<String>("albumId")
val mediumType = call.argument<String>("mediumType")
@ -114,6 +114,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"getMedium" -> {
val mediumId = call.argument<String>("mediumId")
val mediumType = call.argument<String>("mediumType")
@ -123,6 +124,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"getThumbnail" -> {
val mediumId = call.argument<String>("mediumId")
val mediumType = call.argument<String>("mediumType")
@ -135,6 +137,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"getAlbumThumbnail" -> {
val albumId = call.argument<String>("albumId")
val mediumType = call.argument<String>("mediumType")
@ -148,6 +151,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"getFile" -> {
val mediumId = call.argument<String>("mediumId")
val mediumType = call.argument<String>("mediumType")
@ -158,6 +162,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
"cleanCache" -> {
executor.submit {
result.success(
@ -165,6 +170,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
)
}
}
else -> result.notImplemented()
}
}
@ -174,9 +180,11 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
imageType -> {
listImageAlbums().values.toList()
}
videoType -> {
listVideoAlbums().values.toList()
}
else -> {
listAllAlbums().values.toList()
}
@ -224,13 +232,10 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
val albumLinkedMap = linkedMapOf<String, Map<String, Any>>()
albumLinkedMap.put(
allAlbumId,
hashMapOf(
"id" to allAlbumId,
"name" to allAlbumName,
"count" to total
)
albumLinkedMap[allAlbumId] = hashMapOf(
"id" to allAlbumId,
"name" to allAlbumName,
"count" to total
)
albumLinkedMap.putAll(albumHashMap)
return albumLinkedMap
@ -278,13 +283,10 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
val albumLinkedMap = linkedMapOf<String, Map<String, Any>>()
albumLinkedMap.put(
allAlbumId,
hashMapOf(
"id" to allAlbumId,
"name" to allAlbumName,
"count" to total
)
albumLinkedMap[allAlbumId] = hashMapOf(
"id" to allAlbumId,
"name" to allAlbumName,
"count" to total
)
albumLinkedMap.putAll(albumHashMap)
return albumLinkedMap
@ -304,18 +306,28 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
return albumMap
}
private fun listMedia(mediumType: String?, albumId: String, newest: Boolean, skip: Int?, take: Int?): Map<String, Any?> {
private fun listMedia(
mediumType: String?,
albumId: String,
newest: Boolean,
skip: Int?,
take: Int?
): Map<String, Any?> {
return when (mediumType) {
imageType -> {
listImages(albumId, newest, skip, take)
}
videoType -> {
listVideos(albumId, newest, skip, take)
}
else -> {
val images = listImages(albumId, newest, null, null)["items"] as List<Map<String, Any?>>
val videos = listVideos(albumId, newest, null, null)["items"] as List<Map<String, Any?>>
var items = (images + videos).sortedWith(compareBy<Map<String, Any?>> { it["creationDate"] as Long }.thenBy { it["modifiedDate"] as Long })
val comparator = compareBy<Map<String, Any?>> { it["creationDate"] as Long }
.thenBy { it["modifiedDate"] as Long }
var items = (images + videos).sortedWith(comparator)
if (newest) {
items = items.reversed()
}
@ -452,9 +464,11 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
imageType -> {
getImageMedia(mediumId)
}
videoType -> {
getVideoMedia(mediumId)
}
else -> {
getImageMedia(mediumId) ?: getVideoMedia(mediumId)
}
@ -505,14 +519,22 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
return videoMetadata
}
private fun getThumbnail(mediumId: String, mediumType: String?, width: Int?, height: Int?, highQuality: Boolean?): ByteArray? {
private fun getThumbnail(
mediumId: String,
mediumType: String?,
width: Int?,
height: Int?,
highQuality: Boolean?
): ByteArray? {
return when (mediumType) {
imageType -> {
getImageThumbnail(mediumId, width, height, highQuality)
}
videoType -> {
getVideoThumbnail(mediumId, width, height, highQuality)
}
else -> {
getImageThumbnail(mediumId, width, height, highQuality)
?: getVideoThumbnail(mediumId, width, height, highQuality)
@ -537,7 +559,9 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
null
}
} else {
val kind = if (highQuality == true) MediaStore.Images.Thumbnails.MINI_KIND else MediaStore.Images.Thumbnails.MICRO_KIND
val kind =
if (highQuality == true) MediaStore.Images.Thumbnails.MINI_KIND
else MediaStore.Images.Thumbnails.MICRO_KIND
MediaStore.Images.Thumbnails.getThumbnail(
this.contentResolver, mediumId.toLong(),
kind, null
@ -572,7 +596,8 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
} else {
val kind =
if (highQuality == true) MediaStore.Video.Thumbnails.MINI_KIND else MediaStore.Video.Thumbnails.MICRO_KIND
if (highQuality == true) MediaStore.Video.Thumbnails.MINI_KIND
else MediaStore.Video.Thumbnails.MICRO_KIND
MediaStore.Video.Thumbnails.getThumbnail(
this.contentResolver, mediumId.toLong(),
kind, null
@ -589,21 +614,36 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
return byteArray
}
private fun getAlbumThumbnail(albumId: String, mediumType: String?, newest: Boolean, width: Int?, height: Int?, highQuality: Boolean?): ByteArray? {
private fun getAlbumThumbnail(
albumId: String,
mediumType: String?,
newest: Boolean,
width: Int?,
height: Int?,
highQuality: Boolean?
): ByteArray? {
return when (mediumType) {
imageType -> {
getImageAlbumThumbnail(albumId, newest, width, height, highQuality)
}
videoType -> {
getVideoAlbumThumbnail(albumId, newest, width, height, highQuality)
}
else -> {
getAllAlbumThumbnail(albumId, newest, width, height, highQuality)
}
}
}
private fun getImageAlbumThumbnail(albumId: String, newest: Boolean, width: Int?, height: Int?, highQuality: Boolean?): ByteArray? {
private fun getImageAlbumThumbnail(
albumId: String,
newest: Boolean,
width: Int?,
height: Int?,
highQuality: Boolean?
): ByteArray? {
return this.context.run {
val projection = arrayOf(MediaStore.Images.Media._ID)
@ -621,7 +661,13 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
}
private fun getVideoAlbumThumbnail(albumId: String, newest: Boolean, width: Int?, height: Int?, highQuality: Boolean?): ByteArray? {
private fun getVideoAlbumThumbnail(
albumId: String,
newest: Boolean,
width: Int?,
height: Int?,
highQuality: Boolean?
): ByteArray? {
return this.context.run {
val projection = arrayOf(MediaStore.Video.Media._ID)
@ -639,7 +685,13 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
}
private fun getAllAlbumThumbnail(albumId: String, newest: Boolean, width: Int?, height: Int?, highQuality: Boolean?): ByteArray? {
private fun getAllAlbumThumbnail(
albumId: String,
newest: Boolean,
width: Int?,
height: Int?,
highQuality: Boolean?
): ByteArray? {
return this.context.run {
val imageProjection = arrayOf(
MediaStore.Images.Media._ID,
@ -688,20 +740,16 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
if (imageId != null && videoId != null) {
if (imageDateAdded != null && videoDateAdded != null) {
if (newest && imageDateAdded!! < videoDateAdded!! || !newest && imageDateAdded!! > videoDateAdded!!) {
return@run getVideoThumbnail(videoId.toString(), width, height, highQuality)
} else {
return@run getImageThumbnail(imageId.toString(), width, height, highQuality)
}
if (newest && imageDateAdded!! > videoDateAdded!! || !newest && imageDateAdded!! < videoDateAdded!!) {
return@run getImageThumbnail(imageId.toString(), width, height, highQuality)
}
if (imageDateModified != null && videoDateModified != null) {
if (newest && imageDateModified!! < videoDateModified!! || !newest && imageDateModified!! > videoDateModified!!) {
return@run getVideoThumbnail(videoId.toString(), width, height, highQuality)
} else {
return@run getImageThumbnail(imageId.toString(), width, height, highQuality)
}
if (newest && imageDateAdded!! < videoDateAdded!! || !newest && imageDateAdded!! > videoDateAdded!!) {
return@run getVideoThumbnail(videoId.toString(), width, height, highQuality)
}
if (newest && imageDateModified!! >= videoDateModified!! || !newest && imageDateModified!! <= videoDateModified!!) {
return@run getImageThumbnail(imageId.toString(), width, height, highQuality)
}
return@run getVideoThumbnail(videoId.toString(), width, height, highQuality)
}
if (imageId != null) {
@ -805,9 +853,11 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
imageType -> {
getImageFile(mediumId, mimeType = mimeType)
}
videoType -> {
getVideoFile(mediumId)
}
else -> {
getImageFile(mediumId, mimeType = mimeType) ?: getVideoFile(mediumId)
}
@ -874,10 +924,12 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
val bitmap: Bitmap? = this.context.run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
ImageDecoder.decodeBitmap(ImageDecoder.createSource(
this.contentResolver,
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediumId.toLong())
))
ImageDecoder.decodeBitmap(
ImageDecoder.createSource(
this.contentResolver,
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediumId.toLong())
)
)
} catch (e: Exception) {
null
}
@ -891,24 +943,39 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
bitmap?.let {
val compressFormat: Bitmap.CompressFormat
if (mimeType == "image/jpeg") {
val path = File(getCachePath(), "$mediumId.jpeg")
val out = FileOutputStream(path)
compressFormat = Bitmap.CompressFormat.JPEG
it.compress(compressFormat, 100, out)
return path.absolutePath
} else if (mimeType == "image/png") {
val path = File(getCachePath(), "$mediumId.png")
val out = FileOutputStream(path)
compressFormat = Bitmap.CompressFormat.PNG
it.compress(compressFormat, 100, out)
return path.absolutePath
} else if (mimeType == "image/webp") {
val path = File(getCachePath(), "$mediumId.webp")
val out = FileOutputStream(path)
compressFormat = Bitmap.CompressFormat.WEBP_LOSSLESS
it.compress(compressFormat, 100, out)
return path.absolutePath
when (mimeType) {
"image/jpeg" -> {
val path = File(getCachePath(), "$mediumId.jpeg")
val out = FileOutputStream(path)
compressFormat = Bitmap.CompressFormat.JPEG
it.compress(compressFormat, 100, out)
return path.absolutePath
}
"image/png" -> {
val path = File(getCachePath(), "$mediumId.png")
val out = FileOutputStream(path)
compressFormat = Bitmap.CompressFormat.PNG
it.compress(compressFormat, 100, out)
return path.absolutePath
}
"image/webp" -> {
val path = File(getCachePath(), "$mediumId.webp")
val out = FileOutputStream(path)
compressFormat = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Bitmap.CompressFormat.WEBP_LOSSLESS
}
else {
Bitmap.CompressFormat.WEBP
}
it.compress(compressFormat, 100, out)
return path.absolutePath
}
else -> {
return null
}
}
}
@ -1013,7 +1080,7 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
}
}
private fun getCachePath(): File? {
private fun getCachePath(): File {
return this.context.run {
val cachePath = File(this.cacheDir, "photo_gallery")
if (!cachePath.exists()) {
@ -1025,6 +1092,6 @@ class PhotoGalleryPlugin : FlutterPlugin, MethodCallHandler {
private fun cleanCache() {
val cachePath = getCachePath()
cachePath?.deleteRecursively()
cachePath.deleteRecursively()
}
}

View File

@ -83,8 +83,8 @@ class _MyAppState extends State<MyApp> {
...?_albums?.map(
(album) => GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AlbumPage(album))),
MaterialPageRoute(builder: (context) => AlbumPage(album)),
),
child: Column(
children: <Widget>[
ClipRRect(
@ -95,8 +95,7 @@ class _MyAppState extends State<MyApp> {
width: gridWidth,
child: FadeInImage(
fit: BoxFit.cover,
placeholder:
MemoryImage(kTransparentImage),
placeholder: MemoryImage(kTransparentImage),
image: AlbumThumbnailProvider(
album: album,
highQuality: true,
@ -186,8 +185,9 @@ class AlbumPageState extends State<AlbumPage> {
children: <Widget>[
...?_media?.map(
(medium) => GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ViewerPage(medium))),
onTap: () => Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ViewerPage(medium)),
),
child: Container(
color: Colors.grey[300],
child: FadeInImage(
@ -293,9 +293,7 @@ class _VideoProviderState extends State<VideoProvider> {
TextButton(
onPressed: () {
setState(() {
_controller!.value.isPlaying
? _controller!.pause()
: _controller!.play();
_controller!.value.isPlaying ? _controller!.pause() : _controller!.play();
});
},
child: Icon(

View File

@ -34,7 +34,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
mediumId: mediumId,
completion: { (data: [String: Any?]?, error: Error?) -> Void in
result(data)
})
}
)
}
else if(call.method == "getThumbnail") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
@ -49,7 +50,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
highQuality: highQuality,
completion: { (data: Data?, error: Error?) -> Void in
result(data)
})
}
)
}
else if(call.method == "getAlbumThumbnail") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
@ -68,7 +70,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
highQuality: highQuality,
completion: { (data: Data?, error: Error?) -> Void in
result(data)
})
}
)
}
else if(call.method == "getFile") {
let arguments = call.arguments as! Dictionary<String, AnyObject>
@ -79,7 +82,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
mimeType: mimeType,
completion: { (filepath: String?, error: Error?) -> Void in
result(filepath?.replacingOccurrences(of: "file://", with: ""))
})
}
)
}
else if(call.method == "cleanCache") {
cleanCache()
@ -254,7 +258,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
let bytes = image.jpegData(compressionQuality: CGFloat(70))
completion(bytes, nil)
})
}
)
return
}
@ -312,7 +317,8 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
let bytes = image.jpegData(compressionQuality: CGFloat(80))
completion(bytes, nil)
})
}
)
return
}
@ -366,25 +372,28 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
)
} else if(asset.mediaType == PHAssetMediaType.video
|| asset.mediaType == PHAssetMediaType.audio) {
|| asset.mediaType == PHAssetMediaType.audio) {
let options = PHVideoRequestOptions()
options.version = .current
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
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))
}
})
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))
}
})
}
)
}
@ -406,11 +415,11 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
return nil
}
}
private func getMediumFromAsset(asset: PHAsset) -> [String: Any?] {
let mimeType = self.extractMimeTypeFromAsset(asset: asset)
let filename = self.extractFilenameFromAsset(asset: asset)
let size = self.extractSizeFromAsset(asset: asset)
let size = self.extractSizeFromAsset(asset: asset)
return [
"id": asset.localIdentifier,
"filename": filename,
@ -419,7 +428,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
"mimeType": mimeType,
"height": asset.pixelHeight,
"width": asset.pixelWidth,
"size": size,
"size": size,
"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
@ -429,7 +438,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
private func getMediumFromAssetAsync(asset: PHAsset, completion: @escaping ([String : Any?]?, Error?) -> Void) -> Void {
let mimeType = self.extractMimeTypeFromAsset(asset: asset)
let filename = self.extractFilenameFromAsset(asset: asset)
let size = self.extractSizeFromAsset(asset: asset)
let size = self.extractSizeFromAsset(asset: asset)
let manager = PHImageManager.default()
manager.requestImageData(
for: asset,
@ -443,7 +452,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
"mimeType": mimeType,
"height": asset.pixelHeight,
"width": asset.pixelWidth,
"size": size,
"size": size,
"orientation": self.toOrientationValue(orientation: orientation),
"duration": NSInteger(asset.duration * 1000),
"creationDate": (asset.creationDate != nil) ? NSInteger(asset.creationDate!.timeIntervalSince1970 * 1000) : nil,
@ -452,7 +461,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
)
}
private func exportPathForAsset(asset: PHAsset, ext: String) -> URL {
let mediumId = asset.localIdentifier
.replacingOccurrences(of: "/", with: "__")
@ -511,7 +520,7 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
return NSPredicate(format: "mediaType = %d", swiftType.rawValue)
}
private func extractFileExtensionFromUTI(uti: String?) -> String {
guard let assetUTI = uti else {
return ""
@ -561,14 +570,14 @@ public class SwiftPhotoGalleryPlugin: NSObject, FlutterPlugin {
}
return asset.value(forKey: "filename") as? String
}
private func extractSizeFromAsset(asset: PHAsset) -> Int64? {
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return nil
}
return Int64(bitPattern: UInt64(unsignedInt64))
let resources = PHAssetResource.assetResources(for: asset)
guard let resource = resources.first,
let unsignedInt64 = resource.value(forKey: "fileSize") as? CLong else {
return nil
}
return Int64(bitPattern: UInt64(unsignedInt64))
}
private func extractTitleFromFilename(filename: String?) -> String? {