Drop foreground service (#569)

* Remove foreground service permissions

* Remove foreground service + setting

* Remove unused setting name and notification IDs
This commit is contained in:
Ricki Hirner 2024-02-14 17:05:53 +01:00 committed by GitHub
parent a7f6192177
commit ea6e520c93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 7 additions and 152 deletions

View file

@ -6,8 +6,6 @@
<!-- normal permissions -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
@ -63,10 +61,6 @@
tools:node="remove">
</provider>
<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"
tools:node="remove" tools:selector="net.openid.appauth"/>

View file

@ -97,9 +97,6 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide
// watch for account changes/deletions
accountsUpdatedListener.listen()
// foreground service (possible workaround for devices which prevent DAVx5 from being started)
ForegroundService.startOrStop(this)
// watch storage because low storage means synchronization is stopped
storageLowReceiver.listen()

View file

@ -1,116 +0,0 @@
/***************************************************************************************************
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
package at.bitfire.davdroid
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.os.Build
import android.os.PowerManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.getSystemService
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.AppSettingsActivity
import at.bitfire.davdroid.ui.NotificationUtils
import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
class ForegroundService : Service() {
companion object {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface ForegroundServiceEntryPoint {
fun settingsManager(): SettingsManager
}
/**
* 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
*/
private fun batteryOptimizationWhitelisted(context: Context) =
context.getSystemService<PowerManager>()!!.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)
/**
* Whether the foreground service is enabled (checked) in the app settings.
*
* @return true: foreground service enabled; false: foreground service not enabled
*/
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 startOrStop(context: Context) {
val foregroundServiceIntent = Intent(Intent.ACTION_DEFAULT, null, context, ForegroundService::class.java)
if (shouldBeActive(context)) {
if (batteryOptimizationWhitelisted(context)) {
if (Build.VERSION.SDK_INT >= 26)
// 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(foregroundServiceIntent)
} else
notifyBatteryOptimization(context)
} else
try {
context.stopService(foregroundServiceIntent)
} catch (ignored: Exception) { }
}
private fun notifyBatteryOptimization(context: Context) {
val settingsIntent = Intent(context, AppSettingsActivity::class.java).apply {
putExtra(AppSettingsActivity.EXTRA_SCROLL_TO, Settings.BATTERY_OPTIMIZATION)
}
val pendingSettingsIntent = PendingIntent.getActivity(context, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val builder =
NotificationCompat.Builder(context, NotificationUtils.CHANNEL_DEBUG)
.setSmallIcon(R.drawable.ic_warning_notify)
.setContentTitle(context.getString(R.string.battery_optimization_notify_title))
.setContentText(context.getString(R.string.battery_optimization_notify_text))
.setContentIntent(pendingSettingsIntent)
.setCategory(NotificationCompat.CATEGORY_ERROR)
val nm = NotificationManagerCompat.from(context)
nm.notifyIfPossible(NotificationUtils.NOTIFY_BATTERY_OPTIMIZATION, builder.build())
}
}
override fun onCreate() {
super.onCreate()
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)
ServiceCompat.startForeground(this, NotificationUtils.NOTIFY_FOREGROUND, builder.build(), FOREGROUND_SERVICE_TYPE_DATA_SYNC)
}
override fun onBind(intent: Intent?): Nothing? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY
}

View file

@ -238,6 +238,9 @@ class RefreshCollectionsWorker @AssistedInject constructor(
return Result.success()
}
/**
* Used by WorkManager to show a foreground service notification for expedited jobs on Android <12.
*/
override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = NotificationUtils.newBuilder(applicationContext, NotificationUtils.CHANNEL_STATUS)
.setSmallIcon(R.drawable.ic_foreground_notify)

View file

