Loading app/src/main/kotlin/at/bitfire/davdroid/App.kt +3 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.syncadapter.AccountsUpdatedListener import at.bitfire.davdroid.syncadapter.SyncAdapterComponentManager import at.bitfire.davdroid.syncadapter.SyncUtils import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils Loading Loading @@ -100,6 +101,8 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide // don't block UI for some background checks thread { SyncAdapterComponentManager.updateComponents(this) // watch for account changes/deletions accountsUpdatedListener.listen() Loading app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import at.bitfire.davdroid.murenasso.MurenaSsoMigrationService import at.bitfire.davdroid.murenasso.MurenaSsoMigrationService.Companion.NOTIFICATION_TAG_MURENA_SSO import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.syncadapter.AccountUtils import at.bitfire.davdroid.syncadapter.SyncAdapterComponentManager import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.NotificationUtils.CHANNEL_GENERAL import at.bitfire.davdroid.ui.NotificationUtils.NOTIFY_MIGRATE_TO_MURENA_SSO Loading Loading @@ -55,6 +56,8 @@ class BootCompletedReceiver: BroadcastReceiver() { } private fun initializeSync(context: Context) { SyncAdapterComponentManager.updateComponents(context) AccountUtils.getMainAccounts(context) .forEach { // sync intervals are checked in App.onCreate() Loading app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncAdapterComponentManager.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package at.bitfire.davdroid.syncadapter import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import java.util.logging.Level object SyncAdapterComponentManager { private data class SyncServiceSpec( val serviceClass: Class<*>, val authorityResId: Int ) private val optionalSyncServices = listOf( SyncServiceSpec(MurenaTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(MurenaNotesSyncAdapterService::class.java, R.string.notes_authority), SyncServiceSpec(MurenaEmailSyncAdapterService::class.java, R.string.email_authority), SyncServiceSpec(MurenaMediaSyncAdapterService::class.java, R.string.media_authority), SyncServiceSpec(MurenaAppDataSyncAdapterService::class.java, R.string.app_data_authority), SyncServiceSpec(MurenaMeteredEdriveSyncAdapterService::class.java, R.string.metered_edrive_authority), SyncServiceSpec(MurenaPasswordSyncAdapterService::class.java, R.string.password_authority), SyncServiceSpec(GoogleTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(GoogleEmailSyncAdapterService::class.java, R.string.email_authority), SyncServiceSpec(YahooTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(YahooEmailSyncAdapterService::class.java, R.string.email_authority) ) fun updateComponents(context: Context) { val packageManager = context.packageManager for (syncService in optionalSyncServices) { val authority = context.getString(syncService.authorityResId) val isProviderAvailable = hasProvider(packageManager, authority) setComponentEnabled(packageManager, context, syncService.serviceClass, isProviderAvailable, authority) } } private fun hasProvider(packageManager: PackageManager, authority: String): Boolean = packageManager.resolveContentProvider(authority, 0) != null private fun setComponentEnabled( packageManager: PackageManager, context: Context, serviceClass: Class<*>, enabled: Boolean, authority: String ) { val component = ComponentName(context, serviceClass) val desiredState = if (enabled) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else PackageManager.COMPONENT_ENABLED_STATE_DISABLED if (packageManager.getComponentEnabledSetting(component) == desiredState) { return } packageManager.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP) Logger.log.log(Level.INFO, "Set ${serviceClass.simpleName} to ${if (enabled) "ENABLED" else "DISABLED"} (authority=$authority)") } } Loading
app/src/main/kotlin/at/bitfire/davdroid/App.kt +3 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.syncadapter.AccountsUpdatedListener import at.bitfire.davdroid.syncadapter.SyncAdapterComponentManager import at.bitfire.davdroid.syncadapter.SyncUtils import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils Loading Loading @@ -100,6 +101,8 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide // don't block UI for some background checks thread { SyncAdapterComponentManager.updateComponents(this) // watch for account changes/deletions accountsUpdatedListener.listen() Loading
app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +3 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import at.bitfire.davdroid.murenasso.MurenaSsoMigrationService import at.bitfire.davdroid.murenasso.MurenaSsoMigrationService.Companion.NOTIFICATION_TAG_MURENA_SSO import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.syncadapter.AccountUtils import at.bitfire.davdroid.syncadapter.SyncAdapterComponentManager import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.NotificationUtils.CHANNEL_GENERAL import at.bitfire.davdroid.ui.NotificationUtils.NOTIFY_MIGRATE_TO_MURENA_SSO Loading Loading @@ -55,6 +56,8 @@ class BootCompletedReceiver: BroadcastReceiver() { } private fun initializeSync(context: Context) { SyncAdapterComponentManager.updateComponents(context) AccountUtils.getMainAccounts(context) .forEach { // sync intervals are checked in App.onCreate() Loading
app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncAdapterComponentManager.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package at.bitfire.davdroid.syncadapter import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager import at.bitfire.davdroid.R import at.bitfire.davdroid.log.Logger import java.util.logging.Level object SyncAdapterComponentManager { private data class SyncServiceSpec( val serviceClass: Class<*>, val authorityResId: Int ) private val optionalSyncServices = listOf( SyncServiceSpec(MurenaTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(MurenaNotesSyncAdapterService::class.java, R.string.notes_authority), SyncServiceSpec(MurenaEmailSyncAdapterService::class.java, R.string.email_authority), SyncServiceSpec(MurenaMediaSyncAdapterService::class.java, R.string.media_authority), SyncServiceSpec(MurenaAppDataSyncAdapterService::class.java, R.string.app_data_authority), SyncServiceSpec(MurenaMeteredEdriveSyncAdapterService::class.java, R.string.metered_edrive_authority), SyncServiceSpec(MurenaPasswordSyncAdapterService::class.java, R.string.password_authority), SyncServiceSpec(GoogleTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(GoogleEmailSyncAdapterService::class.java, R.string.email_authority), SyncServiceSpec(YahooTasksSyncAdapterService::class.java, R.string.task_authority), SyncServiceSpec(YahooEmailSyncAdapterService::class.java, R.string.email_authority) ) fun updateComponents(context: Context) { val packageManager = context.packageManager for (syncService in optionalSyncServices) { val authority = context.getString(syncService.authorityResId) val isProviderAvailable = hasProvider(packageManager, authority) setComponentEnabled(packageManager, context, syncService.serviceClass, isProviderAvailable, authority) } } private fun hasProvider(packageManager: PackageManager, authority: String): Boolean = packageManager.resolveContentProvider(authority, 0) != null private fun setComponentEnabled( packageManager: PackageManager, context: Context, serviceClass: Class<*>, enabled: Boolean, authority: String ) { val component = ComponentName(context, serviceClass) val desiredState = if (enabled) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else PackageManager.COMPONENT_ENABLED_STATE_DISABLED if (packageManager.getComponentEnabledSetting(component) == desiredState) { return } packageManager.setComponentEnabledSetting(component, desiredState, PackageManager.DONT_KILL_APP) Logger.log.log(Level.INFO, "Set ${serviceClass.simpleName} to ${if (enabled) "ENABLED" else "DISABLED"} (authority=$authority)") } }