1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

Revert "feat(android) Check server is reachable before starting background backup (#8594)" (#8958)

This reverts commit 71b6d8b569.
This commit is contained in:
Alex 2024-04-20 12:15:26 -05:00 committed by GitHub
parent 40931b5668
commit 2dd7c13b88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 32 additions and 114 deletions

View file

@ -90,7 +90,7 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
implementation "androidx.work:work-runtime:$work_version" implementation "androidx.work:work-runtime-ktx:$work_version"
implementation "androidx.concurrent:concurrent-futures:$concurrent_version" implementation "androidx.concurrent:concurrent-futures:$concurrent_version"
implementation "com.google.guava:guava:$guava_version" implementation "com.google.guava:guava:$guava_version"
implementation "com.github.bumptech.glide:glide:$glide_version" implementation "com.github.bumptech.glide:glide:$glide_version"

View file

@ -52,7 +52,6 @@ class BackgroundServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
.putBoolean(ContentObserverWorker.SHARED_PREF_SERVICE_ENABLED, true) .putBoolean(ContentObserverWorker.SHARED_PREF_SERVICE_ENABLED, true)
.putLong(BackupWorker.SHARED_PREF_CALLBACK_KEY, args.get(0) as Long) .putLong(BackupWorker.SHARED_PREF_CALLBACK_KEY, args.get(0) as Long)
.putString(BackupWorker.SHARED_PREF_NOTIFICATION_TITLE, args.get(1) as String) .putString(BackupWorker.SHARED_PREF_NOTIFICATION_TITLE, args.get(1) as String)
.putString(BackupWorker.SHARED_PREF_SERVER_URL, args.get(3) as String)
.apply() .apply()
ContentObserverWorker.enable(ctx, immediate = args.get(2) as Boolean) ContentObserverWorker.enable(ctx, immediate = args.get(2) as Boolean)
result.success(true) result.success(true)

View file

@ -11,8 +11,8 @@ import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.concurrent.futures.CallbackToFutureAdapter
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.concurrent.futures.ResolvableFuture
import androidx.work.BackoffPolicy import androidx.work.BackoffPolicy
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ForegroundInfo import androidx.work.ForegroundInfo
@ -30,16 +30,6 @@ import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterCallbackInformation import io.flutter.view.FlutterCallbackInformation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import java.io.IOException
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/** /**
@ -52,6 +42,7 @@ import java.util.concurrent.TimeUnit
*/ */
class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ctx, params), MethodChannel.MethodCallHandler { class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ctx, params), MethodChannel.MethodCallHandler {
private val resolvableFuture = ResolvableFuture.create<Result>()
private var engine: FlutterEngine? = null private var engine: FlutterEngine? = null
private lateinit var backgroundChannel: MethodChannel private lateinit var backgroundChannel: MethodChannel
private val notificationManager = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private val notificationManager = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -61,80 +52,35 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
private var notificationDetailBuilder: NotificationCompat.Builder? = null private var notificationDetailBuilder: NotificationCompat.Builder? = null
private var fgFuture: ListenableFuture<Void>? = null private var fgFuture: ListenableFuture<Void>? = null
private val job = Job()
private lateinit var completer: CallbackToFutureAdapter.Completer<Result>
private val resolvableFuture = CallbackToFutureAdapter.getFuture { completer ->
this.completer = completer
null
}
init {
resolvableFuture.addListener(
Runnable {
if (resolvableFuture.isCancelled) {
job.cancel()
}
},
taskExecutor.serialTaskExecutor
)
}
override fun startWork(): ListenableFuture<ListenableWorker.Result> { override fun startWork(): ListenableFuture<ListenableWorker.Result> {
Log.d(TAG, "startWork") Log.d(TAG, "startWork")
val ctx = applicationContext val ctx = applicationContext
val prefs = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
prefs.getString(SHARED_PREF_SERVER_URL, null) if (!flutterLoader.initialized()) {
?.takeIf { it.isNotEmpty() } flutterLoader.startInitialization(ctx)
?.let { serverUrl -> doCoroutineWork(serverUrl) }
?: doWork()
return resolvableFuture
}
/**
* This function is used to check if server URL is reachable before starting the backup work.
* Check must be done in a background to avoid blocking the main thread.
*/
private fun doCoroutineWork(serverUrl : String) {
CoroutineScope(Dispatchers.Default + job).launch {
val isReachable = isUrlReachableHttp(serverUrl)
withContext(Dispatchers.Main) {
if (isReachable) {
doWork()
} else {
// Fail when the URL is not reachable
completer.set(Result.failure())
}
}
} }
} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create a Notification channel if necessary
createChannel()
}
if (isIgnoringBatteryOptimizations) {
// normal background services can only up to 10 minutes
// foreground services are allowed to run indefinitely
// requires battery optimizations to be disabled (either manually by the user
// or by the system learning that immich is important to the user)
val title = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
.getString(SHARED_PREF_NOTIFICATION_TITLE, NOTIFICATION_DEFAULT_TITLE)!!
showInfo(getInfoBuilder(title, indeterminate=true).build())
}
engine = FlutterEngine(ctx)
private fun doWork() { flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
Log.d(TAG, "doWork") runDart()
val ctx = applicationContext }
if (!flutterLoader.initialized()) { return resolvableFuture
flutterLoader.startInitialization(ctx)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create a Notification channel if necessary
createChannel()
}
if (isIgnoringBatteryOptimizations) {
// normal background services can only up to 10 minutes
// foreground services are allowed to run indefinitely
// requires battery optimizations to be disabled (either manually by the user
// or by the system learning that immich is important to the user)
val title = ctx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
.getString(SHARED_PREF_NOTIFICATION_TITLE, NOTIFICATION_DEFAULT_TITLE)!!
showInfo(getInfoBuilder(title, indeterminate=true).build())
}
engine = FlutterEngine(ctx)
flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
runDart()
}
} }
/** /**
@ -193,7 +139,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
engine = null engine = null
if (result != null) { if (result != null) {
Log.d(TAG, "stopEngine result=${result}") Log.d(TAG, "stopEngine result=${result}")
this.completer.set(result) resolvableFuture.set(result)
} }
waitOnSetForegroundAsync() waitOnSetForegroundAsync()
} }
@ -324,14 +270,13 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
const val SHARED_PREF_CALLBACK_KEY = "callbackDispatcherHandle" const val SHARED_PREF_CALLBACK_KEY = "callbackDispatcherHandle"
const val SHARED_PREF_NOTIFICATION_TITLE = "notificationTitle" const val SHARED_PREF_NOTIFICATION_TITLE = "notificationTitle"
const val SHARED_PREF_LAST_CHANGE = "lastChange" const val SHARED_PREF_LAST_CHANGE = "lastChange"
const val SHARED_PREF_SERVER_URL = "serverUrl"
private const val TASK_NAME_BACKUP = "immich/BackupWorker" private const val TASK_NAME_BACKUP = "immich/BackupWorker"
private const val NOTIFICATION_CHANNEL_ID = "immich/backgroundService" private const val NOTIFICATION_CHANNEL_ID = "immich/backgroundService"
private const val NOTIFICATION_CHANNEL_ERROR_ID = "immich/backgroundServiceError" private const val NOTIFICATION_CHANNEL_ERROR_ID = "immich/backgroundServiceError"
private const val NOTIFICATION_DEFAULT_TITLE = "Immich" private const val NOTIFICATION_DEFAULT_TITLE = "Immich"
private const val NOTIFICATION_ID = 1 private const val NOTIFICATION_ID = 1
private const val NOTIFICATION_ERROR_ID = 2 private const val NOTIFICATION_ERROR_ID = 2
private const val NOTIFICATION_DETAIL_ID = 3 private const val NOTIFICATION_DETAIL_ID = 3
private const val ONE_MINUTE = 60000L private const val ONE_MINUTE = 60000L
@ -359,7 +304,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
val workInfoList = workInfoFuture.get(1000, TimeUnit.MILLISECONDS) val workInfoList = workInfoFuture.get(1000, TimeUnit.MILLISECONDS)
if (workInfoList != null) { if (workInfoList != null) {
for (workInfo in workInfoList) { for (workInfo in workInfoList) {
if (workInfo.state == WorkInfo.State.ENQUEUED) { if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
val workRequest = buildWorkRequest(requireWifi, requireCharging) val workRequest = buildWorkRequest(requireWifi, requireCharging)
wm.enqueueUniqueWork(TASK_NAME_BACKUP, ExistingWorkPolicy.REPLACE, workRequest) wm.enqueueUniqueWork(TASK_NAME_BACKUP, ExistingWorkPolicy.REPLACE, workRequest)
Log.d(TAG, "updateBackupWorker updated BackupWorker constraints") Log.d(TAG, "updateBackupWorker updated BackupWorker constraints")
@ -414,27 +359,4 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
} }
} }
private const val TAG = "BackupWorker" private const val TAG = "BackupWorker"
/**
* Check if the given URL is reachable via HTTP
*/
suspend fun isUrlReachableHttp(url: String, timeoutMillis: Long = 5000L): Boolean {
return withTimeoutOrNull(timeoutMillis) {
var httpURLConnection: HttpURLConnection? = null
try {
httpURLConnection = (URL(url).openConnection() as HttpURLConnection).apply {
requestMethod = "HEAD"
connectTimeout = timeoutMillis.toInt()
readTimeout = timeoutMillis.toInt()
}
httpURLConnection.connect()
httpURLConnection.responseCode == HttpURLConnection.HTTP_OK
} catch (e: Exception) {
Log.e(TAG, "Failed to reach server URL: $e")
false
} finally {
httpURLConnection?.disconnect()
}
} == true
}