@ -9,7 +9,6 @@ import androidx.appcompat.app.AppCompatDelegate
object Settings {
const val BATTERY_OPTIMIZATION = "battery_optimization"
const val FOREGROUND_SERVICE = "foreground_service"
const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs"

View file

@ -426,6 +426,9 @@ class SyncWorker @AssistedInject constructor(
return@withContext Result.success()
}
/**
* Used by WorkManager to show a foreground service notification for expedited jobs on Android <12.
*/
override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = NotificationUtils.newBuilder(applicationContext, NotificationUtils.CHANNEL_STATUS)
.setSmallIcon(R.drawable.ic_foreground_notify)

View file

@ -23,7 +23,6 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import at.bitfire.cert4android.CustomCertStore
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.ForegroundService
import at.bitfire.davdroid.R
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.settings.Settings
@ -167,16 +166,6 @@ class AppSettingsActivity: AppCompatActivity() {
}
}
findPreference<SwitchPreferenceCompat>(Settings.FOREGROUND_SERVICE)!!.apply {
isChecked = settings.getBooleanOrNull(Settings.FOREGROUND_SERVICE) == true
isEnabled = settings.getBooleanOrNull(Settings.BATTERY_OPTIMIZATION) == true
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
settings.putBoolean(Settings.FOREGROUND_SERVICE, newValue as Boolean)
ForegroundService.startOrStop(requireActivity())
false
}
}
// connection settings
val proxyType = settings.getInt(Settings.PROXY_TYPE)
findPreference<ListPreference>(Settings.PROXY_TYPE)!!.apply {

View file

@ -24,13 +24,10 @@ object NotificationUtils {
// notification IDs
const val NOTIFY_VERBOSE_LOGGING = 1
const val NOTIFY_REFRESH_COLLECTIONS = 2
const val NOTIFY_FOREGROUND = 3
const val NOTIFY_DATABASE_CORRUPTED = 4
const val NOTIFY_BATTERY_OPTIMIZATION = 5
const val NOTIFY_SYNC_ERROR = 10
const val NOTIFY_INVALID_RESOURCE = 11
const val NOTIFY_WEBDAV_ACCESS = 12
// const val NOTIFY_LOW_STORAGE = 13
const val NOTIFY_SYNC_EXPEDITED = 14
const val NOTIFY_TASKS_PROVIDER_TOO_OLD = 20
const val NOTIFY_PERMISSIONS = 21

View file

@ -158,11 +158,7 @@
<string name="refresh_collections_worker_refresh_failed">Service detection failed</string>
<string name="refresh_collections_worker_refresh_couldnt_refresh">Couldn\'t refresh collection list</string>
<!-- Battery Optimization -->
<string name="battery_optimization_notify_title">Can not run in foreground</string>
<string name="battery_optimization_notify_text">Battery optimization whitelisting required</string>
<!-- ForegroundService -->
<!-- Foreground service used by WorkManager on Android <12 -->
<string name="foreground_service_notify_title">Running in foreground</string>
<string name="foreground_service_notify_text">On some devices, this is necessary for automatic synchronization.</string>
@ -177,8 +173,6 @@
<string name="app_settings_battery_optimization">Battery optimization</string>
<string name="app_settings_battery_optimization_whitelisted">App is whitelisted (recommended)</string>
<string name="app_settings_battery_optimization_not_whitelisted">App is not whitelisted (not recommended)</string>
<string name="app_settings_foreground_service">Keep in foreground</string>
<string name="app_settings_foreground_service_details">May help if your device prevents automatic synchronization</string>
<string name="app_settings_connection">Connection</string>
<string name="app_settings_proxy">Proxy type</string>
<string-array name="app_settings_proxy_types">

View file

@ -26,11 +26,6 @@
android:summaryOn="@string/app_settings_battery_optimization_whitelisted"
android:summaryOff="@string/app_settings_battery_optimization_not_whitelisted"/>
<SwitchPreferenceCompat
android:key="foreground_service"
android:title="@string/app_settings_foreground_service"
android:summary="@string/app_settings_foreground_service_details"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/app_settings_connection">