diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt index 034f5ee72e..4383b3098d 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt @@ -9,6 +9,7 @@ import app.alextran.immich.background.BackgroundWorkerFgHostApi import app.alextran.immich.background.BackgroundWorkerLockApi import app.alextran.immich.connectivity.ConnectivityApi import app.alextran.immich.connectivity.ConnectivityApiImpl +import app.alextran.immich.core.ImmichPlugin import app.alextran.immich.images.ThumbnailApi import app.alextran.immich.images.ThumbnailsImpl import app.alextran.immich.sync.NativeSyncApi @@ -42,6 +43,14 @@ class MainActivity : FlutterFragmentActivity() { flutterEngine.plugins.add(BackgroundServicePlugin()) flutterEngine.plugins.add(HttpSSLOptionsPlugin()) flutterEngine.plugins.add(backgroundEngineLockImpl) + flutterEngine.plugins.add(nativeSyncApiImpl) + } + + fun cancelPlugins(flutterEngine: FlutterEngine) { + val nativeApi = + flutterEngine.plugins.get(NativeSyncApiImpl26::class.java) as ImmichPlugin? + ?: flutterEngine.plugins.get(NativeSyncApiImpl30::class.java) as ImmichPlugin? + nativeApi?.detachFromEngine() } } } 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 504267a4e5..b11b53bcde 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,12 +2,13 @@ package app.alextran.immich.background import android.content.Context import android.util.Log +import app.alextran.immich.core.ImmichPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin import java.util.concurrent.atomic.AtomicInteger private const val TAG = "BackgroundEngineLock" -class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterPlugin { +class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, ImmichPlugin() { private val ctx: Context = context.applicationContext companion object { @@ -41,12 +42,14 @@ class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterP } override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + super.onAttachedToEngine(binding) checkAndEnforceBackgroundLock(binding.applicationContext) engineCount.incrementAndGet() Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + super.onDetachedFromEngine(binding) 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.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt index e59cee2c16..7dce1f6edf 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 @@ -190,6 +190,9 @@ class BackgroundWorker(context: Context, params: WorkerParameters) : private fun complete(success: Result) { Log.d(TAG, "About to complete BackupWorker with result: $success") isComplete = true + if (engine != null) { + MainActivity.cancelPlugins(engine!!) + } engine?.destroy() engine = null flutterApi = null diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt new file mode 100644 index 0000000000..4cc131b058 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt @@ -0,0 +1,29 @@ +package app.alextran.immich.core + +import androidx.annotation.CallSuper +import io.flutter.embedding.engine.plugins.FlutterPlugin + +abstract class ImmichPlugin : FlutterPlugin { + private var detached: Boolean = false; + + @CallSuper + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + detached = false; + } + + fun detachFromEngine() { + detached = true + } + + @CallSuper + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + detachFromEngine() + } + + fun completeWhenActive(callback: (T) -> Unit, value: T) { + if (detached) { + return; + } + callback(value); + } +} 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..0ea86bb10d 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.core.ImmichPlugin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -27,7 +28,7 @@ sealed class AssetResult { } @SuppressLint("InlinedApi") -open class NativeSyncApiImplBase(context: Context) { +open class NativeSyncApiImplBase(context: Context) : ImmichPlugin() { private val ctx: Context = context.applicationContext private var hashTask: Job? = null @@ -237,7 +238,7 @@ open class NativeSyncApiImplBase(context: Context) { callback: (Result>) -> Unit ) { if (assetIds.isEmpty()) { - callback(Result.success(emptyList())) + completeWhenActive(callback, Result.success(emptyList())) return } @@ -253,10 +254,10 @@ open class NativeSyncApiImplBase(context: Context) { } }.awaitAll() - callback(Result.success(results)) + completeWhenActive(callback, Result.success(results)) } catch (e: CancellationException) { - callback( - Result.failure( + completeWhenActive( + callback, Result.failure( FlutterError( HASHING_CANCELLED_CODE, "Hashing operation was cancelled", @@ -265,7 +266,7 @@ open class NativeSyncApiImplBase(context: Context) { ) ) } catch (e: Exception) { - callback(Result.failure(e)) + completeWhenActive(callback, Result.failure(e)) } } }