Better handling of tasks app changes (#652)

* Fix task sync update when new app is selected by user or TasksWatcher
* Minor refactoring, KDoc
This commit is contained in:
Ricki Hirner 2024-03-16 12:16:23 +01:00 committed by GitHub
parent 322a7565b0
commit 3ab278a315
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 224 additions and 219 deletions

View file

@ -61,7 +61,7 @@ class App: Application(), Thread.UncaughtExceptionHandler, Configuration.Provide
// watch for account changes/deletions
accountsUpdatedListener.listen()
// watch storage because low storage means synchronization is stopped
// watch storage because low storage means sync framework stops local content update notifications
storageLowReceiver.listen()
// watch installed/removed tasks apps and update sync settings accordingly

View file

@ -6,8 +6,7 @@ package at.bitfire.davdroid
import android.content.Context
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.syncadapter.SyncUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.ical4android.TaskProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -31,28 +30,26 @@ class TasksWatcher private constructor(
override fun onPackageChanged() {
CoroutineScope(Dispatchers.Default).launch {
if (TaskUtils.currentProvider(context) == null) {
/* Currently no usable tasks provider.
Iterate through all supported providers and select one, if available. */
val currentProvider = TaskUtils.currentProvider(context)
Logger.log.info("App launched or package (un)installed; current tasks provider = $currentProvider")
if (currentProvider == null) {
// Iterate through all supported providers and select one, if available.
var providerSelected = false
for (provider in TaskProvider.ProviderName.entries) {
val available = context.packageManager.resolveContentProvider(provider.authority, 0) != null
if (available) {
Logger.log.info("Selecting new tasks provider: $provider")
TaskUtils.selectProvider(context, provider, updateSyncSettings = false)
TaskUtils.selectProvider(context, provider)
providerSelected = true
break
}
}
if (!providerSelected)
// no provider available, also clear setting
TaskUtils.selectProvider(context, null, updateSyncSettings = false)
// no provider available (anymore), also clear setting and sync
TaskUtils.selectProvider(context, null)
}
// update sync settings
SyncUtils.updateTaskSync(context)
}
}

View file

@ -1,77 +0,0 @@
/***************************************************************************************************
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
package at.bitfire.davdroid.resource
import android.content.Context
import android.content.pm.PackageManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.map
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.SyncUtils
import at.bitfire.ical4android.TaskProvider.ProviderName
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
object TaskUtils {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface TaskUtilsEntryPoint {
fun settingsManager(): SettingsManager
}
/**
* Returns the currently selected tasks provider (if it's still available = installed).
*
* @return the currently selected tasks provider, or null if none is available
*/
fun currentProvider(context: Context): ProviderName? {
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
val preferredAuthority = settingsManager.getString(Settings.SELECTED_TASKS_PROVIDER) ?: return null
return preferredAuthorityToProviderName(preferredAuthority, context.packageManager)
}
/**
* Returns the currently selected tasks provider (if it's still available = installed).
*
* @return the currently selected tasks provider, or null if none is available
*/
fun currentProviderLive(context: Context): LiveData<ProviderName?> {
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
return settingsManager.getStringLive(Settings.SELECTED_TASKS_PROVIDER).map { preferred ->
if (preferred != null)
preferredAuthorityToProviderName(preferred, context.packageManager)
else
null
}
}
private fun preferredAuthorityToProviderName(
preferredAuthority: String,
packageManager: PackageManager
): ProviderName? {
ProviderName.entries.toTypedArray()
.sortedByDescending { it.authority == preferredAuthority }
.forEach { providerName ->
if (packageManager.resolveContentProvider(providerName.authority, 0) != null)
return providerName
}
return null
}
fun isAvailable(context: Context) = currentProvider(context) != null
fun selectProvider(context: Context, providerName: ProviderName?, updateSyncSettings: Boolean = false) {
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
settingsManager.putString(Settings.SELECTED_TASKS_PROVIDER, providerName?.authority)
if (updateSyncSettings)
SyncUtils.updateTaskSync(context)
}
}

View file

@ -22,7 +22,7 @@ import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.LocalTask
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.syncadapter.BaseSyncWorker
import at.bitfire.davdroid.syncadapter.SyncUtils
import at.bitfire.davdroid.util.setAndVerifyUserData
@ -420,7 +420,7 @@ class AccountSettingsMigrations(
@Suppress("unused")
private fun update_4_5() {
// call PackageChangedReceiver which then enables/disables OpenTasks sync when it's (not) available
SyncUtils.updateTaskSync(context)
TaskUtils.selectProvider(context, TaskUtils.currentProvider(context))
}
@Suppress("unused")

View file

@ -8,8 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate
object Settings {
const val BATTERY_OPTIMIZATION = "battery_optimization"
const val DISTRUST_SYSTEM_CERTIFICATES = "distrust_system_certs"
const val PROXY_TYPE = "proxy_type" // Integer
@ -43,7 +41,7 @@ object Settings {
* Selected tasks app. When at least one tasks app is installed, this setting is set to its authority.
* In case of multiple available tasks app, the user can choose one and this setting will reflect the selected one.
*
* If no tasks app is installed, this setting is not set.
* If no tasks app is available, this setting is not set.
*/
const val SELECTED_TASKS_PROVIDER = "preferred_tasks_provider"

View file

@ -15,6 +15,7 @@ import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.HttpClient
import at.bitfire.davdroid.resource.LocalJtxCollection
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.ical4android.JtxCollection
import at.bitfire.ical4android.TaskProvider
@ -62,7 +63,7 @@ class JtxSyncer(context: Context): Syncer(context) {
}
} catch (e: TaskProvider.ProviderTooOldException) {
SyncUtils.notifyProviderTooOld(context, e)
TaskUtils.notifyProviderTooOld(context, e)
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync jtx collections", e)
}

View file

@ -4,35 +4,14 @@
package at.bitfire.davdroid.syncadapter
import android.accounts.Account
import android.accounts.AccountManager
import android.app.PendingIntent
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.provider.CalendarContract
import androidx.annotation.WorkerThread
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import at.bitfire.davdroid.InvalidAccountException
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.NotificationUtils
import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible
import at.bitfire.davdroid.util.PermissionUtils
import at.bitfire.ical4android.TaskProvider
import at.bitfire.davdroid.util.TaskUtils
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
/**
@ -47,43 +26,6 @@ object SyncUtils {
fun settingsManager(): SettingsManager
}
/**
* Starts an Intent and redirects the user to the package in the market to update the app
*
* @param e the TaskProvider.ProviderTooOldException to be shown
*/
fun notifyProviderTooOld(context: Context, e: TaskProvider.ProviderTooOldException) {
val nm = NotificationManagerCompat.from(context)
val message = context.getString(R.string.sync_error_tasks_required_version, e.provider.minVersionName)
val pm = context.packageManager
val tasksAppInfo = pm.getPackageInfo(e.provider.packageName, 0)
val tasksAppLabel = tasksAppInfo.applicationInfo.loadLabel(pm)
val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
.setSmallIcon(R.drawable.ic_sync_problem_notify)
.setContentTitle(context.getString(R.string.sync_error_tasks_too_old, tasksAppLabel))
.setContentText(message)
.setSubText("$tasksAppLabel ${e.installedVersionName}")
.setCategory(NotificationCompat.CATEGORY_ERROR)
try {
val icon = pm.getApplicationIcon(e.provider.packageName)
if (icon is BitmapDrawable)
notify.setLargeIcon(icon.bitmap)
} catch (ignored: PackageManager.NameNotFoundException) {
// couldn't get provider app icon
}
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=${e.provider.packageName}"))
val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
if (intent.resolveActivity(pm) != null)
notify.setContentIntent(PendingIntent.getActivity(context, 0, intent, flags))
nm.notifyIfPossible(NotificationUtils.NOTIFY_TASKS_PROVIDER_TOO_OLD, notify.build())
}
/**
* Returns a list of all available sync authorities:
*
@ -107,61 +49,4 @@ object SyncUtils {
return result
}
// task sync utils
/**
* Sets up sync for the current TaskProvider (and disables sync for unavailable task providers).
*
* In case of missing permissions, a notification is shown.
*/
@WorkerThread
fun updateTaskSync(context: Context) {
val currentProvider = TaskUtils.currentProvider(context)
Logger.log.info("App launched or other package (un)installed; current tasks provider = $currentProvider")
var permissionsRequired = false // whether additional permissions are required
// check all accounts and (de)activate task provider(s) if a CalDAV service is defined
val db = EntryPointAccessors.fromApplication(context, SyncUtilsEntryPoint::class.java).appDatabase()
val accountManager = AccountManager.get(context)
for (account in accountManager.getAccountsByType(context.getString(R.string.account_type))) {
val hasCalDAV = db.serviceDao().getByAccountAndType(account.name, Service.TYPE_CALDAV) != null
for (providerName in TaskProvider.ProviderName.entries) {
val isSyncable = ContentResolver.getIsSyncable(account, providerName.authority) // may be -1 (unknown state)
val shallBeSyncable = hasCalDAV && providerName == currentProvider
if ((shallBeSyncable && isSyncable != 1) || (!shallBeSyncable && isSyncable != 0)) {
// enable/disable sync
setSyncableFromSettings(context, account, providerName.authority, shallBeSyncable)
// if sync has just been enabled: check whether additional permissions are required
if (shallBeSyncable && !PermissionUtils.havePermissions(context, providerName.permissions))
permissionsRequired = true
}
}
}
if (permissionsRequired) {
Logger.log.warning("Tasks synchronization is now enabled for at least one account, but permissions are not granted")
PermissionUtils.notifyPermissions(context, null)
}
}
private fun setSyncableFromSettings(context: Context, account: Account, authority: String, syncable: Boolean) {
val settingsManager by lazy { EntryPointAccessors.fromApplication(context, SyncUtilsEntryPoint::class.java).settingsManager() }
if (syncable) {
Logger.log.info("Enabling $authority sync for $account")
ContentResolver.setIsSyncable(account, authority, 1)
try {
val settings = AccountSettings(context, account)
val interval = settings.getTasksSyncInterval() ?: settingsManager.getLong(Settings.DEFAULT_SYNC_INTERVAL)
settings.setSyncInterval(authority, interval)
} catch (e: InvalidAccountException) {
// account has already been removed
}
} else {
Logger.log.info("Disabling $authority sync for $account")
ContentResolver.setIsSyncable(account, authority, 0)
}
}
}

View file

@ -15,6 +15,7 @@ import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.HttpClient
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.ical4android.DmfsTaskList
import at.bitfire.ical4android.TaskProvider
@ -61,7 +62,7 @@ class TaskSyncer(context: Context): Syncer(context) {
TasksSyncManager(context, account, accountSettings, httpClient.value, extras, authority, syncResult, taskList).performSync()
}
} catch (e: TaskProvider.ProviderTooOldException) {
SyncUtils.notifyProviderTooOld(context, e)
TaskUtils.notifyProviderTooOld(context, e)
syncResult.databaseError = true
} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync task lists", e)

View file

@ -63,7 +63,7 @@ import at.bitfire.cert4android.CustomCertStore
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage

View file

@ -51,7 +51,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.PackageChangedReceiver
import at.bitfire.davdroid.R
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
import at.bitfire.davdroid.ui.widget.CardWithImage

View file

@ -75,7 +75,7 @@ import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker

View file

@ -35,7 +35,7 @@ import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.HttpClient
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.syncadapter.AccountsCleanupWorker

View file

@ -63,7 +63,7 @@ import androidx.lifecycle.ViewModelProvider
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker

View file

@ -7,7 +7,7 @@ package at.bitfire.davdroid.ui.intro
import android.app.Application
import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.TasksActivity
import at.bitfire.davdroid.ui.TasksCard

View file

@ -32,7 +32,7 @@ import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.db.HomeSet
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.util.TaskUtils
import at.bitfire.davdroid.servicedetection.DavResourceFinder
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
import at.bitfire.davdroid.settings.AccountSettings

View file

@ -0,0 +1,200 @@
/***************************************************************************************************
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
**************************************************************************************************/
package at.bitfire.davdroid.util
import android.accounts.Account
import android.accounts.AccountManager
import android.app.PendingIntent
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.map
import at.bitfire.davdroid.InvalidAccountException
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.Service
import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.PeriodicSyncWorker
import at.bitfire.davdroid.syncadapter.SyncUtils
import at.bitfire.davdroid.ui.NotificationUtils
import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible
import at.bitfire.ical4android.TaskProvider
import at.bitfire.ical4android.TaskProvider.ProviderName
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
object TaskUtils {
@EntryPoint
@InstallIn(SingletonComponent::class)
interface TaskUtilsEntryPoint {
fun settingsManager(): SettingsManager
}
/**
* Returns the currently selected tasks provider (if it's still available = installed).
*
* @return the currently selected tasks provider, or null if none is available
*/
fun currentProvider(context: Context): ProviderName? {
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
val preferredAuthority = settingsManager.getString(Settings.SELECTED_TASKS_PROVIDER) ?: return null
return preferredAuthorityToProviderName(preferredAuthority, context.packageManager)
}
/**
* Returns the currently selected tasks provider (if it's still available = installed).
*
* @return the currently selected tasks provider, or null if none is available
*/
fun currentProviderLive(context: Context): LiveData<ProviderName?> {
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
return settingsManager.getStringLive(Settings.SELECTED_TASKS_PROVIDER).map { preferred ->
if (preferred != null)
preferredAuthorityToProviderName(preferred, context.packageManager)
else
null
}
}
private fun preferredAuthorityToProviderName(
preferredAuthority: String,
packageManager: PackageManager
): ProviderName? {
ProviderName.entries.toTypedArray()
.sortedByDescending { it.authority == preferredAuthority }
.forEach { providerName ->
if (packageManager.resolveContentProvider(providerName.authority, 0) != null)
return providerName
}
return null
}
fun isAvailable(context: Context) = currentProvider(context) != null
/**
* Starts an Intent and redirects the user to the package in the market to update the app
*
* @param e the TaskProvider.ProviderTooOldException to be shown
*/
fun notifyProviderTooOld(context: Context, e: TaskProvider.ProviderTooOldException) {
val nm = NotificationManagerCompat.from(context)
val message = context.getString(R.string.sync_error_tasks_required_version, e.provider.minVersionName)
val pm = context.packageManager
val tasksAppInfo = pm.getPackageInfo(e.provider.packageName, 0)
val tasksAppLabel = tasksAppInfo.applicationInfo.loadLabel(pm)
val notify = NotificationUtils.newBuilder(context, NotificationUtils.CHANNEL_SYNC_ERRORS)
.setSmallIcon(R.drawable.ic_sync_problem_notify)
.setContentTitle(context.getString(R.string.sync_error_tasks_too_old, tasksAppLabel))
.setContentText(message)
.setSubText("$tasksAppLabel ${e.installedVersionName}")
.setCategory(NotificationCompat.CATEGORY_ERROR)
try {
val icon = pm.getApplicationIcon(e.provider.packageName)
if (icon is BitmapDrawable)
notify.setLargeIcon(icon.bitmap)
} catch (ignored: PackageManager.NameNotFoundException) {
// couldn't get provider app icon
}
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=${e.provider.packageName}"))
val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
if (intent.resolveActivity(pm) != null)
notify.setContentIntent(PendingIntent.getActivity(context, 0, intent, flags))
nm.notifyIfPossible(NotificationUtils.NOTIFY_TASKS_PROVIDER_TOO_OLD, notify.build())
}
/**
* Sets up sync for the current TaskProvider (and disables sync for unavailable task providers):
*
* 1. Makes selected tasks authority _syncable_ in the sync framework, all other authorities _not syncable_.
* 2. Creates periodic sync worker for selected authority, disables periodic sync workers for all other authorities.
* 3. If the permissions don't allow synchronizing with the selected tasks app, a notification is shown.
*
* Called
*
* - when a user explicitly selects another task app, or
* - when there previously was no (usable) tasks app and [at.bitfire.davdroid.TasksWatcher] detected a new one.
*/
fun selectProvider(context: Context, selectedProvider: ProviderName?) {
Logger.log.info("Selecting tasks app: $selectedProvider")
val settingsManager = EntryPointAccessors.fromApplication(context, TaskUtilsEntryPoint::class.java).settingsManager()
settingsManager.putString(Settings.SELECTED_TASKS_PROVIDER, selectedProvider?.authority)
var permissionsRequired = false // whether additional permissions are required
// check all accounts and (de)activate task provider(s) if a CalDAV service is defined
val db = EntryPointAccessors.fromApplication(context, SyncUtils.SyncUtilsEntryPoint::class.java).appDatabase()
val accountManager = AccountManager.get(context)
for (account in accountManager.getAccountsByType(context.getString(R.string.account_type))) {
val hasCalDAV = db.serviceDao().getByAccountAndType(account.name, Service.TYPE_CALDAV) != null
for (providerName in TaskProvider.ProviderName.entries) {
val syncable = hasCalDAV && providerName == selectedProvider
// enable/disable sync for the given account and authority
setSyncable(
context,
account,
providerName.authority,
syncable
)
// if sync has just been enabled: check whether additional permissions are required
if (syncable && !PermissionUtils.havePermissions(context, providerName.permissions))
permissionsRequired = true
}
}
if (permissionsRequired) {
Logger.log.warning("Tasks synchronization is now enabled for at least one account, but permissions are not granted")
PermissionUtils.notifyPermissions(context, null)
}
}
private fun setSyncable(context: Context, account: Account, authority: String, syncable: Boolean) {
val settingsManager by lazy { EntryPointAccessors.fromApplication(context, SyncUtils.SyncUtilsEntryPoint::class.java).settingsManager() }
try {
val settings = AccountSettings(context, account)
if (syncable) {
Logger.log.info("Enabling $authority sync for $account")
// make account syncable by sync framework
ContentResolver.setIsSyncable(account, authority, 1)
// set sync interval according to settings; also updates periodic sync workers and sync framework on-content-change
val interval = settings.getTasksSyncInterval() ?: settingsManager.getLong(Settings.DEFAULT_SYNC_INTERVAL)
settings.setSyncInterval(authority, interval)
} else {
Logger.log.info("Disabling $authority sync for $account")
// make account not syncable by sync framework
ContentResolver.setIsSyncable(account, authority, 0)
// disable periodic sync worker
PeriodicSyncWorker.disable(context, account, authority)
}
} catch (e: InvalidAccountException) {
// account has already been removed, make sure periodic sync is disabled, too
PeriodicSyncWorker.disable(context, account, authority)
}
}
}