Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit ee637e4f authored by Ricki Hirner's avatar Ricki Hirner
Browse files

ForegroundService: don't use stopSelf or stopForeground (bitfireAT/davx5#462)

May fix #342
parent 769825b4
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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"
+1 −1
Original line number Diff line number Diff line
@@ -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()
+30 −45
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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) {
@@ -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
+6 −2
Original line number Diff line number Diff line
@@ -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
@@ -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
                }
            }