mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 04:20:00 +03:00
Avoid scheduling alarm until network is back
This commit is contained in:
parent
43c24e55ab
commit
3ff475af7a
3 changed files with 93 additions and 7 deletions
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.SupervisorJob
|
|||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.isTokenError
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
|
@ -86,17 +87,26 @@ abstract class SyncService : Service() {
|
|||
val isInit = initialize(intent)
|
||||
if (isInit) {
|
||||
periodic = intent?.getBooleanExtra(EXTRA_PERIODIC, false) ?: false
|
||||
Timber.i("## Sync: command received, periodic: $periodic")
|
||||
// default is syncing
|
||||
doSyncIfNotAlreadyRunning()
|
||||
val onNetworkBack = intent?.getBooleanExtra(EXTRA_NETWORK_BACK_RESTART, false) ?: false
|
||||
Timber.d("## Sync: command received, periodic: $periodic networkBack: $onNetworkBack")
|
||||
if (onNetworkBack && !backgroundDetectionObserver.isInBackground) {
|
||||
// the restart after network occurs while the app is in foreground
|
||||
// so just stop. It will be restarted when entering background
|
||||
preventReschedule = true
|
||||
stopMe()
|
||||
} else {
|
||||
// default is syncing
|
||||
doSyncIfNotAlreadyRunning()
|
||||
}
|
||||
} else {
|
||||
Timber.i("## Sync: Failed to initialize service")
|
||||
Timber.d("## Sync: Failed to initialize service")
|
||||
stopMe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return START_STICKY
|
||||
// It's ok to be not sticky because we will explicitly start it again on the next alarm?
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -107,7 +117,7 @@ abstract class SyncService : Service() {
|
|||
isRunning.set(false)
|
||||
// Cancelling the context will trigger the catch close the doSync try
|
||||
serviceScope.coroutineContext.cancelChildren()
|
||||
if (!preventReschedule && periodic && sessionId != null) {
|
||||
if (!preventReschedule && periodic && sessionId != null && backgroundDetectionObserver.isInBackground) {
|
||||
Timber.d("## Sync: Reschedule service in $syncDelaySeconds sec")
|
||||
onRescheduleAsked(sessionId ?: "", false, syncTimeoutSeconds, syncDelaySeconds)
|
||||
}
|
||||
|
@ -140,6 +150,7 @@ abstract class SyncService : Service() {
|
|||
Timber.v("## Sync: Execute sync request with timeout $syncTimeoutSeconds seconds")
|
||||
val params = SyncTask.Params(syncTimeoutSeconds * 1000L)
|
||||
try {
|
||||
// never do that in foreground, let the syncThread work
|
||||
syncTask.execute(params)
|
||||
// Start sync if we were doing an initial sync and the syncThread is not launched yet
|
||||
if (isInitialSync && session.getSyncState() == SyncState.Idle) {
|
||||
|
@ -153,6 +164,12 @@ abstract class SyncService : Service() {
|
|||
// no need to retry
|
||||
preventReschedule = true
|
||||
}
|
||||
if (throwable is Failure.NetworkConnection) {
|
||||
// Network is off, no need to reschedule endless alarms :/
|
||||
preventReschedule = true
|
||||
// Instead start a work to restart background sync when network is back
|
||||
onNetworkError(sessionId ?: "", isInitialSync, syncTimeoutSeconds, syncDelaySeconds)
|
||||
}
|
||||
// JobCancellation could be caught here when onDestroy cancels the coroutine context
|
||||
if (isRunning.get()) stopMe()
|
||||
}
|
||||
|
@ -189,6 +206,8 @@ abstract class SyncService : Service() {
|
|||
|
||||
abstract fun onRescheduleAsked(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int)
|
||||
|
||||
abstract fun onNetworkError(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int)
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
@ -198,6 +217,7 @@ abstract class SyncService : Service() {
|
|||
const val EXTRA_TIMEOUT_SECONDS = "EXTRA_TIMEOUT_SECONDS"
|
||||
const val EXTRA_DELAY_SECONDS = "EXTRA_DELAY_SECONDS"
|
||||
const val EXTRA_PERIODIC = "EXTRA_PERIODIC"
|
||||
const val EXTRA_NETWORK_BACK_RESTART = "EXTRA_NETWORK_BACK_RESTART"
|
||||
|
||||
const val ACTION_STOP = "ACTION_STOP"
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ object FcmHelper {
|
|||
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
|
||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY -> {
|
||||
// we rely on periodic worker
|
||||
Timber.i("## Sync: Work scheduled to periodically sync")
|
||||
Timber.i("## Sync: Work scheduled to periodically sync in ${vectorPreferences.backgroundSyncDelay()} sec")
|
||||
activeSessionHolder
|
||||
.getSafeActiveSession()
|
||||
?.startAutomaticBackgroundSync(
|
||||
|
|
|
@ -21,11 +21,21 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.Data
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkRequest
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.vectorComponent
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import org.matrix.android.sdk.internal.session.sync.job.SyncService
|
||||
import timber.log.Timber
|
||||
|
||||
class VectorSyncService : SyncService() {
|
||||
|
||||
|
@ -48,6 +58,16 @@ class VectorSyncService : SyncService() {
|
|||
}
|
||||
}
|
||||
|
||||
fun newPeriodicNetworkBackIntent(context: Context, sessionId: String, timeoutSeconds: Int, delayInSeconds: Int): Intent {
|
||||
return Intent(context, VectorSyncService::class.java).also {
|
||||
it.putExtra(EXTRA_SESSION_ID, sessionId)
|
||||
it.putExtra(EXTRA_TIMEOUT_SECONDS, timeoutSeconds)
|
||||
it.putExtra(EXTRA_PERIODIC, true)
|
||||
it.putExtra(EXTRA_DELAY_SECONDS, delayInSeconds)
|
||||
it.putExtra(EXTRA_NETWORK_BACK_RESTART, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopIntent(context: Context): Intent {
|
||||
return Intent(context, VectorSyncService::class.java).also {
|
||||
it.action = ACTION_STOP
|
||||
|
@ -76,6 +96,28 @@ class VectorSyncService : SyncService() {
|
|||
reschedule(sessionId, timeout, delay)
|
||||
}
|
||||
|
||||
override fun onNetworkError(sessionId: String, isInitialSync: Boolean, timeout: Int, delay: Int) {
|
||||
Timber.d("## Sync: A network error occured during sync")
|
||||
val uploadWorkRequest: WorkRequest =
|
||||
OneTimeWorkRequestBuilder<RestartWhenNetworkOn>()
|
||||
.setInputData(Data.Builder()
|
||||
.putString("sessionId", sessionId)
|
||||
.putInt("timeout", timeout)
|
||||
.putInt("delay", delay)
|
||||
.build()
|
||||
)
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
Timber.d("## Sync: Schedule a work to restart service when network will be on")
|
||||
WorkManager
|
||||
.getInstance(applicationContext)
|
||||
.enqueue(uploadWorkRequest)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
removeForegroundNotification()
|
||||
super.onDestroy()
|
||||
|
@ -100,4 +142,28 @@ class VectorSyncService : SyncService() {
|
|||
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
}
|
||||
}
|
||||
|
||||
class RestartWhenNetworkOn(appContext: Context, workerParams: WorkerParameters) :
|
||||
Worker(appContext, workerParams) {
|
||||
override fun doWork(): Result {
|
||||
val sessionId = inputData.getString("sessionId") ?: return Result.failure()
|
||||
val timeout = inputData.getInt("timeout", 6)
|
||||
val delay = inputData.getInt("delay", 60)
|
||||
|
||||
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent.getForegroundService(applicationContext, 0, newPeriodicNetworkBackIntent(applicationContext, sessionId, timeout, delay), 0)
|
||||
} else {
|
||||
PendingIntent.getService(applicationContext, 0, newPeriodicNetworkBackIntent(applicationContext, sessionId, timeout, delay), 0)
|
||||
}
|
||||
val firstMillis = System.currentTimeMillis() + delay * 1000L
|
||||
val alarmMgr = getSystemService<AlarmManager>(applicationContext, AlarmManager::class.java)!!
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
} else {
|
||||
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
}
|
||||
// Indicate whether the work finished successfully with the Result
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue