From 09e5503fb118cb0d73a969979eca96a062312286 Mon Sep 17 00:00:00 2001 From: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Date: Wed, 7 May 2025 19:34:08 +0530 Subject: [PATCH] reduce number of native calls --- .../alextran/immich/platform/MediaManager.kt | 147 +++----- .../alextran/immich/platform/Messages.g.kt | 28 +- .../alextran/immich/platform/MessagesImpl.kt | 28 +- mobile/ios/Runner/Platform/MediaManager.swift | 87 ++--- mobile/ios/Runner/Platform/Messages.g.swift | 24 +- mobile/ios/Runner/Platform/MessagesImpl.swift | 21 +- .../domain/services/device_sync.service.dart | 5 +- mobile/lib/platform/messages.dart | 10 +- mobile/lib/platform/messages.g.dart | 102 +++--- mobile/lib/routing/router.gr.dart | 332 +++++++++--------- 10 files changed, 344 insertions(+), 440 deletions(-) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MediaManager.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MediaManager.kt index 565382fed5..5495831f95 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MediaManager.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MediaManager.kt @@ -5,10 +5,8 @@ import SyncDelta import android.content.Context import android.os.Build import android.provider.MediaStore -import android.provider.MediaStore.VOLUME_EXTERNAL import androidx.annotation.RequiresApi import androidx.annotation.RequiresExtension -import androidx.core.content.edit import kotlinx.serialization.json.Json import java.io.File import java.time.Instant @@ -33,80 +31,48 @@ class MediaManager(context: Context) { @RequiresApi(Build.VERSION_CODES.Q) fun shouldFullSync(callback: (Result) -> Unit) { + val prefs = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) val currVersion = MediaStore.getVersion(ctx) - val lastVersion = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) - .getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null) + val lastVersion = prefs.getString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, null) callback(Result.success(currVersion != lastVersion)) } - @RequiresApi(Build.VERSION_CODES.Q) - @RequiresExtension(extension = Build.VERSION_CODES.R, version = 1) - fun hasMediaChanges(callback: (Result) -> Unit) { - val genMap = getSavedGenerationMap(ctx) - val currentVolumes = MediaStore.getExternalVolumeNames(ctx) - if (currentVolumes != genMap.keys) { - callback(Result.success(true)) - return - } - - val hasChanged = currentVolumes.any { volume -> - val currentGen = MediaStore.getGeneration(ctx, volume) - currentGen != genMap[volume] - } - callback(Result.success(hasChanged)) - } - @RequiresApi(Build.VERSION_CODES.Q) @RequiresExtension(extension = Build.VERSION_CODES.R, version = 1) fun checkpointSync(callback: (Result) -> Unit) { - val genMap = MediaStore.getExternalVolumeNames(ctx).associateWith { - MediaStore.getGeneration(ctx, it) - }.let { Json.encodeToString(it) } - - ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit { + val genMap = + MediaStore.getExternalVolumeNames(ctx).associateWith { MediaStore.getGeneration(ctx, it) } + val prefs = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE) + prefs.edit().apply { putString(SHARED_PREF_MEDIA_STORE_VERSION_KEY, MediaStore.getVersion(ctx)) - putString(SHARED_PREF_MEDIA_STORE_GEN_KEY, genMap) + putString(SHARED_PREF_MEDIA_STORE_GEN_KEY, Json.encodeToString(genMap)) + apply() } callback(Result.success(Unit)) } @RequiresApi(Build.VERSION_CODES.Q) - fun getAssetIdsForAlbum( - albumId: String, - callback: (Result>) -> Unit - ) { + fun getAssetIdsForAlbum(albumId: String, callback: (Result>) -> Unit) { try { - val currentIds = mutableListOf() - val uri = MediaStore.Files.getContentUri(VOLUME_EXTERNAL) + val uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) val projection = arrayOf(MediaStore.Files.FileColumns._ID) - val selection = """ - ${MediaStore.Files.FileColumns.BUCKET_ID} = ? - AND ( - ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? - OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? - ) - """.trimIndent() + val selection = + "${MediaStore.Files.FileColumns.BUCKET_ID} = ? AND (${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)" val selectionArgs = arrayOf( albumId, MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(), MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString() ) - val sortOrder = null - ctx.contentResolver.query( - uri, - projection, - selection, - selectionArgs, - sortOrder - )?.use { cursor -> - val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID) - while (cursor.moveToNext()) { - currentIds.add(cursor.getLong(idColumn).toString()) - } - } + val ids = + ctx.contentResolver.query(uri, projection, selection, selectionArgs, null)?.use { cursor -> + val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID) + generateSequence { + if (cursor.moveToNext()) cursor.getLong(idColumn).toString() else null + }.toList() + } ?: emptyList() - callback(Result.success(currentIds)) + callback(Result.success(ids)) } catch (e: Exception) { callback(Result.failure(e)) } @@ -120,70 +86,57 @@ class MediaManager(context: Context) { val currentVolumes = MediaStore.getExternalVolumeNames(ctx) val changed = mutableListOf() val deleted = mutableListOf() + val formatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC) + var hasChanges = genMap.keys != currentVolumes for (volume in currentVolumes) { val currentGen = MediaStore.getGeneration(ctx, volume) val storedGen = genMap[volume] - - if (storedGen != null && currentGen < storedGen) { + if (storedGen != null && currentGen <= storedGen) { continue } + hasChanges = true - val queryUri = MediaStore.Files.getContentUri(volume) + val uri = MediaStore.Files.getContentUri(volume) val projection = arrayOf( MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME, - MediaStore.MediaColumns.TITLE, - MediaStore.Files.FileColumns.MEDIA_TYPE, - MediaStore.MediaColumns.BUCKET_ID, - MediaStore.MediaColumns.DURATION, MediaStore.MediaColumns.DATE_TAKEN, MediaStore.MediaColumns.DATE_ADDED, MediaStore.MediaColumns.DATE_MODIFIED, + MediaStore.Files.FileColumns.MEDIA_TYPE, + MediaStore.MediaColumns.BUCKET_ID, + MediaStore.MediaColumns.DURATION ) - val selectionParts = mutableListOf() - val selectionArgsList = mutableListOf() - - selectionParts.add( - "(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?)" + val selection = + "(${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? OR ${MediaStore.Files.FileColumns.MEDIA_TYPE} = ?) AND (${MediaStore.MediaColumns.GENERATION_MODIFIED} > ? OR ${MediaStore.MediaColumns.GENERATION_ADDED} > ?)" + val selectionArgs = arrayOf( + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(), + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString(), + storedGen?.toString() ?: "0", + storedGen?.toString() ?: "0" ) - selectionArgsList.add(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString()) - selectionArgsList.add(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()) - - if (storedGen != null) { - selectionParts.add( - "(${MediaStore.MediaColumns.GENERATION_MODIFIED} > ? OR ${MediaStore.MediaColumns.GENERATION_ADDED} > ?)" - ) - selectionArgsList.add(storedGen.toString()) - selectionArgsList.add(storedGen.toString()) - } - - val selection = selectionParts.joinToString(" AND ") - val selectionArgs = selectionArgsList.toTypedArray() - val sortOrder = null ctx.contentResolver.query( - queryUri, + uri, projection, selection, selectionArgs, - sortOrder + null )?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID) - val mediaTypeColumn = - cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE) - val nameColumn = - cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME) val dataColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA) - val dateTakeColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN) + val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME) + val dateTakenColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN) val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED) val dateModifiedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED) - val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) + val mediaTypeColumn = + cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE) val bucketIdColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_ID) - val formatter = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneOffset.UTC) + val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn).toString() @@ -195,14 +148,11 @@ class MediaManager(context: Context) { val mediaType = cursor.getInt(mediaTypeColumn) val name = cursor.getString(nameColumn) - // Date taken is milliseconds since epoch - var takenAt = cursor.getLong(dateTakeColumn) - if (takenAt == 0L) { - // Date added is seconds since epoch - takenAt = cursor.getLong(dateAddedColumn) * 1000 - } - val takenInstant = Instant.ofEpochMilli(takenAt) - val createdAt = formatter.format(takenInstant) + // Date taken is milliseconds since epoch, Date added is seconds since epoch + val takenAt = cursor.getLong(dateTakenColumn).takeIf { it > 0 } ?: (cursor.getLong( + dateAddedColumn + ) * 1000) + val createdAt = formatter.format(Instant.ofEpochMilli(takenAt)) // Date modified is seconds since epoch val modifiedAt = formatter.format(Instant.ofEpochMilli(cursor.getLong(dateModifiedColumn) * 1000)) @@ -228,9 +178,8 @@ class MediaManager(context: Context) { } } // Unmounted volumes are handled in dart when the album is removed - val syncDelta = SyncDelta(updates = changed, deletes = deleted) - callback(Result.success(syncDelta)) + callback(Result.success(SyncDelta(hasChanges, changed, deleted))) } catch (e: Exception) { callback(Result.failure(e)) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/Messages.g.kt index b6b3a8ab3a..b4682d28db 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/Messages.g.kt @@ -125,19 +125,22 @@ data class Asset ( /** Generated class from Pigeon that represents data sent in messages. */ data class SyncDelta ( + val hasChanges: Boolean, val updates: List, val deletes: List ) { companion object { fun fromList(pigeonVar_list: List): SyncDelta { - val updates = pigeonVar_list[0] as List - val deletes = pigeonVar_list[1] as List - return SyncDelta(updates, deletes) + val hasChanges = pigeonVar_list[0] as Boolean + val updates = pigeonVar_list[1] as List + val deletes = pigeonVar_list[2] as List + return SyncDelta(hasChanges, updates, deletes) } } fun toList(): List { return listOf( + hasChanges, updates, deletes, ) @@ -188,7 +191,6 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface ImHostService { fun shouldFullSync(callback: (Result) -> Unit) - fun hasMediaChanges(callback: (Result) -> Unit) fun getMediaChanges(callback: (Result) -> Unit) fun checkpointSync(callback: (Result) -> Unit) fun getAssetIdsForAlbum(albumId: String, callback: (Result>) -> Unit) @@ -221,24 +223,6 @@ interface ImHostService { channel.setMessageHandler(null) } } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ImHostService.hasMediaChanges$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { _, reply -> - api.hasMediaChanges{ result: Result -> - 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) - } - } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges$separatedMessageChannelSuffix", codec, taskQueue) if (api != null) { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MessagesImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MessagesImpl.kt index a4963365d0..96e03d5e0e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MessagesImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/platform/MessagesImpl.kt @@ -10,6 +10,16 @@ class MessagesImpl(context: Context) : ImHostService { private val ctx: Context = context.applicationContext private val mediaManager: MediaManager = MediaManager(ctx) + companion object { + private fun isMediaChangesSupported(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && + SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1 + } + + private fun unsupportedFeatureException() = + IllegalStateException("Method not supported on this Android version.") + } + override fun shouldFullSync(callback: (Result) -> Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { mediaManager.shouldFullSync(callback) @@ -18,24 +28,16 @@ class MessagesImpl(context: Context) : ImHostService { } } - override fun hasMediaChanges(callback: (Result) -> Unit) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) { - mediaManager.hasMediaChanges(callback) - } else { - callback(Result.failure(IllegalStateException("hasMediaChanges changes not supported on this Android version."))) - } - } - override fun getMediaChanges(callback: (Result) -> Unit) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) { + if (isMediaChangesSupported()) { mediaManager.getMediaChanges(callback) } else { - callback(Result.failure(IllegalStateException("getMediaChanges not supported on this Android version."))) + callback(Result.failure(unsupportedFeatureException())) } } override fun checkpointSync(callback: (Result) -> Unit) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) { + if (isMediaChangesSupported()) { mediaManager.checkpointSync(callback) } else { callback(Result.success(Unit)) @@ -46,10 +48,10 @@ class MessagesImpl(context: Context) : ImHostService { albumId: String, callback: (Result>) -> Unit ) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= 1) { + if (isMediaChangesSupported()) { mediaManager.getAssetIdsForAlbum(albumId, callback) } else { - callback(Result.failure(IllegalStateException("getAssetIdsForAlbum not supported on this Android version."))) + callback(Result.failure(unsupportedFeatureException())) } } } diff --git a/mobile/ios/Runner/Platform/MediaManager.swift b/mobile/ios/Runner/Platform/MediaManager.swift index 862f9cff26..ab84ce2328 100644 --- a/mobile/ios/Runner/Platform/MediaManager.swift +++ b/mobile/ios/Runner/Platform/MediaManager.swift @@ -1,26 +1,24 @@ import Photos class MediaManager { - let _defaults: UserDefaults - - let _changeTokenKey = "immich:changeToken"; + private let _defaults: UserDefaults + private let _changeTokenKey = "immich:changeToken" init(with defaults: UserDefaults = .standard) { - _defaults = defaults + self._defaults = defaults } @available(iOS 16, *) func _getChangeToken() -> PHPersistentChangeToken? { guard let encodedToken = _defaults.data(forKey: _changeTokenKey) else { - print("_getChangeToken: Change token not available in UserDefaults") + print("MediaManager::_getChangeToken: Change token not available in UserDefaults") return nil } do { - let changeToken = try NSKeyedUnarchiver.unarchivedObject(ofClass: PHPersistentChangeToken.self, from: encodedToken) - return changeToken + return try NSKeyedUnarchiver.unarchivedObject(ofClass: PHPersistentChangeToken.self, from: encodedToken) } catch { - print("_getChangeToken: Cannot decode the token from UserDefaults") + print("MediaManager::_getChangeToken: Cannot decode the token from UserDefaults") return nil } } @@ -30,9 +28,9 @@ class MediaManager { do { let encodedToken = try NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: true) _defaults.set(encodedToken, forKey: _changeTokenKey) - print("_setChangeToken: Change token saved to UserDefaults") + print("MediaManager::_setChangeToken: Change token saved to UserDefaults") } catch { - print("_setChangeToken: Failed to persist the token to UserDefaults: \(error)") + print("MediaManager::_setChangeToken: Failed to persist the token to UserDefaults: \(error)") } } @@ -52,7 +50,7 @@ class MediaManager { guard let storedToken = _getChangeToken() else { // No token exists, perform the initial full sync - print("shouldUseOldSync: No token found") + print("MediaManager::shouldUseOldSync: No token found. Full sync required") completion(.success(true)) return } @@ -62,44 +60,40 @@ class MediaManager { completion(.success(false)) } catch { // fallback to using old sync when we cannot detect changes using the available token - print("shouldUseOldSync: fetchPersistentChanges failed with error (\(error))") + print("MediaManager::shouldUseOldSync: fetchPersistentChanges failed with error (\(error))") completion(.success(true)) } } - @available(iOS 16, *) - func hasMediaChanges(completion: @escaping (Result) -> Void) { - guard PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized else { - completion(.failure(PigeonError(code: "1", message: "No photo library access", details: nil))) - return - } - - let storedToken = _getChangeToken() - let currentToken = PHPhotoLibrary.shared().currentChangeToken - completion(.success(storedToken != currentToken)) - } - @available(iOS 16, *) func getMediaChanges(completion: @escaping (Result) -> Void) { guard PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized else { - completion(.failure(PigeonError(code: "1", message: "No photo library access", details: nil))) + completion(.failure(PigeonError(code: "NO_AUTH", message: "No photo library access", details: nil))) return } guard let storedToken = _getChangeToken() else { // No token exists, definitely need a full sync - print("getMediaChanges: No token found") - completion(.failure(PigeonError(code: "2", message: "No stored change token", details: nil))) + print("MediaManager::getMediaChanges: No token found") + completion(.failure(PigeonError(code: "NO_TOKEN", message: "No stored change token", details: nil))) return } + + let currentToken = PHPhotoLibrary.shared().currentChangeToken + if storedToken == currentToken { + completion(.success(SyncDelta(hasChanges: false, updates: [], deletes: []))) + return + } do { let result = try PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken) - let dateFormatter = ISO8601DateFormatter() dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] - var delta = SyncDelta(updates: [], deletes: []) + + var updatedArr: [Asset] = [] + var deletedArr: [String] = [] + for changes in result { let details = try changes.changeDetails(for: PHObjectType.asset) let updated = details.updatedLocalIdentifiers.union(details.insertedLocalIdentifiers) @@ -108,8 +102,7 @@ class MediaManager { let options = PHFetchOptions() options.includeHiddenAssets = true let updatedAssets = PHAsset.fetchAssets(withLocalIdentifiers: Array(updated), options: options) - - var updates: [Asset] = [] + updatedAssets.enumerateObjects { (asset, _, _) in let id = asset.localIdentifier let name = PHAssetResource.assetResources(for: asset).first?.originalFilename ?? asset.title() @@ -118,33 +111,41 @@ class MediaManager { let updatedAt = asset.modificationDate.map { dateFormatter.string(from: $0) } let durationInSeconds: Int64 = Int64(asset.duration) - let dAsset = Asset(id: id, name: name, type: type, createdAt: createdAt, updatedAt: updatedAt, durationInSeconds: durationInSeconds, albumIds: self._getAlbumIdsForAsset(asset: asset)) - updates.append(dAsset) + let domainAsset = Asset( + id: id, + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + albumIds: self._getAlbumIds(forAsset: asset) + ) + updatedArr.append(domainAsset) } - delta.updates.append(contentsOf: updates) - delta.deletes.append(contentsOf: deleted) + deletedArr.append(contentsOf: details.deletedLocalIdentifiers) } + let delta = SyncDelta(hasChanges: true, updates: updatedArr, deletes: deletedArr) completion(.success(delta)) return } catch { - print("getMediaChanges: Error fetching persistent changes: \(error)") + print("MediaManager::getMediaChanges: Error fetching persistent changes: \(error)") completion(.failure(PigeonError(code: "3", message: error.localizedDescription, details: nil))) return } } @available(iOS 16, *) - func _getAlbumIdsForAsset(asset: PHAsset) -> [String] { + func _getAlbumIds(forAsset: PHAsset) -> [String] { var albumIds: [String] = [] - var albums = PHAssetCollection.fetchAssetCollectionsContaining(asset, with: .album, options: nil) - albums.enumerateObjects { (album, _, _) in - albumIds.append(album.localIdentifier) - } - albums = PHAssetCollection.fetchAssetCollectionsContaining(asset, with: .smartAlbum, options: nil) - albums.enumerateObjects { (album, _, _) in + let albumTypes: [PHAssetCollectionType] = [.album, .smartAlbum] + + albumTypes.forEach { type in + let collections = PHAssetCollection.fetchAssetCollectionsContaining(forAsset, with: type, options: nil) + collections.enumerateObjects { (album, _, _) in albumIds.append(album.localIdentifier) + } } return albumIds } diff --git a/mobile/ios/Runner/Platform/Messages.g.swift b/mobile/ios/Runner/Platform/Messages.g.swift index de9e4974ab..e9fb772b5c 100644 --- a/mobile/ios/Runner/Platform/Messages.g.swift +++ b/mobile/ios/Runner/Platform/Messages.g.swift @@ -179,22 +179,26 @@ struct Asset: Hashable { /// Generated class from Pigeon that represents data sent in messages. struct SyncDelta: Hashable { + var hasChanges: Bool var updates: [Asset] var deletes: [String] // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> SyncDelta? { - let updates = pigeonVar_list[0] as! [Asset] - let deletes = pigeonVar_list[1] as! [String] + let hasChanges = pigeonVar_list[0] as! Bool + let updates = pigeonVar_list[1] as! [Asset] + let deletes = pigeonVar_list[2] as! [String] return SyncDelta( + hasChanges: hasChanges, updates: updates, deletes: deletes ) } func toList() -> [Any?] { return [ + hasChanges, updates, deletes, ] @@ -251,7 +255,6 @@ class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol ImHostService { func shouldFullSync(completion: @escaping (Result) -> Void) - func hasMediaChanges(completion: @escaping (Result) -> Void) func getMediaChanges(completion: @escaping (Result) -> Void) func checkpointSync(completion: @escaping (Result) -> Void) func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], Error>) -> Void) @@ -283,21 +286,6 @@ class ImHostServiceSetup { } else { shouldFullSyncChannel.setMessageHandler(nil) } - let hasMediaChangesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ImHostService.hasMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - hasMediaChangesChannel.setMessageHandler { _, reply in - api.hasMediaChanges { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - hasMediaChangesChannel.setMessageHandler(nil) - } let getMediaChangesChannel = taskQueue == nil ? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) : FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) diff --git a/mobile/ios/Runner/Platform/MessagesImpl.swift b/mobile/ios/Runner/Platform/MessagesImpl.swift index 92dc60e326..3dc00ecb3c 100644 --- a/mobile/ios/Runner/Platform/MessagesImpl.swift +++ b/mobile/ios/Runner/Platform/MessagesImpl.swift @@ -4,33 +4,24 @@ class ImHostServiceImpl: ImHostService { let _mediaManager: MediaManager init() { - _mediaManager = MediaManager() + self._mediaManager = MediaManager() } func shouldFullSync(completion: @escaping (Result) -> Void) { if #available(iOS 16, *) { _mediaManager.shouldFullSync(completion: completion) - return; } else { // Always fall back to full sync on older iOS versions completion(.success(true)) } } - func hasMediaChanges(completion: @escaping (Result) -> Void) { - if #available(iOS 16, *) { - _mediaManager.hasMediaChanges(completion: completion) - } else { - completion(.failure(PigeonError(code: "-1", message: "Not supported", details: nil))) - } - } - func getMediaChanges(completion: @escaping (Result) -> Void) { - if #available(iOS 16, *) { - _mediaManager.getMediaChanges(completion: completion) - } else { - completion(.failure(PigeonError(code: "-1", message: "Not supported", details: nil))) + guard #available(iOS 16, *) else { + completion(.failure(PigeonError(code: "UNSUPPORTED_OS", message: "This feature requires iOS 16 or later.", details: nil))) + return } + _mediaManager.getMediaChanges(completion: completion) } func checkpointSync(completion: @escaping (Result) -> Void) { @@ -42,7 +33,7 @@ class ImHostServiceImpl: ImHostService { } func getAssetIdsForAlbum(albumId: String, completion: @escaping (Result<[String], any Error>) -> Void) { - // Android specific, ignore the call with an empty list + // Android specific, empty list is safe no-op completion(.success([])) } } diff --git a/mobile/lib/domain/services/device_sync.service.dart b/mobile/lib/domain/services/device_sync.service.dart index 08b5440daa..ad619d54b6 100644 --- a/mobile/lib/domain/services/device_sync.service.dart +++ b/mobile/lib/domain/services/device_sync.service.dart @@ -35,15 +35,14 @@ class DeviceSyncService { return await fullSync(); } - if (!await _hostService.hasMediaChanges()) { + final delta = await _hostService.getMediaChanges(); + if (!delta.hasChanges) { _log.fine("No media changes detected. Skipping sync"); return; } final deviceAlbums = await _albumMediaRepository.getAll(); await _localAlbumRepository.updateAll(deviceAlbums); - - final delta = await _hostService.getMediaChanges(); await _localAlbumRepository.handleSyncDelta(delta); if (_platform.isAndroid) { diff --git a/mobile/lib/platform/messages.dart b/mobile/lib/platform/messages.dart index 807543c9a9..58df82b355 100644 --- a/mobile/lib/platform/messages.dart +++ b/mobile/lib/platform/messages.dart @@ -33,7 +33,12 @@ class Asset { } class SyncDelta { - SyncDelta({this.updates = const [], this.deletes = const []}); + SyncDelta({ + this.hasChanges = false, + this.updates = const [], + this.deletes = const [], + }); + bool hasChanges; List updates; List deletes; } @@ -43,9 +48,6 @@ abstract class ImHostService { @async bool shouldFullSync(); - @async - bool hasMediaChanges(); - @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) SyncDelta getMediaChanges(); diff --git a/mobile/lib/platform/messages.g.dart b/mobile/lib/platform/messages.g.dart index 05a8dc24a5..b53a5ee140 100644 --- a/mobile/lib/platform/messages.g.dart +++ b/mobile/lib/platform/messages.g.dart @@ -14,21 +14,22 @@ PlatformException _createConnectionError(String channelName) { message: 'Unable to establish connection on channel: "$channelName".', ); } + bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + return a.length == b.length && + a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } - class Asset { Asset({ required this.id, @@ -67,7 +68,8 @@ class Asset { } Object encode() { - return _toList(); } + return _toList(); + } static Asset decode(Object result) { result as List; @@ -96,35 +98,40 @@ class Asset { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } class SyncDelta { SyncDelta({ + this.hasChanges = false, this.updates = const [], this.deletes = const [], }); + bool hasChanges; + List updates; List deletes; List _toList() { return [ + hasChanges, updates, deletes, ]; } Object encode() { - return _toList(); } + return _toList(); + } static SyncDelta decode(Object result) { result as List; return SyncDelta( - updates: (result[0] as List?)!.cast(), - deletes: (result[1] as List?)!.cast(), + hasChanges: result[0]! as bool, + updates: (result[1] as List?)!.cast(), + deletes: (result[2] as List?)!.cast(), ); } @@ -142,11 +149,9 @@ class SyncDelta { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -154,10 +159,10 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is Asset) { + } else if (value is Asset) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is SyncDelta) { + } else if (value is SyncDelta) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -168,9 +173,9 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return Asset.decode(readValue(buffer)!); - case 130: + case 130: return SyncDelta.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -182,9 +187,11 @@ class ImHostService { /// Constructor for [ImHostService]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - ImHostService({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + ImHostService( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -192,36 +199,10 @@ class ImHostService { final String pigeonVar_messageChannelSuffix; Future shouldFullSync() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.shouldFullSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - 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 bool?)!; - } - } - - Future hasMediaChanges() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.hasMediaChanges$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostService.shouldFullSync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -248,8 +229,10 @@ class ImHostService { } Future getMediaChanges() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostService.getMediaChanges$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -276,8 +259,10 @@ class ImHostService { } Future checkpointSync() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.checkpointSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostService.checkpointSync$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, @@ -299,13 +284,16 @@ class ImHostService { } Future> getAssetIdsForAlbum(String albumId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final String pigeonVar_channelName = + 'dev.flutter.pigeon.immich_mobile.ImHostService.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([albumId]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index be3c7a7a71..c2deea872d 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -14,7 +14,7 @@ part of 'router.dart'; /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { const ActivitiesRoute({List? children}) - : super(ActivitiesRoute.name, initialChildren: children); + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -35,13 +35,13 @@ class AlbumAdditionalSharedUserSelectionRoute required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -83,14 +83,14 @@ class AlbumAssetSelectionRoute bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -130,7 +130,7 @@ class AlbumAssetSelectionRouteArgs { /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { const AlbumOptionsRoute({List? children}) - : super(AlbumOptionsRoute.name, initialChildren: children); + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -150,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -188,10 +188,10 @@ class AlbumSharedUserSelectionRoute required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -225,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -258,7 +258,7 @@ class AlbumViewerRouteArgs { /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { const AlbumsRoute({List? children}) - : super(AlbumsRoute.name, initialChildren: children); + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -274,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -290,7 +290,7 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { const AllPeopleRoute({List? children}) - : super(AllPeopleRoute.name, initialChildren: children); + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -306,7 +306,7 @@ class AllPeopleRoute extends PageRouteInfo { /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { const AllPlacesRoute({List? children}) - : super(AllPlacesRoute.name, initialChildren: children); + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -322,7 +322,7 @@ class AllPlacesRoute extends PageRouteInfo { /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { const AllVideosRoute({List? children}) - : super(AllVideosRoute.name, initialChildren: children); + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -342,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -375,7 +375,7 @@ class AppLogDetailRouteArgs { /// [AppLogPage] class AppLogRoute extends PageRouteInfo { const AppLogRoute({List? children}) - : super(AppLogRoute.name, initialChildren: children); + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -391,7 +391,7 @@ class AppLogRoute extends PageRouteInfo { /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { const ArchiveRoute({List? children}) - : super(ArchiveRoute.name, initialChildren: children); + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -407,7 +407,7 @@ class ArchiveRoute extends PageRouteInfo { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -423,7 +423,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -439,7 +439,7 @@ class BackupControllerRoute extends PageRouteInfo { /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { const BackupOptionsRoute({List? children}) - : super(BackupOptionsRoute.name, initialChildren: children); + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -455,7 +455,7 @@ class BackupOptionsRoute extends PageRouteInfo { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -475,10 +475,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -515,10 +515,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -560,15 +560,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -612,7 +612,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -628,7 +628,7 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { const FavoritesRoute({List? children}) - : super(FavoritesRoute.name, initialChildren: children); + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -644,7 +644,7 @@ class FavoritesRoute extends PageRouteInfo { /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { const FeatInDevRoute({List? children}) - : super(FeatInDevRoute.name, initialChildren: children); + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -665,10 +665,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -712,10 +712,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -754,16 +754,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -811,7 +811,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -827,7 +827,7 @@ class HeaderSettingsRoute extends PageRouteInfo { /// [LibraryPage] class LibraryRoute extends PageRouteInfo { const LibraryRoute({List? children}) - : super(LibraryRoute.name, initialChildren: children); + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -843,7 +843,7 @@ class LibraryRoute extends PageRouteInfo { /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { const LocalAlbumsRoute({List? children}) - : super(LocalAlbumsRoute.name, initialChildren: children); + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -859,7 +859,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LoginPage] class LoginRoute extends PageRouteInfo { const LoginRoute({List? children}) - : super(LoginRoute.name, initialChildren: children); + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -879,13 +879,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -923,11 +923,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -964,14 +964,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1018,16 +1018,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1079,10 +1079,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1112,7 +1112,7 @@ class PartnerDetailRouteArgs { /// [PartnerPage] class PartnerRoute extends PageRouteInfo { const PartnerRoute({List? children}) - : super(PartnerRoute.name, initialChildren: children); + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1128,7 +1128,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1144,7 +1144,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1165,14 +1165,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1212,7 +1212,7 @@ class PersonResultRouteArgs { /// [PhotosPage] class PhotosRoute extends PageRouteInfo { const PhotosRoute({List? children}) - : super(PhotosRoute.name, initialChildren: children); + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1232,13 +1232,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -1273,7 +1273,7 @@ class PlacesCollectionRouteArgs { /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { const RecentlyTakenRoute({List? children}) - : super(RecentlyTakenRoute.name, initialChildren: children); + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -1293,10 +1293,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -1328,7 +1328,7 @@ class SearchRouteArgs { /// [SettingsPage] class SettingsRoute extends PageRouteInfo { const SettingsRoute({List? children}) - : super(SettingsRoute.name, initialChildren: children); + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -1348,10 +1348,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -1385,10 +1385,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -1424,15 +1424,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -1478,7 +1478,7 @@ class SharedLinkEditRouteArgs { /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { const SharedLinkRoute({List? children}) - : super(SharedLinkRoute.name, initialChildren: children); + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -1494,7 +1494,7 @@ class SharedLinkRoute extends PageRouteInfo { /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { const SplashScreenRoute({List? children}) - : super(SplashScreenRoute.name, initialChildren: children); + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -1510,7 +1510,7 @@ class SplashScreenRoute extends PageRouteInfo { /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { const TabControllerRoute({List? children}) - : super(TabControllerRoute.name, initialChildren: children); + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -1526,7 +1526,7 @@ class TabControllerRoute extends PageRouteInfo { /// [TrashPage] class TrashRoute extends PageRouteInfo { const TrashRoute({List? children}) - : super(TrashRoute.name, initialChildren: children); + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute';