Loading app/src/main/AndroidManifest.xml +3 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,9 @@ tools:node="remove"> </provider> <service android:name=".ForegroundService"/> <service android:name=".ForegroundService" android:foregroundServiceType="dataSync" /> <!-- Remove the node added by AppAuth (remove only from net.openid.appauth library, not from our flavor manifest files) --> <activity android:name="net.openid.appauth.RedirectUriReceiverActivity" Loading app/src/main/kotlin/at/bitfire/davdroid/App.kt +1 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide accountsUpdatedListener.listen() // foreground service (possible workaround for devices which prevent DAVx5 from being started) ForegroundService.startIfActive(this) ForegroundService.startOrStop(this) // watch storage because low storage means synchronization is stopped storageLowReceiver.listen() Loading app/src/main/kotlin/at/bitfire/davdroid/ForegroundService.kt +30 −45 Original line number Diff line number Diff line Loading @@ -25,25 +25,6 @@ import dagger.hilt.components.SingletonComponent class ForegroundService : Service() { override fun onCreate() { super.onCreate() /* Call startForeground as soon as possible (must be within 5 seconds after the service has been created). If the foreground service shouldn't remain active (because the setting has been disabled), we'll immediately stop it with stopForeground() in onStartCommand(). */ val settingsIntent = Intent(this, AppSettingsActivity::class.java).apply { putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.FOREGROUND_SERVICE) } val builder = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_STATUS) .setSmallIcon(R.drawable.ic_foreground_notify) .setContentTitle(getString(R.string.foreground_service_notify_title)) .setContentText(getString(R.string.foreground_service_notify_text)) .setStyle(NotificationCompat.BigTextStyle()) .setContentIntent(PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) .setCategory(NotificationCompat.CATEGORY_STATUS) startForeground(NotificationUtils.NOTIFY_FOREGROUND, builder.build()) } companion object { @EntryPoint Loading @@ -52,13 +33,6 @@ class ForegroundService : Service() { fun settingsManager(): SettingsManager } /** * Starts/stops a foreground service, according to the app setting [Settings.FOREGROUND_SERVICE] * if [Settings.BATTERY_OPTIMIZATION] is enabled - meaning DAVx5 is whitelisted from optimization. */ const val ACTION_FOREGROUND = "foreground" /** * Whether the app is currently exempted from battery optimization. * @return true if battery optimization is not applied to the current app; false if battery optimization is applied Loading @@ -68,28 +42,34 @@ class ForegroundService : Service() { /** * Whether the foreground service is enabled (checked) in the app settings. * * @return true: foreground service enabled; false: foreground service not enabled */ fun foregroundServiceActivated(context: Context): Boolean { private fun shouldBeActive(context: Context): Boolean { val settingsManager = EntryPointAccessors.fromApplication(context, ForegroundServiceEntryPoint::class.java).settingsManager() return settingsManager.getBooleanOrNull(Settings.FOREGROUND_SERVICE) == true } /** * Starts the foreground service when enabled in the app settings and applicable. * Stops a potentially running foreground service when disabled in the app settings. */ fun startIfActive(context: Context) { if (foregroundServiceActivated(context)) { fun startOrStop(context: Context) { val foregroundServiceIntent = Intent(Intent.ACTION_DEFAULT, null, context, ForegroundService::class.java) if (shouldBeActive(context)) { if (batteryOptimizationWhitelisted(context)) { val serviceIntent = Intent(ACTION_FOREGROUND, null, context, ForegroundService::class.java) if (Build.VERSION.SDK_INT >= 26) // we now have 5 seconds to call Service.startForeground() [https://developer.android.com/about/versions/oreo/android-8.0-changes.html#back-all] context.startForegroundService(serviceIntent) // After the next call, we have 5 seconds to call startForeground() // [https://developer.android.com/about/versions/oreo/android-8.0-changes.html#back-all] context.startForegroundService(foregroundServiceIntent) else context.startService(serviceIntent) context.startService(foregroundServiceIntent) } else notifyBatteryOptimization(context) } } else try { context.stopService(foregroundServiceIntent) } catch (ignored: Exception) { } } private fun notifyBatteryOptimization(context: Context) { Loading @@ -111,19 +91,24 @@ class ForegroundService : Service() { } override fun onBind(intent: Intent?): Nothing? = null override fun onCreate() { super.onCreate() override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // Command is always ACTION_FOREGROUND → re-evaluate foreground setting if (foregroundServiceActivated(this)) // keep service open return START_STICKY else { // don't keep service active stopForeground(true) stopSelf() // Stop the service so that onCreate() will run again for the next command return START_NOT_STICKY val settingsIntent = Intent(this, AppSettingsActivity::class.java).apply { putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.FOREGROUND_SERVICE) } val builder = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_STATUS) .setSmallIcon(R.drawable.ic_foreground_notify) .setContentTitle(getString(R.string.foreground_service_notify_title)) .setContentText(getString(R.string.foreground_service_notify_text)) .setStyle(NotificationCompat.BigTextStyle()) .setContentIntent(PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) .setCategory(NotificationCompat.CATEGORY_STATUS) startForeground(NotificationUtils.NOTIFY_FOREGROUND, builder.build()) } override fun onBind(intent: Intent?): Nothing? = null override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY } No newline at end of file app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsActivity.kt +6 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,11 @@ import androidx.annotation.UiThread import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.getSystemService import androidx.preference.* import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import at.bitfire.cert4android.CustomCertStore import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.ForegroundService Loading Loading @@ -168,7 +172,7 @@ class AppSettingsActivity: AppCompatActivity() { isEnabled = settings.getBooleanOrNull(Settings.BATTERY_OPTIMIZATION) == true onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> settings.putBoolean(Settings.FOREGROUND_SERVICE, newValue as Boolean) requireActivity().startService(Intent(ForegroundService.ACTION_FOREGROUND, null, requireActivity(), ForegroundService::class.java)) ForegroundService.startOrStop(requireActivity()) false } } Loading Loading
app/src/main/AndroidManifest.xml +3 −1 Original line number Diff line number Diff line Loading @@ -62,7 +62,9 @@ tools:node="remove"> </provider> <service android:name=".ForegroundService"/> <service android:name=".ForegroundService" android:foregroundServiceType="dataSync" /> <!-- Remove the node added by AppAuth (remove only from net.openid.appauth library, not from our flavor manifest files) --> <activity android:name="net.openid.appauth.RedirectUriReceiverActivity" Loading
app/src/main/kotlin/at/bitfire/davdroid/App.kt +1 −1 Original line number Diff line number Diff line Loading @@ -98,7 +98,7 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide accountsUpdatedListener.listen() // foreground service (possible workaround for devices which prevent DAVx5 from being started) ForegroundService.startIfActive(this) ForegroundService.startOrStop(this) // watch storage because low storage means synchronization is stopped storageLowReceiver.listen() Loading
app/src/main/kotlin/at/bitfire/davdroid/ForegroundService.kt +30 −45 Original line number Diff line number Diff line Loading @@ -25,25 +25,6 @@ import dagger.hilt.components.SingletonComponent class ForegroundService : Service() { override fun onCreate() { super.onCreate() /* Call startForeground as soon as possible (must be within 5 seconds after the service has been created). If the foreground service shouldn't remain active (because the setting has been disabled), we'll immediately stop it with stopForeground() in onStartCommand(). */ val settingsIntent = Intent(this, AppSettingsActivity::class.java).apply { putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.FOREGROUND_SERVICE) } val builder = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_STATUS) .setSmallIcon(R.drawable.ic_foreground_notify) .setContentTitle(getString(R.string.foreground_service_notify_title)) .setContentText(getString(R.string.foreground_service_notify_text)) .setStyle(NotificationCompat.BigTextStyle()) .setContentIntent(PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) .setCategory(NotificationCompat.CATEGORY_STATUS) startForeground(NotificationUtils.NOTIFY_FOREGROUND, builder.build()) } companion object { @EntryPoint Loading @@ -52,13 +33,6 @@ class ForegroundService : Service() { fun settingsManager(): SettingsManager } /** * Starts/stops a foreground service, according to the app setting [Settings.FOREGROUND_SERVICE] * if [Settings.BATTERY_OPTIMIZATION] is enabled - meaning DAVx5 is whitelisted from optimization. */ const val ACTION_FOREGROUND = "foreground" /** * Whether the app is currently exempted from battery optimization. * @return true if battery optimization is not applied to the current app; false if battery optimization is applied Loading @@ -68,28 +42,34 @@ class ForegroundService : Service() { /** * Whether the foreground service is enabled (checked) in the app settings. * * @return true: foreground service enabled; false: foreground service not enabled */ fun foregroundServiceActivated(context: Context): Boolean { private fun shouldBeActive(context: Context): Boolean { val settingsManager = EntryPointAccessors.fromApplication(context, ForegroundServiceEntryPoint::class.java).settingsManager() return settingsManager.getBooleanOrNull(Settings.FOREGROUND_SERVICE) == true } /** * Starts the foreground service when enabled in the app settings and applicable. * Stops a potentially running foreground service when disabled in the app settings. */ fun startIfActive(context: Context) { if (foregroundServiceActivated(context)) { fun startOrStop(context: Context) { val foregroundServiceIntent = Intent(Intent.ACTION_DEFAULT, null, context, ForegroundService::class.java) if (shouldBeActive(context)) { if (batteryOptimizationWhitelisted(context)) { val serviceIntent = Intent(ACTION_FOREGROUND, null, context, ForegroundService::class.java) if (Build.VERSION.SDK_INT >= 26) // we now have 5 seconds to call Service.startForeground() [https://developer.android.com/about/versions/oreo/android-8.0-changes.html#back-all] context.startForegroundService(serviceIntent) // After the next call, we have 5 seconds to call startForeground() // [https://developer.android.com/about/versions/oreo/android-8.0-changes.html#back-all] context.startForegroundService(foregroundServiceIntent) else context.startService(serviceIntent) context.startService(foregroundServiceIntent) } else notifyBatteryOptimization(context) } } else try { context.stopService(foregroundServiceIntent) } catch (ignored: Exception) { } } private fun notifyBatteryOptimization(context: Context) { Loading @@ -111,19 +91,24 @@ class ForegroundService : Service() { } override fun onBind(intent: Intent?): Nothing? = null override fun onCreate() { super.onCreate() override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // Command is always ACTION_FOREGROUND → re-evaluate foreground setting if (foregroundServiceActivated(this)) // keep service open return START_STICKY else { // don't keep service active stopForeground(true) stopSelf() // Stop the service so that onCreate() will run again for the next command return START_NOT_STICKY val settingsIntent = Intent(this, AppSettingsActivity::class.java).apply { putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.FOREGROUND_SERVICE) } val builder = NotificationCompat.Builder(this, NotificationUtils.CHANNEL_STATUS) .setSmallIcon(R.drawable.ic_foreground_notify) .setContentTitle(getString(R.string.foreground_service_notify_title)) .setContentText(getString(R.string.foreground_service_notify_text)) .setStyle(NotificationCompat.BigTextStyle()) .setContentIntent(PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) .setCategory(NotificationCompat.CATEGORY_STATUS) startForeground(NotificationUtils.NOTIFY_FOREGROUND, builder.build()) } override fun onBind(intent: Intent?): Nothing? = null override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY } No newline at end of file
app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsActivity.kt +6 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,11 @@ import androidx.annotation.UiThread import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.getSystemService import androidx.preference.* import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import at.bitfire.cert4android.CustomCertStore import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.ForegroundService Loading Loading @@ -168,7 +172,7 @@ class AppSettingsActivity: AppCompatActivity() { isEnabled = settings.getBooleanOrNull(Settings.BATTERY_OPTIMIZATION) == true onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> settings.putBoolean(Settings.FOREGROUND_SERVICE, newValue as Boolean) requireActivity().startService(Intent(ForegroundService.ACTION_FOREGROUND, null, requireActivity(), ForegroundService::class.java)) ForegroundService.startOrStop(requireActivity()) false } } Loading