View file

@ -1,7 +1,7 @@
buildscript { buildscript {
ext.kotlin_version = '1.8.22' ext.kotlin_version = '1.8.20'
ext.kotlin_coroutines_version = '1.7.1' ext.kotlin_coroutines_version = '1.7.1'
ext.work_version = '2.9.0' ext.work_version = '2.7.1'
ext.concurrent_version = '1.1.0' ext.concurrent_version = '1.1.0'
ext.guava_version = '33.0.0-android' ext.guava_version = '33.0.0-android'
ext.glide_version = '4.14.2' ext.glide_version = '4.14.2'

View file

@ -20,7 +20,6 @@ import 'package:immich_mobile/shared/models/store.dart';
import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/backup_progress.dart';
import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/diff.dart';
import 'package:immich_mobile/utils/url_helper.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:path_provider_ios/path_provider_ios.dart'; import 'package:path_provider_ios/path_provider_ios.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@ -69,10 +68,8 @@ class BackgroundService {
final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!; final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!;
final String title = final String title =
"backup_background_service_default_notification".tr(); "backup_background_service_default_notification".tr();
final bool ok = await _foregroundChannel.invokeMethod( final bool ok = await _foregroundChannel
'enable', .invokeMethod('enable', [callback.toRawHandle(), title, immediate]);
[callback.toRawHandle(), title, immediate, getServerUrl()],
);
return ok; return ok;
} catch (error) { } catch (error) {
return false; return false;