diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt index 5a3b0e1f3d..e6480cf9f4 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichApp.kt @@ -3,7 +3,6 @@ package app.alextran.immich import android.app.Application import androidx.work.Configuration import androidx.work.WorkManager -import app.alextran.immich.background.BackgroundWorkerApiImpl class ImmichApp : Application() { override fun onCreate() { @@ -17,6 +16,5 @@ class ImmichApp : Application() { // As a workaround, we also run a backup check when initializing the application ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) - BackgroundWorkerApiImpl.enqueueBackgroundWorker(this) } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichPlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichPlugin.kt new file mode 100644 index 0000000000..d13e65cc87 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/ImmichPlugin.kt @@ -0,0 +1,20 @@ +package app.alextran.immich + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +fun dispatch( + dispatcher: CoroutineDispatcher = Dispatchers.IO, + callback: (Result) -> Unit, + block: () -> T +) { + CoroutineScope(dispatcher).launch { + val result = runCatching { block() } + withContext(Dispatchers.Main) { + callback(result) + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt index d8afe32b5c..a692ac08fd 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt @@ -2,49 +2,50 @@ package app.alextran.immich.background import android.content.Context import android.util.Log +import app.alextran.immich.dispatch import io.flutter.embedding.engine.plugins.FlutterPlugin import java.util.concurrent.atomic.AtomicInteger private const val TAG = "BackgroundEngineLock" class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterPlugin { - private val ctx: Context = context.applicationContext + private val ctx: Context = context.applicationContext - companion object { + companion object { - private var engineCount = AtomicInteger(0) + private var engineCount = AtomicInteger(0) - private fun checkAndEnforceBackgroundLock(ctx: Context) { - // work manager task is running while the main app is opened, cancel the worker - if (BackgroundWorkerPreferences(ctx).isLocked() && - engineCount.get() > 1 && - BackgroundWorkerApiImpl.isBackgroundWorkerRunning() - ) { - Log.i(TAG, "Background worker is locked, cancelling the background worker") - BackgroundWorkerApiImpl.cancelBackgroundWorker(ctx) - } - } + private fun checkAndEnforceBackgroundLock(ctx: Context) { + // work manager task is running while the main app is opened, cancel the worker + if (BackgroundWorkerPreferences(ctx).isLocked() && + engineCount.get() > 1 && + BackgroundWorkerApiImpl.isBackgroundWorkerRunning() + ) { + Log.i(TAG, "Background worker is locked, cancelling the background worker") + BackgroundWorkerApiImpl.cancelBackgroundWorker(ctx) + } } + } - override fun lock() { - BackgroundWorkerPreferences(ctx).setLocked(true) - checkAndEnforceBackgroundLock(ctx) - Log.i(TAG, "Background worker is locked") - } + override fun lock(callback: (Result) -> Unit) = dispatch(callback = callback) { + BackgroundWorkerPreferences(ctx).setLocked(true) + checkAndEnforceBackgroundLock(ctx) + Log.i(TAG, "Background worker is locked") + } - override fun unlock() { - BackgroundWorkerPreferences(ctx).setLocked(false) - Log.i(TAG, "Background worker is unlocked") - } + override fun unlock(callback: (Result) -> Unit) = dispatch(callback = callback) { + BackgroundWorkerPreferences(ctx).setLocked(false) + Log.i(TAG, "Background worker is unlocked") + } - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - checkAndEnforceBackgroundLock(binding.applicationContext) - engineCount.incrementAndGet() - Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") - } + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + checkAndEnforceBackgroundLock(binding.applicationContext) + engineCount.incrementAndGet() + Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") + } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - engineCount.decrementAndGet() - Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") - } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + engineCount.decrementAndGet() + Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") + } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt index 052395c172..b62ffcf583 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt @@ -133,11 +133,12 @@ private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() { } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface BackgroundWorkerFgHostApi { - fun enable() - fun configure(settings: BackgroundWorkerSettings) - fun disable() + fun enable(callback: (Result) -> Unit) + fun configure(settings: BackgroundWorkerSettings, callback: (Result) -> Unit) + fun disable(callback: (Result) -> Unit) companion object { /** The codec used by BackgroundWorkerFgHostApi. */ @@ -152,13 +153,14 @@ interface BackgroundWorkerFgHostApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enable$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - api.enable() - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerPigeonUtils.wrapError(exception) + api.enable{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(BackgroundWorkerPigeonUtils.wrapError(error)) + } else { + reply.reply(BackgroundWorkerPigeonUtils.wrapResult(null)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -170,13 +172,14 @@ interface BackgroundWorkerFgHostApi { channel.setMessageHandler { message, reply -> val args = message as List val settingsArg = args[0] as BackgroundWorkerSettings - val wrapped: List = try { - api.configure(settingsArg) - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerPigeonUtils.wrapError(exception) + api.configure(settingsArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(BackgroundWorkerPigeonUtils.wrapError(error)) + } else { + reply.reply(BackgroundWorkerPigeonUtils.wrapResult(null)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -186,13 +189,14 @@ interface BackgroundWorkerFgHostApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disable$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - api.disable() - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerPigeonUtils.wrapError(exception) + api.disable{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(BackgroundWorkerPigeonUtils.wrapError(error)) + } else { + reply.reply(BackgroundWorkerPigeonUtils.wrapResult(null)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt index 71d9f5ffe3..6063adbc19 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt @@ -215,7 +215,7 @@ class BackgroundWorker(context: Context, params: WorkerParameters) : if (foregroundFuture != null && !foregroundFuture.isCancelled && !foregroundFuture.isDone) { try { foregroundFuture.get(500, TimeUnit.MILLISECONDS) - } catch (e: Exception) { + } catch (_: Exception) { // ignored, there is nothing to be done } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt index 78f2e9e461..18a3666844 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerApiImpl.kt @@ -8,6 +8,7 @@ import androidx.work.Constraints import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager +import app.alextran.immich.dispatch import io.flutter.embedding.engine.FlutterEngineCache import java.util.concurrent.TimeUnit @@ -16,16 +17,18 @@ private const val TAG = "BackgroundWorkerApiImpl" class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi { private val ctx: Context = context.applicationContext - override fun enable() { - enqueueMediaObserver(ctx) - } + override fun enable(callback: (Result) -> Unit) = + dispatch(callback = callback) { enqueueMediaObserver(ctx) } - override fun configure(settings: BackgroundWorkerSettings) { + override fun configure( + settings: BackgroundWorkerSettings, + callback: (Result) -> Unit + ) = dispatch(callback = callback) { BackgroundWorkerPreferences(ctx).updateSettings(settings) enqueueMediaObserver(ctx) } - override fun disable() { + override fun disable(callback: (Result) -> Unit) = dispatch(callback = callback) { WorkManager.getInstance(ctx).apply { cancelUniqueWork(OBSERVER_WORKER_NAME) cancelUniqueWork(BACKGROUND_WORKER_NAME) @@ -38,7 +41,6 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi { private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1" const val ENGINE_CACHE_KEY = "immich::background_worker::engine" - fun enqueueMediaObserver(ctx: Context) { val settings = BackgroundWorkerPreferences(ctx).getSettings() val constraints = Constraints.Builder().apply { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt index 3d00bafba2..381b514de1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt @@ -44,10 +44,11 @@ private open class BackgroundWorkerLockPigeonCodec : StandardMessageCodec() { } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface BackgroundWorkerLockApi { - fun lock() - fun unlock() + fun lock(callback: (Result) -> Unit) + fun unlock(callback: (Result) -> Unit) companion object { /** The codec used by BackgroundWorkerLockApi. */ @@ -62,13 +63,14 @@ interface BackgroundWorkerLockApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.lock$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - api.lock() - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerLockPigeonUtils.wrapError(exception) + api.lock{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(BackgroundWorkerLockPigeonUtils.wrapError(error)) + } else { + reply.reply(BackgroundWorkerLockPigeonUtils.wrapResult(null)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -78,13 +80,14 @@ interface BackgroundWorkerLockApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerLockApi.unlock$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - api.unlock() - listOf(null) - } catch (exception: Throwable) { - BackgroundWorkerLockPigeonUtils.wrapError(exception) + api.unlock{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(BackgroundWorkerLockPigeonUtils.wrapError(error)) + } else { + reply.reply(BackgroundWorkerLockPigeonUtils.wrapResult(null)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt index 434ba47ca1..bc17d9933e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt @@ -82,9 +82,10 @@ private open class ConnectivityPigeonCodec : StandardMessageCodec() { } } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface ConnectivityApi { - fun getCapabilities(): List + fun getCapabilities(callback: (Result>) -> Unit) companion object { /** The codec used by ConnectivityApi. */ @@ -100,12 +101,15 @@ interface ConnectivityApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.ConnectivityApi.getCapabilities$separatedMessageChannelSuffix", codec, taskQueue) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - listOf(api.getCapabilities()) - } catch (exception: Throwable) { - ConnectivityPigeonUtils.wrapError(exception) + api.getCapabilities{ result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(ConnectivityPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(ConnectivityPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt index e8554dd63a..f95863e78c 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/ConnectivityApiImpl.kt @@ -4,6 +4,7 @@ import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.net.wifi.WifiManager +import app.alextran.immich.dispatch class ConnectivityApiImpl(context: Context) : ConnectivityApi { private val connectivityManager = @@ -11,7 +12,13 @@ class ConnectivityApiImpl(context: Context) : ConnectivityApi { private val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager - override fun getCapabilities(): List { + + override fun getCapabilities(callback: (Result>) -> Unit) = + dispatch(callback = callback) { + getCapabilities() + } + + private fun getCapabilities(): List { val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) ?: return emptyList() diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index 28400c803f..ebf0bb0c6e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -296,13 +296,13 @@ private open class MessagesPigeonCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface NativeSyncApi { fun shouldFullSync(): Boolean - fun getMediaChanges(): SyncDelta + fun getMediaChanges(callback: (Result) -> Unit) fun checkpointSync() fun clearSyncCheckpoint() - fun getAssetIdsForAlbum(albumId: String): List - fun getAlbums(): List - fun getAssetsCountSince(albumId: String, timestamp: Long): Long - fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List + fun getAssetIdsForAlbum(albumId: String, callback: (Result>) -> Unit) + fun getAlbums(callback: (Result>) -> Unit) + fun getAssetsCountSince(albumId: String, timestamp: Long, callback: (Result) -> Unit) + fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?, callback: (Result>) -> Unit) fun hashAssets(assetIds: List, allowNetworkAccess: Boolean, callback: (Result>) -> Unit) fun cancelHashing() @@ -335,12 +335,15 @@ interface NativeSyncApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$separatedMessageChannelSuffix", codec, taskQueue) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - listOf(api.getMediaChanges()) - } catch (exception: Throwable) { - MessagesPigeonUtils.wrapError(exception) + api.getMediaChanges{ result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -384,12 +387,15 @@ interface NativeSyncApi { channel.setMessageHandler { message, reply -> val args = message as List val albumIdArg = args[0] as String - val wrapped: List = try { - listOf(api.getAssetIdsForAlbum(albumIdArg)) - } catch (exception: Throwable) { - MessagesPigeonUtils.wrapError(exception) + api.getAssetIdsForAlbum(albumIdArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -399,12 +405,15 @@ interface NativeSyncApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$separatedMessageChannelSuffix", codec, taskQueue) if (api != null) { channel.setMessageHandler { _, reply -> - val wrapped: List = try { - listOf(api.getAlbums()) - } catch (exception: Throwable) { - MessagesPigeonUtils.wrapError(exception) + api.getAlbums{ result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -417,12 +426,15 @@ interface NativeSyncApi { val args = message as List val albumIdArg = args[0] as String val timestampArg = args[1] as Long - val wrapped: List = try { - listOf(api.getAssetsCountSince(albumIdArg, timestampArg)) - } catch (exception: Throwable) { - MessagesPigeonUtils.wrapError(exception) + api.getAssetsCountSince(albumIdArg, timestampArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -435,12 +447,15 @@ interface NativeSyncApi { val args = message as List val albumIdArg = args[0] as String val updatedTimeCondArg = args[1] as Long? - val wrapped: List = try { - listOf(api.getAssetsForAlbum(albumIdArg, updatedTimeCondArg)) - } catch (exception: Throwable) { - MessagesPigeonUtils.wrapError(exception) + api.getAssetsForAlbum(albumIdArg, updatedTimeCondArg) { result: Result> -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt index 5deacc30db..84922940cc 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl26.kt @@ -18,7 +18,7 @@ class NativeSyncApiImpl26(context: Context) : NativeSyncApiImplBase(context), Na // No-op for Android 10 and below } - override fun getMediaChanges(): SyncDelta { - throw IllegalStateException("Method not supported on this Android version.") - } + override fun getMediaChanges(callback: (Result) -> Unit) = + callback(Result.failure(IllegalStateException("Method not supported on this Android version."))) + } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt index 052032e143..be120a8bbf 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImpl30.kt @@ -5,6 +5,7 @@ import android.os.Build import android.provider.MediaStore import androidx.annotation.RequiresApi import androidx.annotation.RequiresExtension +import app.alextran.immich.dispatch import kotlinx.serialization.json.Json @RequiresApi(Build.VERSION_CODES.Q) @@ -47,7 +48,12 @@ class NativeSyncApiImpl30(context: Context) : NativeSyncApiImplBase(context), Na } } - override fun getMediaChanges(): SyncDelta { + override fun getMediaChanges(callback: (Result) -> Unit) = + dispatch(callback = callback) { + getMediaChanges() + } + + private fun getMediaChanges(): SyncDelta { val genMap = getSavedGenerationMap() val currentVolumes = MediaStore.getExternalVolumeNames(ctx) val changed = mutableListOf() diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 868f3c6cdd..81b177fb0e 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -7,6 +7,7 @@ import android.database.Cursor import android.provider.MediaStore import android.util.Base64 import androidx.core.database.getStringOrNull +import app.alextran.immich.dispatch import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -145,7 +146,10 @@ open class NativeSyncApiImplBase(context: Context) { } } - fun getAlbums(): List { + fun getAlbums(callback: (Result>) -> Unit) = + dispatch(callback = callback) { getAlbums() } + + private fun getAlbums(): List { val albums = mutableListOf() val albumsCount = mutableMapOf() @@ -192,7 +196,10 @@ open class NativeSyncApiImplBase(context: Context) { .sortedBy { it.id } } - fun getAssetIdsForAlbum(albumId: String): List { + fun getAssetIdsForAlbum(albumId: String, callback: (Result>) -> Unit) = + dispatch(callback = callback) { getAssetIdsForAlbum(albumId); } + + private fun getAssetIdsForAlbum(albumId: String): List { val projection = arrayOf(MediaStore.MediaColumns._ID) return getCursor( @@ -208,15 +215,23 @@ open class NativeSyncApiImplBase(context: Context) { } ?: emptyList() } - fun getAssetsCountSince(albumId: String, timestamp: Long): Long = + fun getAssetsCountSince(albumId: String, timestamp: Long, callback: (Result) -> Unit) = + dispatch(callback = callback) { getAssetsCountSince(albumId, timestamp) } + + private fun getAssetsCountSince(albumId: String, timestamp: Long): Long = getCursor( MediaStore.VOLUME_EXTERNAL, "$BUCKET_SELECTION AND ${MediaStore.Files.FileColumns.DATE_ADDED} > ? AND $MEDIA_SELECTION", arrayOf(albumId, timestamp.toString(), *MEDIA_SELECTION_ARGS), )?.use { cursor -> cursor.count.toLong() } ?: 0L + fun getAssetsForAlbum( + albumId: String, + updatedTimeCond: Long?, + callback: (Result>) -> Unit + ) = dispatch(callback = callback) { getAssetsForAlbum(albumId, updatedTimeCond) } - fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List { + private fun getAssetsForAlbum(albumId: String, updatedTimeCond: Long?): List { var selection = "$BUCKET_SELECTION AND $MEDIA_SELECTION" val selectionArgs = mutableListOf(albumId, *MEDIA_SELECTION_ARGS) @@ -254,7 +269,7 @@ open class NativeSyncApiImplBase(context: Context) { }.awaitAll() callback(Result.success(results)) - } catch (e: CancellationException) { + } catch (_: CancellationException) { callback( Result.failure( FlutterError( diff --git a/mobile/pigeon/background_worker_api.dart b/mobile/pigeon/background_worker_api.dart index 6f6c781de2..dd67becf4e 100644 --- a/mobile/pigeon/background_worker_api.dart +++ b/mobile/pigeon/background_worker_api.dart @@ -20,10 +20,13 @@ class BackgroundWorkerSettings { @HostApi() abstract class BackgroundWorkerFgHostApi { + @async void enable(); + @async void configure(BackgroundWorkerSettings settings); + @async void disable(); } diff --git a/mobile/pigeon/background_worker_lock_api.dart b/mobile/pigeon/background_worker_lock_api.dart index 44c5220367..0d6eea3a38 100644 --- a/mobile/pigeon/background_worker_lock_api.dart +++ b/mobile/pigeon/background_worker_lock_api.dart @@ -11,7 +11,9 @@ import 'package:pigeon/pigeon.dart'; ) @HostApi() abstract class BackgroundWorkerLockApi { + @async void lock(); + @async void unlock(); } diff --git a/mobile/pigeon/connectivity_api.dart b/mobile/pigeon/connectivity_api.dart index c5677ee20e..f1f3bab8a7 100644 --- a/mobile/pigeon/connectivity_api.dart +++ b/mobile/pigeon/connectivity_api.dart @@ -15,6 +15,7 @@ enum NetworkCapability { cellular, wifi, vpn, unmetered } @HostApi() abstract class ConnectivityApi { + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) List getCapabilities(); } diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index ac08a68ca3..d9118e791c 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -83,6 +83,7 @@ class HashResult { abstract class NativeSyncApi { bool shouldFullSync(); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) SyncDelta getMediaChanges(); @@ -90,15 +91,19 @@ abstract class NativeSyncApi { void clearSyncCheckpoint(); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) List getAssetIdsForAlbum(String albumId); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) List getAlbums(); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) int getAssetsCountSince(String albumId, int timestamp); + @async @TaskQueue(type: TaskQueueType.serialBackgroundThread) List getAssetsForAlbum(String albumId, {int? updatedTimeCond});