resolve merge conflicts

use updated approach for calculating checksums
This commit is contained in:
Peter Ombodi 2025-09-18 17:27:35 +03:00
parent bd9e4871ec
commit 42f99e8039
8 changed files with 364 additions and 116 deletions

View file

@ -89,7 +89,8 @@ data class PlatformAsset (
val height: Long? = null,
val durationInSeconds: Long,
val orientation: Long,
val isFavorite: Boolean
val isFavorite: Boolean,
val size: Long? = null
)
{
companion object {
@ -104,7 +105,8 @@ data class PlatformAsset (
val durationInSeconds = pigeonVar_list[7] as Long
val orientation = pigeonVar_list[8] as Long
val isFavorite = pigeonVar_list[9] as Boolean
return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite)
val size = pigeonVar_list[10] as Long?
return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite, size)
}
}
fun toList(): List<Any?> {
@ -119,6 +121,7 @@ data class PlatformAsset (
durationInSeconds,
orientation,
isFavorite,
size,
)
}
override fun equals(other: Any?): Boolean {
@ -243,6 +246,40 @@ data class HashResult (
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
data class TrashedAssetParams (
val id: String,
val type: Long,
val albumId: String? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): TrashedAssetParams {
val id = pigeonVar_list[0] as String
val type = pigeonVar_list[1] as Long
val albumId = pigeonVar_list[2] as String?
return TrashedAssetParams(id, type, albumId)
}
}
fun toList(): List<Any?> {
return listOf(
id,
type,
albumId,
)
}
override fun equals(other: Any?): Boolean {
if (other !is TrashedAssetParams) {
return false
}
if (this === other) {
return true
}
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
@ -266,6 +303,11 @@ private open class MessagesPigeonCodec : StandardMessageCodec() {
HashResult.fromList(it)
}
}
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TrashedAssetParams.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@ -287,6 +329,10 @@ private open class MessagesPigeonCodec : StandardMessageCodec() {
stream.write(132)
writeValue(stream, value.toList())
}
is TrashedAssetParams -> {
stream.write(133)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@ -305,6 +351,8 @@ interface NativeSyncApi {
fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
fun hashAssets(assetIds: List<String>, allowNetworkAccess: Boolean, callback: (Result<List<HashResult>>) -> Unit)
fun cancelHashing()
fun getTrashedAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List<PlatformAsset>
fun hashTrashedAssets(trashedAssets: List<TrashedAssetParams>, callback: (Result<List<HashResult>>) -> Unit)
companion object {
/** The codec used by NativeSyncApi. */
@ -483,6 +531,44 @@ interface NativeSyncApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum$separatedMessageChannelSuffix", codec, taskQueue)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val albumIdArg = args[0] as String
val updatedTimeCondArg = args[1] as Long?
val wrapped: List<Any?> = try {
listOf(api.getTrashedAssetsForAlbum(albumIdArg, updatedTimeCondArg))
} catch (exception: Throwable) {
MessagesPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets$separatedMessageChannelSuffix", codec, taskQueue)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val trashedAssetsArg = args[0] as List<TrashedAssetParams>
api.hashTrashedAssets(trashedAssetsArg) { result: Result<List<HashResult>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}

View file

@ -29,7 +29,10 @@ class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), Na
throw IllegalStateException("Method not supported on this Android version.")
}
override fun hashTrashedAssets(trashedAssets: List<TrashedAssetParams>): List<ByteArray?> {
override fun hashTrashedAssets(
trashedAssets: List<TrashedAssetParams>,
callback: (Result<List<HashResult>>) -> Unit
) {
throw IllegalStateException("Method not supported on this Android version.")
}
}

View file

@ -9,10 +9,15 @@ import android.os.Bundle
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresExtension
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.withPermit
import kotlinx.serialization.json.Json
import java.io.BufferedInputStream
import java.security.DigestInputStream
import java.security.MessageDigest
import kotlin.coroutines.cancellation.CancellationException
@RequiresApi(Build.VERSION_CODES.Q)
@RequiresExtension(extension = Build.VERSION_CODES.R, version = 1)
@ -74,20 +79,41 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
return trashed
}
override fun hashTrashedAssets(trashedAssets: List<TrashedAssetParams>): List<ByteArray?> {
val result = ArrayList<ByteArray?>(trashedAssets.size)
for (item in trashedAssets) {
val digest = try {
val id = item.id.toLong()
val mediaType = item.type.toInt()
val uri = contentUriForType(id, mediaType)
sha1OfUri(ctx, uri)
} catch (_: Throwable) {
null
}
result.add(digest)
override fun hashTrashedAssets(
trashedAssets: List<TrashedAssetParams>,
callback: (Result<List<HashResult>>) -> Unit
) {
if (trashedAssets.isEmpty()) {
callback(Result.success(emptyList()))
return
}
hashTask?.cancel()
hashTask = CoroutineScope(Dispatchers.IO).launch {
try {
val results = trashedAssets.map { assetParams ->
async {
hashSemaphore.withPermit {
ensureActive()
hashTrashedAsset(assetParams)
}
}
}.awaitAll()
callback(Result.success(results))
} catch (e: CancellationException) {
callback(
Result.failure(
FlutterError(
HASHING_CANCELLED_CODE,
"Hashing operation was cancelled",
null
)
)
)
} catch (e: Exception) {
callback(Result.failure(e))
}
}
return result
}
override fun shouldFullSync(): Boolean =
@ -144,6 +170,13 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
return SyncDelta(hasChanges, changed, deleted, assetAlbums)
}
suspend fun hashTrashedAsset(assetParams: TrashedAssetParams): HashResult {
val id = assetParams.id.toLong()
val mediaType = assetParams.type.toInt()
val assetUri = contentUriForType(id, mediaType)
return hashAssetFromUri(assetParams.id, assetUri)
}
private fun contentUriForType(id: Long, mediaType: Int): Uri {
val vol = MediaStore.VOLUME_EXTERNAL
val base = when (mediaType) {
@ -154,20 +187,4 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na
}
return ContentUris.withAppendedId(base, id)
}
private fun sha1OfUri(ctx: Context, uri: Uri): ByteArray? {
return try {
val md = MessageDigest.getInstance("SHA-1")
ctx.contentResolver.openInputStream(uri)?.use { input ->
DigestInputStream(BufferedInputStream(input), md).use { dis ->
val buf = ByteArray(DEFAULT_BUFFER_SIZE)
while (dis.read(buf) != -1) { /* pump */
}
}
} ?: return null
md.digest()
} catch (_: Exception) {
null
}
}
}

View file

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import android.util.Base64
import androidx.core.database.getStringOrNull
@ -30,12 +31,12 @@ sealed class AssetResult {
open class NativeSyncApiImplBase(context: Context) {
private val ctx: Context = context.applicationContext
private var hashTask: Job? = null
internal var hashTask: Job? = null
companion object {
private const val MAX_CONCURRENT_HASH_OPERATIONS = 16
private val hashSemaphore = Semaphore(MAX_CONCURRENT_HASH_OPERATIONS)
private const val HASHING_CANCELLED_CODE = "HASH_CANCELLED"
internal val hashSemaphore = Semaphore(MAX_CONCURRENT_HASH_OPERATIONS)
internal const val HASHING_CANCELLED_CODE = "HASH_CANCELLED"
const val MEDIA_SELECTION =
"(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)"
@ -274,12 +275,15 @@ open class NativeSyncApiImplBase(context: Context) {
}
private suspend fun hashAsset(assetId: String): HashResult {
return try {
val assetUri = ContentUris.withAppendedId(
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
assetId.toLong()
)
val assetUri = ContentUris.withAppendedId(
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL),
assetId.toLong()
)
return hashAssetFromUri(assetId, assetUri)
}
protected suspend fun hashAssetFromUri(assetId: String, assetUri: Uri): HashResult {
return try {
val digest = MessageDigest.getInstance("SHA-1")
ctx.contentResolver.openInputStream(assetUri)?.use { inputStream ->
var bytesRead: Int

View file

@ -140,6 +140,7 @@ struct PlatformAsset: Hashable {
var durationInSeconds: Int64
var orientation: Int64
var isFavorite: Bool
var size: Int64? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
@ -154,6 +155,7 @@ struct PlatformAsset: Hashable {
let durationInSeconds = pigeonVar_list[7] as! Int64
let orientation = pigeonVar_list[8] as! Int64
let isFavorite = pigeonVar_list[9] as! Bool
let size: Int64? = nilOrValue(pigeonVar_list[10])
return PlatformAsset(
id: id,
@ -165,7 +167,8 @@ struct PlatformAsset: Hashable {
height: height,
durationInSeconds: durationInSeconds,
orientation: orientation,
isFavorite: isFavorite
isFavorite: isFavorite,
size: size
)
}
func toList() -> [Any?] {
@ -180,6 +183,7 @@ struct PlatformAsset: Hashable {
durationInSeconds,
orientation,
isFavorite,
size,
]
}
static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool {
@ -300,6 +304,39 @@ struct HashResult: Hashable {
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct TrashedAssetParams: Hashable {
var id: String
var type: Int64
var albumId: String? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> TrashedAssetParams? {
let id = pigeonVar_list[0] as! String
let type = pigeonVar_list[1] as! Int64
let albumId: String? = nilOrValue(pigeonVar_list[2])
return TrashedAssetParams(
id: id,
type: type,
albumId: albumId
)
}
func toList() -> [Any?] {
return [
id,
type,
albumId,
]
}
static func == (lhs: TrashedAssetParams, rhs: TrashedAssetParams) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher)
}
}
private class MessagesPigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
@ -311,6 +348,8 @@ private class MessagesPigeonCodecReader: FlutterStandardReader {
return SyncDelta.fromList(self.readValue() as! [Any?])
case 132:
return HashResult.fromList(self.readValue() as! [Any?])
case 133:
return TrashedAssetParams.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
@ -331,6 +370,9 @@ private class MessagesPigeonCodecWriter: FlutterStandardWriter {
} else if let value = value as? HashResult {
super.writeByte(132)
super.writeValue(value.toList())
} else if let value = value as? TrashedAssetParams {
super.writeByte(133)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@ -364,6 +406,8 @@ protocol NativeSyncApi {
func getAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset]
func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void)
func cancelHashing() throws
func getTrashedAssetsForAlbum(albumId: String, updatedTimeCond: Int64?) throws -> [PlatformAsset]
func hashTrashedAssets(trashedAssets: [TrashedAssetParams], completion: @escaping (Result<[HashResult], Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@ -532,5 +576,42 @@ class NativeSyncApiSetup {
} else {
cancelHashingChannel.setMessageHandler(nil)
}
let getTrashedAssetsForAlbumChannel = taskQueue == nil
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
if let api = api {
getTrashedAssetsForAlbumChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let albumIdArg = args[0] as! String
let updatedTimeCondArg: Int64? = nilOrValue(args[1])
do {
let result = try api.getTrashedAssetsForAlbum(albumId: albumIdArg, updatedTimeCond: updatedTimeCondArg)
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
getTrashedAssetsForAlbumChannel.setMessageHandler(nil)
}
let hashTrashedAssetsChannel = taskQueue == nil
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
if let api = api {
hashTrashedAssetsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let trashedAssetsArg = args[0] as! [TrashedAssetParams]
api.hashTrashedAssets(trashedAssets: trashedAssetsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
hashTrashedAssetsChannel.setMessageHandler(nil)
}
}
}

View file

@ -31,7 +31,7 @@ class HashService {
_localAssetRepository = localAssetRepository,
_cancelChecker = cancelChecker,
_nativeSyncApi = nativeSyncApi,
_batchSize = batchSize ?? kBatchHashFileLimit;
_batchSize = batchSize ?? kBatchHashFileLimit,
_trashSyncService = trashSyncService;
bool get isCancelled => _cancelChecker?.call() ?? false;
@ -144,7 +144,6 @@ class HashService {
}
Future<void> _hashTrashedAssets(LocalAlbum album, Iterable<TrashedAsset> assetsToHash) async {
int bytesProcessed = 0;
final toHash = <TrashedAsset>[];
for (final asset in assetsToHash) {
@ -160,13 +159,11 @@ class HashService {
continue;
}
bytesProcessed += asset.size!;
toHash.add(asset);
if (toHash.length >= batchFileLimit || bytesProcessed >= batchSizeLimit) {
if (toHash.length == _batchSize) {
await _processTrashedBatch(album, toHash);
toHash.clear();
bytesProcessed = 0;
}
}
@ -181,27 +178,27 @@ class HashService {
_log.fine("Hashing ${toHash.length} trashed files");
final params = toHash.map((e) => TrashedAssetParams(id: e.id, type: e.type.index, albumId: album.id)).toList();
final hashes = await _nativeSyncApi.hashTrashedAssets(params);
final hashResults = await _nativeSyncApi.hashTrashedAssets(params);
assert(
hashes.length == toHash.length,
"Trashed Assets, Hashes length does not match toHash length: ${hashes.length} != ${toHash.length}",
hashResults.length == toHash.length,
"Trashed Assets, Hashes length does not match toHash length: ${hashResults.length} != ${toHash.length}",
);
final hashed = <TrashedAsset>[];
for (int i = 0; i < hashes.length; i++) {
for (int i = 0; i < hashResults.length; i++) {
if (isCancelled) {
_log.warning("Hashing cancelled. Stopped processing batch.");
return;
}
final hash = hashes[i];
final hashResult = hashResults[i];
final asset = toHash[i];
if (hash?.length == 20) {
hashed.add(asset.copyWith(checksum: base64.encode(hash!)));
if (hashResult.hash != null) {
hashed.add(asset.copyWith(checksum: hashResult.hash!));
} else {
_log.warning(
"Failed to hash trashed file for ${asset.id}: ${asset.name} created at ${asset.createdAt} from album: ${album.name}",
"Failed to hash trashed asset with id: ${hashResult.assetId}, name: ${asset.name}, createdAt: ${asset.createdAt}, from album: ${album.name}. Error: ${hashResult.error ?? "unknown"}",
);
}
}
@ -209,61 +206,4 @@ class HashService {
_log.fine("Hashed ${hashed.length}/${toHash.length} trashed assets");
await _trashSyncService.updateChecksums(hashed);
}
// Future<void> _hashTrashedAssets(LocalAlbum album, Iterable<TrashedAsset> assetsToHash) async {
// final trashedAssets = assetsToHash
// .map((e) => TrashedAssetParams(id: e.id, type: e.type.index, albumId: album.id))
// .toList();
//
// final byId = <String, LocalAsset>{for (final a in assetsToHash) a.id: a};
//
// final hashed = <LocalAsset>[];
// const chunkSize = 10;
//
// for (int i = 0; i < trashedAssets.length; i += chunkSize) {
// if (isCancelled) {
// _log.warning("Hashing cancelled. Stopped processing assets.");
// return;
// }
// final end = (i + chunkSize <= trashedAssets.length) ? i + chunkSize : trashedAssets.length;
// final batch = trashedAssets.sublist(i, end);
//
// List<Uint8List?> hashes;
// try {
// hashes = await _nativeSyncApi.hashTrashedAssets(batch);
// } catch (e, s) {
// _log.severe("hashTrashedAssets failed for batch [$i..${end - 1}]: $e", e, s);
// continue;
// }
//
// if (hashes.length != batch.length) {
// _log.warning(
// "hashTrashedAssets returned ${hashes.length} hashes for ${batch.length} assets (batch [$i..${end - 1}]).",
// );
// }
//
// final limit = hashes.length < batch.length ? hashes.length : batch.length;
// for (int j = 0; j < limit; j++) {
// if (isCancelled) {
// _log.warning("Hashing cancelled. Stopped processing assets.");
// return;
// }
// final asset = batch[j];
// final hash = hashes[j];
//
// if (hash != null && hash.length == 20) {
// final localAsset = byId[asset.id];
// if (localAsset != null) {
// hashed.add(localAsset.copyWith(checksum: base64.encode(hash)));
// }
// } else {
// _log.warning("Failed to hash file for ${asset.id} from album: ${album.name}");
// }
// }
// }
//
// _log.warning("updateHashes for album: ${album.name}, assets: ${hashed.map((e) => '${e.name}-${e.checksum}')}");
//
// await _trashSyncService.updateHashes(hashed);
// }
}

View file

@ -41,6 +41,7 @@ class PlatformAsset {
required this.durationInSeconds,
required this.orientation,
required this.isFavorite,
this.size,
});
String id;
@ -63,8 +64,22 @@ class PlatformAsset {
bool isFavorite;
int? size;
List<Object?> _toList() {
return <Object?>[id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite];
return <Object?>[
id,
name,
type,
createdAt,
updatedAt,
width,
height,
durationInSeconds,
orientation,
isFavorite,
size,
];
}
Object encode() {
@ -84,6 +99,7 @@ class PlatformAsset {
durationInSeconds: result[7]! as int,
orientation: result[8]! as int,
isFavorite: result[9]! as bool,
size: result[10] as int?,
);
}
@ -244,6 +260,45 @@ class HashResult {
int get hashCode => Object.hashAll(_toList());
}
class TrashedAssetParams {
TrashedAssetParams({required this.id, required this.type, this.albumId});
String id;
int type;
String? albumId;
List<Object?> _toList() {
return <Object?>[id, type, albumId];
}
Object encode() {
return _toList();
}
static TrashedAssetParams decode(Object result) {
result as List<Object?>;
return TrashedAssetParams(id: result[0]! as String, type: result[1]! as int, albumId: result[2] as String?);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! TrashedAssetParams || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
@ -263,6 +318,9 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is HashResult) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else if (value is TrashedAssetParams) {
buffer.putUint8(133);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@ -279,6 +337,8 @@ class _PigeonCodec extends StandardMessageCodec {
return SyncDelta.decode(readValue(buffer)!);
case 132:
return HashResult.decode(readValue(buffer)!);
case 133:
return TrashedAssetParams.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
@ -562,4 +622,60 @@ class NativeSyncApi {
return;
}
}
Future<List<PlatformAsset>> getTrashedAssetsForAlbum(String albumId, {int? updatedTimeCond}) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getTrashedAssetsForAlbum$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[albumId, updatedTimeCond]);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<PlatformAsset>();
}
}
Future<List<HashResult>> hashTrashedAssets(List<TrashedAssetParams> trashedAssets) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashTrashedAssets$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[trashedAssets]);
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<HashResult>();
}
}
}

View file

@ -125,6 +125,7 @@ abstract class NativeSyncApi {
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
List<PlatformAsset> getTrashedAssetsForAlbum(String albumId, {int? updatedTimeCond});
@async
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
List<Uint8List?> hashTrashedAssets(List<TrashedAssetParams> trashedAssets);
List<HashResult> hashTrashedAssets(List<TrashedAssetParams> trashedAssets);
}