mirror of
https://github.com/immich-app/immich.git
synced 2025-07-31 15:08:44 -04:00
rebase
This commit is contained in:
parent
e1cc8f8fe1
commit
8d163ec932
@ -15,9 +15,6 @@ import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
private object ThumbnailsPigeonUtils {
|
||||
|
||||
fun createConnectionError(channelName: String): FlutterError {
|
||||
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
|
||||
|
||||
fun wrapResult(result: Any?): List<Any?> {
|
||||
return listOf(result)
|
||||
}
|
||||
@ -98,30 +95,3 @@ interface ThumbnailApi {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
|
||||
class PlatformThumbnailApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
|
||||
companion object {
|
||||
/** The codec used by PlatformThumbnailApi. */
|
||||
val codec: MessageCodec<Any?> by lazy {
|
||||
ThumbnailsPigeonCodec()
|
||||
}
|
||||
}
|
||||
fun getThumbnail(assetIdArg: String, widthArg: Long, heightArg: Long, callback: (Result<ByteArray?>) -> Unit)
|
||||
{
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
val channelName = "dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail$separatedMessageChannelSuffix"
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
|
||||
channel.send(listOf(assetIdArg, widthArg, heightArg)) {
|
||||
if (it is List<*>) {
|
||||
if (it.size > 1) {
|
||||
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
|
||||
} else {
|
||||
val output = it[0] as ByteArray?
|
||||
callback(Result.success(output))
|
||||
}
|
||||
} else {
|
||||
callback(Result.failure(ThumbnailsPigeonUtils.createConnectionError(channelName)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,10 +37,6 @@ private func wrapError(_ error: Any) -> [Any?] {
|
||||
]
|
||||
}
|
||||
|
||||
private func createConnectionError(withChannelName channelName: String) -> PigeonError {
|
||||
return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
|
||||
}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
@ -104,37 +100,3 @@ class ThumbnailApiSetup {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
|
||||
protocol PlatformThumbnailApiProtocol {
|
||||
func getThumbnail(assetId assetIdArg: String, width widthArg: Int64, height heightArg: Int64, completion: @escaping (Result<FlutterStandardTypedData?, PigeonError>) -> Void)
|
||||
}
|
||||
class PlatformThumbnailApi: PlatformThumbnailApiProtocol {
|
||||
private let binaryMessenger: FlutterBinaryMessenger
|
||||
private let messageChannelSuffix: String
|
||||
init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") {
|
||||
self.binaryMessenger = binaryMessenger
|
||||
self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
|
||||
}
|
||||
var codec: ThumbnailsPigeonCodec {
|
||||
return ThumbnailsPigeonCodec.shared
|
||||
}
|
||||
func getThumbnail(assetId assetIdArg: String, width widthArg: Int64, height heightArg: Int64, completion: @escaping (Result<FlutterStandardTypedData?, PigeonError>) -> Void) {
|
||||
let channelName: String = "dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail\(messageChannelSuffix)"
|
||||
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
|
||||
channel.sendMessage([assetIdArg, widthArg, heightArg] as [Any?]) { response in
|
||||
guard let listResponse = response as? [Any?] else {
|
||||
completion(.failure(createConnectionError(withChannelName: channelName)))
|
||||
return
|
||||
}
|
||||
if listResponse.count > 1 {
|
||||
let code: String = listResponse[0] as! String
|
||||
let message: String? = nilOrValue(listResponse[1])
|
||||
let details: String? = nilOrValue(listResponse[2])
|
||||
completion(.failure(PigeonError(code: code, message: message, details: details)))
|
||||
} else {
|
||||
let result: FlutterStandardTypedData? = nilOrValue(listResponse[0])
|
||||
completion(.success(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,23 @@
|
||||
import CryptoKit
|
||||
import Flutter
|
||||
import Photos
|
||||
import MobileCoreServices
|
||||
import Photos
|
||||
|
||||
// https://stackoverflow.com/a/55839062
|
||||
extension UIImage {
|
||||
func toData (options: NSDictionary?, type: ImageType) -> Data? {
|
||||
func toData(options: NSDictionary?, type: ImageType) -> Data? {
|
||||
guard cgImage != nil else { return nil }
|
||||
return toData(options: options, type: type.value)
|
||||
}
|
||||
|
||||
// about properties: https://developer.apple.com/documentation/imageio/1464962-cgimagedestinationaddimage
|
||||
func toData (options: NSDictionary?, type: CFString) -> Data? {
|
||||
func toData(options: NSDictionary?, type: CFString) -> Data? {
|
||||
guard let cgImage = cgImage else { return nil }
|
||||
return autoreleasepool { () -> Data? in
|
||||
let data = NSMutableData()
|
||||
guard let imageDestination = CGImageDestinationCreateWithData(data as CFMutableData, type, 1, nil) else { return nil }
|
||||
guard
|
||||
let imageDestination = CGImageDestinationCreateWithData(data as CFMutableData, type, 1, nil)
|
||||
else { return nil }
|
||||
CGImageDestinationAddImage(imageDestination, cgImage, options)
|
||||
CGImageDestinationFinalize(imageDestination)
|
||||
return data as Data
|
||||
@ -75,41 +77,10 @@ class ThumbnailApiImpl: ThumbnailApi {
|
||||
requestOptions.version = .current
|
||||
return requestOptions
|
||||
}()
|
||||
private static let processingQueue = DispatchQueue(label: "thumbnail.processing", qos: .userInteractive, attributes: .concurrent)
|
||||
private static let imageCache = NSCache<NSString, FlutterStandardTypedData>()
|
||||
private static let processingQueue = DispatchQueue(
|
||||
label: "thumbnail.processing", qos: .userInteractive, attributes: .concurrent)
|
||||
|
||||
func requestThumbnail(
|
||||
assetId: String,
|
||||
width: Int64,
|
||||
height: Int64,
|
||||
completion: @escaping (Result<Int32, Error>) -> Void
|
||||
) {
|
||||
Self.processingQueue.async {
|
||||
do {
|
||||
let asset = try self.getAsset(assetId: assetId)
|
||||
|
||||
let requestId = Self.cacheManager.requestImage(
|
||||
for: asset,
|
||||
targetSize: CGSize(width: Double(width), height: Double(height)),
|
||||
contentMode: .aspectFill,
|
||||
options: Self.requestOptions,
|
||||
resultHandler: { (image, info) -> Void in
|
||||
guard let data = image?.toData(options: nil, type: .bmp) else { return }
|
||||
Self.imageCache.setObject(FlutterStandardTypedData(bytes: data), forKey: assetId as NSString)
|
||||
}
|
||||
)
|
||||
completion(.success(requestId))
|
||||
} catch {
|
||||
completion(.failure(PigeonError(code: "", message: "Could not get asset data", details: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getThumbnail(assetId assetIdArg: String, width widthArg: Int64, height heightArg: Int64, completion: @escaping (Result<FlutterStandardTypedData?, PigeonError>) -> Void) {
|
||||
|
||||
}
|
||||
|
||||
func sendThumbnail(
|
||||
func getThumbnail(
|
||||
assetId: String,
|
||||
width: Int64,
|
||||
height: Int64,
|
||||
@ -130,15 +101,12 @@ class ThumbnailApiImpl: ThumbnailApi {
|
||||
}
|
||||
)
|
||||
} catch {
|
||||
completion(.failure(PigeonError(code: "", message: "Could not get asset data", details: nil)))
|
||||
completion(
|
||||
.failure(PigeonError(code: "", message: "Could not get asset data", details: nil)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel(requestId: Int32) {
|
||||
Self.cacheManager.cancelImageRequest(requestId as PHImageRequestID)
|
||||
}
|
||||
|
||||
private func getAsset(assetId: String) throws -> PHAsset {
|
||||
guard
|
||||
let asset = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: Self.fetchOptions)
|
||||
@ -148,4 +116,9 @@ class ThumbnailApiImpl: ThumbnailApi {
|
||||
}
|
||||
return asset
|
||||
}
|
||||
|
||||
// func cancel(requestId: Int32) {
|
||||
// Self.cacheManager.cancelImageRequest(requestId as PHImageRequestID)
|
||||
// }
|
||||
|
||||
}
|
||||
|
62
mobile/lib/platform/thumbnail_api.g.dart
generated
62
mobile/lib/platform/thumbnail_api.g.dart
generated
@ -15,17 +15,6 @@ PlatformException _createConnectionError(String channelName) {
|
||||
);
|
||||
}
|
||||
|
||||
List<Object?> wrapResponse(
|
||||
{Object? result, PlatformException? error, bool empty = false}) {
|
||||
if (empty) {
|
||||
return <Object?>[];
|
||||
}
|
||||
if (error == null) {
|
||||
return <Object?>[result];
|
||||
}
|
||||
return <Object?>[error.code, error.message, error.details];
|
||||
}
|
||||
|
||||
class _PigeonCodec extends StandardMessageCodec {
|
||||
const _PigeonCodec();
|
||||
@override
|
||||
@ -97,54 +86,3 @@ class ThumbnailApi {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PlatformThumbnailApi {
|
||||
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||
|
||||
Future<Uint8List?> getThumbnail(String assetId, int width, int height);
|
||||
|
||||
static void setUp(
|
||||
PlatformThumbnailApi? api, {
|
||||
BinaryMessenger? binaryMessenger,
|
||||
String messageChannelSuffix = '',
|
||||
}) {
|
||||
messageChannelSuffix =
|
||||
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||
{
|
||||
final BasicMessageChannel<
|
||||
Object?> pigeonVar_channel = BasicMessageChannel<
|
||||
Object?>(
|
||||
'dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail$messageChannelSuffix',
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
pigeonVar_channel.setMessageHandler(null);
|
||||
} else {
|
||||
pigeonVar_channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_assetId = (args[0] as String?);
|
||||
assert(arg_assetId != null,
|
||||
'Argument for dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail was null, expected non-null String.');
|
||||
final int? arg_width = (args[1] as int?);
|
||||
assert(arg_width != null,
|
||||
'Argument for dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail was null, expected non-null int.');
|
||||
final int? arg_height = (args[2] as int?);
|
||||
assert(arg_height != null,
|
||||
'Argument for dev.flutter.pigeon.immich_mobile.PlatformThumbnailApi.getThumbnail was null, expected non-null int.');
|
||||
try {
|
||||
final Uint8List? output =
|
||||
await api.getThumbnail(arg_assetId!, arg_width!, arg_height!);
|
||||
return wrapResponse(result: output);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class LocalAlbumThumbnail extends ConsumerWidget {
|
||||
|
||||
return ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
child: Thumbnail(asset: data),
|
||||
child: Thumbnail.fromBaseAsset(asset: data),
|
||||
);
|
||||
},
|
||||
error: (error, stack) {
|
||||
|
@ -30,8 +30,8 @@ abstract class ThumbnailApi {
|
||||
// });
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class PlatformThumbnailApi {
|
||||
@async
|
||||
Uint8List? getThumbnail(String assetId, int width, int height);
|
||||
}
|
||||
// @FlutterApi()
|
||||
// abstract class PlatformThumbnailApi {
|
||||
// @async
|
||||
// Uint8List? getThumbnail(String assetId, int width, int height);
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user