diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAllAccountWorker.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAllAccountWorker.kt new file mode 100644 index 0000000000000000000000000000000000000000..ae3b0c1e4e07ca2993a9f0af069260812d968c36 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAllAccountWorker.kt @@ -0,0 +1,91 @@ +/* + * Copyright MURENA SAS 2023 + * 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 . + */ + +package at.bitfire.davdroid.syncadapter + +import android.content.Context +import androidx.hilt.work.HiltWorker +import androidx.work.Data +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import at.bitfire.davdroid.log.Logger +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import java.util.concurrent.TimeUnit +import java.util.logging.Level + +/** Right after login to the cloud account, there is no guarantee the sync setup is finished, so user's might not see the calendar, task, contact accounts setup. + * To resolve this we need to try syncing for a specific time limit with a multiplying wait time. + * We need to use OneTimeWorker instead of PeriodicTimeWorker, because PeriodicTimWorker requires >= 15 min between it's job. But in our case, the job delays are in seconds. + **/ +@HiltWorker +class SyncAllAccountWorker @AssistedInject constructor( + @Assisted appContext: Context, + @Assisted workerParameters: WorkerParameters, +) : Worker(appContext, workerParameters) { + + companion object { + private const val NAME = "sync-all-accounts" + private const val WAIT_SEC = "wait-sec" + private const val DEFAULT_WAIT_SEC: Long = 5 + private const val WAIT_LIMIT_IN_SEC: Long = 120 + + fun enqueue(context: Context, waitSec: Long = DEFAULT_WAIT_SEC) { + if (waitSec > WAIT_LIMIT_IN_SEC) { + return + } + + val data = Data.Builder() + data.putLong(WAIT_SEC, waitSec) + + WorkManager.getInstance(context).enqueueUniqueWork( + NAME, ExistingWorkPolicy.REPLACE, + OneTimeWorkRequestBuilder() + .setInitialDelay( + waitSec, + TimeUnit.SECONDS + ) // wait some time before sync all accounts + .setInputData(data.build()) + .build() + ) + } + } + + override fun doWork(): Result { + Logger.log.log(Level.FINE, "sync all accounts work started") + + val result = SyncUtils.syncAllAccounts(applicationContext) + if (!result) { + return Result.success() + } + + enqueueNext() + return Result.success() + } + + private fun enqueueNext() { + var waitSec = inputData.getLong(WAIT_SEC, DEFAULT_WAIT_SEC) + + if (waitSec < DEFAULT_WAIT_SEC) { + waitSec = DEFAULT_WAIT_SEC + } + + enqueue(applicationContext, waitSec * 2) + } +} diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncUtils.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncUtils.kt index 4b3278562109fd86ada6967424d54641e40a2ada..b2d663b69a0c74e2b5622cc4a5eb1700c0db3c20 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncUtils.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncUtils.kt @@ -14,10 +14,13 @@ import android.content.pm.PackageManager import android.graphics.drawable.BitmapDrawable import android.net.Uri import android.os.Build +import android.view.MenuItem +import androidx.annotation.StringRes import androidx.annotation.WorkerThread import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import at.bitfire.davdroid.Constants +import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.PermissionUtils import at.bitfire.davdroid.R @@ -157,4 +160,32 @@ object SyncUtils { } } -} \ No newline at end of file + private fun allAccounts(context: Context): List { + val accountManager = AccountManager.get(context) + val accounts = mutableListOf() + + accounts.addAll(getAccountsByType(context, accountManager, R.string.account_type)) + accounts.addAll(getAccountsByType(context, accountManager, R.string.eelo_account_type)) + accounts.addAll(getAccountsByType(context, accountManager, R.string.google_account_type)) + + return accounts + } + + private fun getAccountsByType(context: Context, accountManager: AccountManager, @StringRes type: Int): Array { + val accountType = context.getString(type) + return accountManager.getAccountsByType(accountType) + } + + fun syncAllAccounts(context: Context): Boolean { + val accounts = allAccounts(context) + if (accounts.isEmpty()) { + return false + } + + accounts.forEach { + DavUtils.requestSync(context, it) + } + + return true + } +} diff --git a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt index e24ab4f805f4f9371d37d9278648f106e4f5c05f..1a51058d8d5150aa1fa356e17067ab4df65d8e3a 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/AccountsActivity.kt @@ -4,28 +4,19 @@ package at.bitfire.davdroid.ui -import android.accounts.AccountManager import android.app.Activity import android.content.Intent -import android.content.SyncStatusObserver -import android.content.pm.ShortcutManager -import android.os.Build import android.os.Bundle import android.view.MenuItem import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat -import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.R import at.bitfire.davdroid.databinding.ActivityAccountsBinding +import at.bitfire.davdroid.syncadapter.SyncUtils import at.bitfire.davdroid.ui.setup.LoginActivity import com.google.android.material.navigation.NavigationView import dagger.hilt.android.AndroidEntryPoint -import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -92,14 +83,7 @@ class AccountsActivity: AppCompatActivity(), NavigationView.OnNavigationItemSele return true } - - private fun allAccounts() = - AccountManager.get(this).getAccountsByType(getString(R.string.account_type)) - fun syncAllAccounts(item: MenuItem? = null) { - val accounts = allAccounts() - for (account in accounts) - DavUtils.requestSync(this, account) + SyncUtils.syncAllAccounts(this) } - } diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt index 90c1e729784b84ccd4a154d9dade0eb04bfb02e0..235eb48eb65736de396ec6bd7d13adf66ee43a63 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/AccountDetailsFragment.kt @@ -24,7 +24,11 @@ import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import at.bitfire.davdroid.Constants import at.bitfire.davdroid.DavUtils import at.bitfire.davdroid.InvalidAccountException @@ -41,6 +45,7 @@ import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.settings.SettingsManager import at.bitfire.davdroid.syncadapter.AccountUtils +import at.bitfire.davdroid.syncadapter.SyncAllAccountWorker import at.bitfire.vcard4android.GroupMethod import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @@ -352,6 +357,7 @@ class AccountDetailsFragment : Fragment() { } DavUtils.requestSync(activity, account) + SyncAllAccountWorker.enqueue(context, 2) result.postValue(true) }