diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index af00aac413..36a967da6c 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - - + + - - + + + diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/AppClearedService.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/AppClearedService.kt deleted file mode 100644 index bbdaa27f5f..0000000000 --- a/mobile/android/app/src/main/kotlin/com/example/mobile/AppClearedService.kt +++ /dev/null @@ -1,25 +0,0 @@ -package app.alextran.immich - -import android.app.Service -import android.content.Intent -import android.os.IBinder - -/** - * Catches the event when either the system or the user kills the app - * (does not apply on force close!) - */ -class AppClearedService() : Service() { - - override fun onBind(intent: Intent): IBinder? { - return null - } - - override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - return START_NOT_STICKY; - } - - override fun onTaskRemoved(rootIntent: Intent) { - ContentObserverWorker.workManagerAppClearedWorkaround(applicationContext) - stopSelf(); - } -} \ No newline at end of file diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt index bebaa579be..3cb231eaf6 100644 --- a/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt +++ b/mobile/android/app/src/main/kotlin/com/example/mobile/BackgroundServicePlugin.kt @@ -10,7 +10,7 @@ import io.flutter.plugin.common.MethodChannel * Android plugin for Dart `BackgroundService` * * Receives messages/method calls from the foreground Dart side to manage - * the background service, e.g. start (enqueue), stop (cancel) + * the background service, e.g. start (enqueue), stop (cancel) */ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { @@ -38,14 +38,15 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { val ctx = context!! - when(call.method) { + when (call.method) { "enable" -> { val args = call.arguments>()!! ctx.getSharedPreferences(BackupWorker.SHARED_PREF_NAME, Context.MODE_PRIVATE) - .edit() - .putLong(BackupWorker.SHARED_PREF_CALLBACK_KEY, args.get(0) as Long) - .putString(BackupWorker.SHARED_PREF_NOTIFICATION_TITLE, args.get(1) as String) - .apply() + .edit() + .putBoolean(ContentObserverWorker.SHARED_PREF_SERVICE_ENABLED, true) + .putLong(BackupWorker.SHARED_PREF_CALLBACK_KEY, args.get(0) as Long) + .putString(BackupWorker.SHARED_PREF_NOTIFICATION_TITLE, args.get(1) as String) + .apply() ContentObserverWorker.enable(ctx, immediate = args.get(2) as Boolean) result.success(true) } @@ -54,7 +55,7 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler { val requireUnmeteredNetwork = args.get(0) as Boolean val requireCharging = args.get(1) as Boolean ContentObserverWorker.configureWork(ctx, requireUnmeteredNetwork, requireCharging) - result.success(true) + result.success(true) } "disable" -> { ContentObserverWorker.disable(ctx) diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/ContentObserverWorker.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/ContentObserverWorker.kt index ecbec640fa..a58ea14518 100644 --- a/mobile/android/app/src/main/kotlin/com/example/mobile/ContentObserverWorker.kt +++ b/mobile/android/app/src/main/kotlin/com/example/mobile/ContentObserverWorker.kt @@ -46,9 +46,6 @@ class ContentObserverWorker(ctx: Context, params: WorkerParameters) : Worker(ctx * @param context Android Context */ fun enable(context: Context, immediate: Boolean = false) { - // migration to remove any old active background task - WorkManager.getInstance(context).cancelUniqueWork("immich/photoListener") - enqueueObserverWorker(context, ExistingWorkPolicy.KEEP) Log.d(TAG, "enabled ContentObserverWorker") if (immediate) { @@ -123,8 +120,10 @@ class ContentObserverWorker(ctx: Context, params: WorkerParameters) : Worker(ctx WorkManager.getInstance(context).enqueueUniqueWork(TASK_NAME_OBSERVER, policy, work) } - private fun startBackupWorker(context: Context, delayMilliseconds: Long) { + fun startBackupWorker(context: Context, delayMilliseconds: Long) { val sp = context.getSharedPreferences(BackupWorker.SHARED_PREF_NAME, Context.MODE_PRIVATE) + if (!sp.getBoolean(SHARED_PREF_SERVICE_ENABLED, false)) + return val requireWifi = sp.getBoolean(SHARED_PREF_REQUIRE_WIFI, true) val requireCharging = sp.getBoolean(SHARED_PREF_REQUIRE_CHARGING, false) BackupWorker.enqueueBackupWorker(context, requireWifi, requireCharging, delayMilliseconds) diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/ImmichApp.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/ImmichApp.kt new file mode 100644 index 0000000000..86b82d2be9 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/com/example/mobile/ImmichApp.kt @@ -0,0 +1,19 @@ +package app.alextran.immich + +import android.app.Application +import androidx.work.Configuration +import androidx.work.WorkManager + +class ImmichApp : Application() { + override fun onCreate() { + super.onCreate() + val config = Configuration.Builder().build() + WorkManager.initialize(this, config) + // always start BackupWorker after WorkManager init; this fixes the following bug: + // After the process is killed (by user or system), the first trigger (taking a new picture) is lost. + // Thus, the BackupWorker is not started. If the system kills the process after each initialization + // (because of low memory etc.), the backup is never performed. + // As a workaround, we also run a backup check when initializing the application + ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0) + } +} \ No newline at end of file diff --git a/mobile/android/app/src/main/kotlin/com/example/mobile/MainActivity.kt b/mobile/android/app/src/main/kotlin/com/example/mobile/MainActivity.kt index 2e6372231d..5df36cb18f 100644 --- a/mobile/android/app/src/main/kotlin/com/example/mobile/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/com/example/mobile/MainActivity.kt @@ -5,21 +5,11 @@ import io.flutter.embedding.engine.FlutterEngine import android.os.Bundle import android.content.Intent -class MainActivity: FlutterActivity() { +class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) - flutterEngine.getPlugins().add(BackgroundServicePlugin()) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - try { - startService(Intent(getBaseContext(), AppClearedService::class.java)); - } catch (e: Exception) { - // startService must not be called when app is in background (crashes app) - // there is nothing we can do - } + flutterEngine.plugins.add(BackgroundServicePlugin()) } }