Loading app/src/main/AndroidManifest.xml +7 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_STATS"/> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <!-- account management permissions not required for own accounts since API level 22 --> Loading Loading @@ -214,6 +215,12 @@ android:resource="@xml/contacts"/> </service> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <!-- provider to share debug info/logs --> <provider android:name="androidx.core.content.FileProvider" Loading app/src/main/java/at/bitfire/davdroid/App.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import android.os.StrictMode import androidx.appcompat.content.res.AppCompatResources import androidx.core.graphics.drawable.toBitmap import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.UiUtils Loading Loading @@ -73,6 +74,9 @@ class App: Application(), Thread.UncaughtExceptionHandler { // check whether a tasks app is currently installed TasksWatcher.updateTaskSync(this@App) // check/repair sync intervals AccountSettings.repairSyncIntervals(this@App) } } Loading app/src/main/java/at/bitfire/davdroid/BootCompletedReceiver.kt 0 → 100644 +22 −0 Original line number Diff line number Diff line package at.bitfire.davdroid import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import at.bitfire.davdroid.log.Logger /** * There are circumstances when Android drops automatic sync of accounts and resets them * to manual synchronization, for instance when the device is booted into safe mode. * * This receiver causes the app to be started when the device is rebooted. When the app * is started, it checks (and repairs, if necessary) the sync intervals in [App.onCreate]. */ class BootCompletedReceiver: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Logger.log.info("Device has been rebooted; checking sync intervals etc.") // sync intervals are checked in App.onCreate() } } No newline at end of file app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt +52 −3 Original line number Diff line number Diff line Loading @@ -57,7 +57,10 @@ class AccountSettings( const val CURRENT_VERSION = 11 const val KEY_SETTINGS_VERSION = "version" /** Stores the tasks sync interval (in minutes) so that it can be set again when the provider is switched */ const val KEY_SYNC_INTERVAL_ADDRESSBOOKS = "sync_interval_addressbooks" const val KEY_SYNC_INTERVAL_CALENDARS = "sync_interval_calendars" /** Stores the tasks sync interval (in seconds) so that it can be set again when the provider is switched */ const val KEY_SYNC_INTERVAL_TASKS = "sync_interval_tasks" const val KEY_USERNAME = "user_name" Loading Loading @@ -120,6 +123,46 @@ class AccountSettings( return bundle } fun repairSyncIntervals(context: Context) { val addressBooksAuthority = context.getString(R.string.address_books_authority) val taskAuthority = TaskUtils.currentProvider(context)?.authority val am = AccountManager.get(context) for (account in am.getAccountsByType(context.getString(R.string.account_type))) { val settings = AccountSettings(context, account) // repair address book sync settings.getSavedAddressbooksSyncInterval()?.let { shouldBe -> val current = settings.getSyncInterval(addressBooksAuthority) if (current != shouldBe) { Logger.log.warning("${account.name}: $addressBooksAuthority sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(addressBooksAuthority, shouldBe) } } // repair calendar sync settings.getSavedCalendarsSyncInterval()?.let { strInterval -> val shouldBe = strInterval.toLong() val current = settings.getSyncInterval(CalendarContract.AUTHORITY) if (current != shouldBe) { Logger.log.warning("${account.name}: ${CalendarContract.AUTHORITY} sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(CalendarContract.AUTHORITY, shouldBe) } } if (taskAuthority != null) // repair calendar sync settings.getSavedTasksSyncInterval()?.let { strInterval -> val shouldBe = strInterval.toLong() val current = settings.getSyncInterval(taskAuthority) if (current != shouldBe) { Logger.log.warning("${account.name}: $taskAuthority sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(taskAuthority, shouldBe) } } } } } Loading Loading @@ -217,13 +260,19 @@ class AccountSettings( if (!success) return false // store task sync interval in account settings (used when the provider is switched) if (TaskProvider.ProviderName.values().any { it.authority == authority }) // store sync interval in account settings (used when the provider is switched) if (authority == context.getString(R.string.address_books_authority)) accountManager.setUserData(account, KEY_SYNC_INTERVAL_ADDRESSBOOKS, seconds.toString()) else if (authority == CalendarContract.AUTHORITY) accountManager.setUserData(account, KEY_SYNC_INTERVAL_CALENDARS, seconds.toString()) else if (TaskProvider.ProviderName.values().any { it.authority == authority }) accountManager.setUserData(account, KEY_SYNC_INTERVAL_TASKS, seconds.toString()) return true } fun getSavedAddressbooksSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_ADDRESSBOOKS)?.toLong() fun getSavedCalendarsSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_CALENDARS)?.toLong() fun getSavedTasksSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_TASKS)?.toLong() fun getSyncWifiOnly() = Loading Loading
app/src/main/AndroidManifest.xml +7 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_STATS"/> <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <!-- account management permissions not required for own accounts since API level 22 --> Loading Loading @@ -214,6 +215,12 @@ android:resource="@xml/contacts"/> </service> <receiver android:name=".BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <!-- provider to share debug info/logs --> <provider android:name="androidx.core.content.FileProvider" Loading
app/src/main/java/at/bitfire/davdroid/App.kt +4 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ import android.os.StrictMode import androidx.appcompat.content.res.AppCompatResources import androidx.core.graphics.drawable.toBitmap import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.ui.DebugInfoActivity import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.UiUtils Loading Loading @@ -73,6 +74,9 @@ class App: Application(), Thread.UncaughtExceptionHandler { // check whether a tasks app is currently installed TasksWatcher.updateTaskSync(this@App) // check/repair sync intervals AccountSettings.repairSyncIntervals(this@App) } } Loading
app/src/main/java/at/bitfire/davdroid/BootCompletedReceiver.kt 0 → 100644 +22 −0 Original line number Diff line number Diff line package at.bitfire.davdroid import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import at.bitfire.davdroid.log.Logger /** * There are circumstances when Android drops automatic sync of accounts and resets them * to manual synchronization, for instance when the device is booted into safe mode. * * This receiver causes the app to be started when the device is rebooted. When the app * is started, it checks (and repairs, if necessary) the sync intervals in [App.onCreate]. */ class BootCompletedReceiver: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Logger.log.info("Device has been rebooted; checking sync intervals etc.") // sync intervals are checked in App.onCreate() } } No newline at end of file
app/src/main/java/at/bitfire/davdroid/settings/AccountSettings.kt +52 −3 Original line number Diff line number Diff line Loading @@ -57,7 +57,10 @@ class AccountSettings( const val CURRENT_VERSION = 11 const val KEY_SETTINGS_VERSION = "version" /** Stores the tasks sync interval (in minutes) so that it can be set again when the provider is switched */ const val KEY_SYNC_INTERVAL_ADDRESSBOOKS = "sync_interval_addressbooks" const val KEY_SYNC_INTERVAL_CALENDARS = "sync_interval_calendars" /** Stores the tasks sync interval (in seconds) so that it can be set again when the provider is switched */ const val KEY_SYNC_INTERVAL_TASKS = "sync_interval_tasks" const val KEY_USERNAME = "user_name" Loading Loading @@ -120,6 +123,46 @@ class AccountSettings( return bundle } fun repairSyncIntervals(context: Context) { val addressBooksAuthority = context.getString(R.string.address_books_authority) val taskAuthority = TaskUtils.currentProvider(context)?.authority val am = AccountManager.get(context) for (account in am.getAccountsByType(context.getString(R.string.account_type))) { val settings = AccountSettings(context, account) // repair address book sync settings.getSavedAddressbooksSyncInterval()?.let { shouldBe -> val current = settings.getSyncInterval(addressBooksAuthority) if (current != shouldBe) { Logger.log.warning("${account.name}: $addressBooksAuthority sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(addressBooksAuthority, shouldBe) } } // repair calendar sync settings.getSavedCalendarsSyncInterval()?.let { strInterval -> val shouldBe = strInterval.toLong() val current = settings.getSyncInterval(CalendarContract.AUTHORITY) if (current != shouldBe) { Logger.log.warning("${account.name}: ${CalendarContract.AUTHORITY} sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(CalendarContract.AUTHORITY, shouldBe) } } if (taskAuthority != null) // repair calendar sync settings.getSavedTasksSyncInterval()?.let { strInterval -> val shouldBe = strInterval.toLong() val current = settings.getSyncInterval(taskAuthority) if (current != shouldBe) { Logger.log.warning("${account.name}: $taskAuthority sync interval should be $shouldBe but is $current -> setting to $current") settings.setSyncInterval(taskAuthority, shouldBe) } } } } } Loading Loading @@ -217,13 +260,19 @@ class AccountSettings( if (!success) return false // store task sync interval in account settings (used when the provider is switched) if (TaskProvider.ProviderName.values().any { it.authority == authority }) // store sync interval in account settings (used when the provider is switched) if (authority == context.getString(R.string.address_books_authority)) accountManager.setUserData(account, KEY_SYNC_INTERVAL_ADDRESSBOOKS, seconds.toString()) else if (authority == CalendarContract.AUTHORITY) accountManager.setUserData(account, KEY_SYNC_INTERVAL_CALENDARS, seconds.toString()) else if (TaskProvider.ProviderName.values().any { it.authority == authority }) accountManager.setUserData(account, KEY_SYNC_INTERVAL_TASKS, seconds.toString()) return true } fun getSavedAddressbooksSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_ADDRESSBOOKS)?.toLong() fun getSavedCalendarsSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_CALENDARS)?.toLong() fun getSavedTasksSyncInterval() = accountManager.getUserData(account, KEY_SYNC_INTERVAL_TASKS)?.toLong() fun getSyncWifiOnly() = Loading