initial commit of a permanent GuardService for fdroid background syncing
This commit is contained in:
parent
d261dd705d
commit
e14fb16db2
|
@ -29,7 +29,7 @@ import javax.inject.Inject
|
|||
@MatrixScope
|
||||
internal class BackgroundDetectionObserver @Inject constructor() : LifecycleObserver {
|
||||
|
||||
var isInBackground: Boolean = false
|
||||
var isInBackground: Boolean = true
|
||||
private set
|
||||
|
||||
private
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="im.vector.app">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<!--
|
||||
Required for long polling account synchronisation in background.
|
||||
If not present ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent action won't work
|
||||
|
@ -24,6 +26,11 @@
|
|||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<service
|
||||
android:name=".fdroid.service.GuardService"
|
||||
android:exported="false"
|
||||
tools:ignore="Instantiatable" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -19,17 +19,35 @@ package im.vector.app.fdroid
|
|||
import android.content.Context
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
|
||||
import im.vector.app.fdroid.service.GuardService
|
||||
import im.vector.app.features.settings.BackgroundSyncMode
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import timber.log.Timber
|
||||
import android.content.Intent
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.matrix.android.sdk.internal.session.sync.job.SyncService
|
||||
|
||||
object BackgroundSyncStarter {
|
||||
fun start(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
|
||||
if (vectorPreferences.areNotificationEnabledForDevice()) {
|
||||
val activeSession = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
|
||||
val intent = Intent(context, GuardService::class.java)
|
||||
if (vectorPreferences.getFdroidSyncBackgroundMode() == BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_REALTIME) {
|
||||
intent.putExtra(SyncService.EXTRA_SESSION_ID, activeSession.sessionId)
|
||||
} else {
|
||||
intent.putExtra(SyncService.EXTRA_SESSION_ID, "") // this assures the GuardService runs, but will not start VectorSyncService
|
||||
}
|
||||
intent.putExtra(SyncService.EXTRA_DELAY_SECONDS, vectorPreferences.backgroundSyncDelay())
|
||||
try {
|
||||
Timber.i("## Sync: starting GuardService")
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
} catch (ex: Throwable) {
|
||||
Timber.e("## Sync: ERROR starting GuardService")
|
||||
}
|
||||
|
||||
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
|
||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY -> {
|
||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY -> {
|
||||
// we rely on periodic worker
|
||||
Timber.i("## Sync: Work scheduled to periodically sync in ${vectorPreferences.backgroundSyncDelay()}s")
|
||||
activeSession.startAutomaticBackgroundSync(
|
||||
|
@ -43,7 +61,7 @@ object BackgroundSyncStarter {
|
|||
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.sessionId, vectorPreferences.backgroundSyncDelay())
|
||||
Timber.i("## Sync: Alarm scheduled to start syncing")
|
||||
}
|
||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED -> {
|
||||
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED -> {
|
||||
// we do nothing
|
||||
Timber.i("## Sync: background sync is disabled")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.app.fdroid.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import androidx.core.content.getSystemService
|
||||
import timber.log.Timber
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.vectorComponent
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import im.vector.app.core.services.VectorSyncService
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.matrix.android.sdk.internal.session.sync.job.SyncService
|
||||
import android.app.AlarmManager
|
||||
import android.os.Build
|
||||
import android.app.PendingIntent
|
||||
import im.vector.app.features.settings.BackgroundSyncMode
|
||||
|
||||
/**
|
||||
*/
|
||||
class GuardService : Service() {
|
||||
|
||||
private var timer = Timer()
|
||||
private var sessionId: String? = null
|
||||
private var running: Boolean = false
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Timber.i("## Sync: onStartCommand GuardService running:$running")
|
||||
if (running) {
|
||||
if (intent != null) {
|
||||
val liveLine = intent.getBooleanExtra(EXTRA_LIFELINE, false)
|
||||
if (liveLine) {//called from lifeLine?
|
||||
scheduleLifeLine()
|
||||
return START_STICKY
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
timer.cancel()
|
||||
} catch (ex: Exception) {
|
||||
}
|
||||
val notificationSubtitleRes = R.string.notification_listening_for_events
|
||||
val notification = notificationUtils.buildForegroundServiceNotification(notificationSubtitleRes, false)
|
||||
startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE + 1, notification)
|
||||
try {
|
||||
val sharedPref = getSharedPreferences(PREF_NAME_SESSION_ID, 0)
|
||||
var delayInSeconds = BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS
|
||||
if (intent != null) {
|
||||
sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID)
|
||||
delayInSeconds = intent.getIntExtra(SyncService.EXTRA_DELAY_SECONDS, BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS)
|
||||
val liveLine = intent.getBooleanExtra(EXTRA_LIFELINE, false)
|
||||
if (liveLine) {
|
||||
Timber.i("## Sync: GuardService restarted by liveLine")
|
||||
}
|
||||
if (sessionId.isNullOrEmpty()) {
|
||||
Timber.i("## Sync: GuardService getting sessionId from sharedPreferences")
|
||||
sessionId = sharedPref.getString(PREF_NAME_SESSION_ID, null)
|
||||
} else {
|
||||
Timber.i("## Sync: GuardService saving sessionId to sharedPreferences")
|
||||
val editor = sharedPref.edit()
|
||||
editor.putString(PREF_NAME_SESSION_ID, sessionId)
|
||||
editor.apply()
|
||||
}
|
||||
} else {
|
||||
Timber.i("## Sync: GuardService intent is null in GuardService, getting sessionId from sharedPreferences")
|
||||
sessionId = sharedPref.getString(PREF_NAME_SESSION_ID, null)
|
||||
}
|
||||
timer = Timer()
|
||||
timer.scheduleAtFixedRate(object : TimerTask() {
|
||||
override fun run() {
|
||||
if (sessionId.isNullOrEmpty()) {
|
||||
Timber.i("## Sync: timer still alive GuardService sessionId:nullOrEmpty")
|
||||
} else {
|
||||
Timber.i("## Sync: timer still alive GuardService sessionId:${sessionId}")
|
||||
try {
|
||||
val syncIntent = Intent(applicationContext, VectorSyncService::class.java)
|
||||
syncIntent.putExtra(SyncService.EXTRA_SESSION_ID, sessionId)
|
||||
syncIntent.putExtra(SyncService.EXTRA_PERIODIC, true)
|
||||
ContextCompat.startForegroundService(applicationContext, syncIntent)
|
||||
} catch (ex: Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, delayInSeconds * 1000L, delayInSeconds * 1000L)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e("## Sync: error in GuardService when creating timer")
|
||||
}
|
||||
if (!running) {
|
||||
scheduleLifeLine()
|
||||
}
|
||||
running = true
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
private lateinit var notificationUtils: NotificationUtils
|
||||
|
||||
override fun onCreate() {
|
||||
Timber.i("## Sync: onCreate GuardService")
|
||||
super.onCreate()
|
||||
notificationUtils = vectorComponent().notificationUtils()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Timber.i("## Sync: onDestroy GuardService")
|
||||
try {
|
||||
timer.cancel()
|
||||
} catch (ex: Exception) {
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun scheduleLifeLine() {
|
||||
Timber.d("## Sync: GuardService scheduleLifeLine")
|
||||
val intent = Intent(applicationContext, GuardService::class.java)
|
||||
intent.putExtra(EXTRA_LIFELINE, true)
|
||||
|
||||
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent.getForegroundService(this, 0, intent, 0)
|
||||
} else {
|
||||
PendingIntent.getService(this, 0, intent, 0)
|
||||
}
|
||||
val firstMillis = System.currentTimeMillis() + 60 * 1000L
|
||||
val alarmMgr = getSystemService<AlarmManager>()!!
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
} else {
|
||||
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pendingIntent)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREF_NAME_SESSION_ID = "GuardServiceLastActiveSessionId"
|
||||
const val EXTRA_LIFELINE = "GuardServiceLifeLine"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue