diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7ab710945..cc2c02ee6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -609,10 +609,6 @@
android:name=".etebase.EteBaseCalendarSettingsActivity"
android:theme="@style/Tasks" />
-
-
diff --git a/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt b/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt
index dd78ac702..c1361ea12 100644
--- a/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt
+++ b/app/src/main/java/org/tasks/caldav/BaseCaldavCalendarSettingsActivity.kt
@@ -80,7 +80,7 @@ abstract class BaseCaldavCalendarSettingsActivity : BaseListSettingsActivity() {
get() = caldavCalendar == null
override val toolbarTitle: String
- get() = if (isNew) getString(R.string.new_list) else caldavCalendar!!.name!!
+ get() = if (isNew) getString(R.string.new_list) else caldavCalendar!!.name ?: ""
@OnTextChanged(R.id.name)
fun onNameChanged() {
diff --git a/app/src/main/java/org/tasks/data/CaldavAccount.kt b/app/src/main/java/org/tasks/data/CaldavAccount.kt
index 5f36281f4..6401c370a 100644
--- a/app/src/main/java/org/tasks/data/CaldavAccount.kt
+++ b/app/src/main/java/org/tasks/data/CaldavAccount.kt
@@ -47,6 +47,7 @@ class CaldavAccount : Parcelable {
@ColumnInfo(name = "cda_repeat")
var isSuppressRepeatingTasks = false
+ @Deprecated("use etebase")
@ColumnInfo(name = "cda_encryption_key")
@Transient
var encryptionKey: String? = null
@@ -78,6 +79,7 @@ class CaldavAccount : Parcelable {
return encryption.decrypt(password) ?: ""
}
+ @Deprecated("use etebase")
fun getEncryptionPassword(encryption: KeyStoreEncryption): String {
return encryption.decrypt(encryptionKey) ?: ""
}
diff --git a/app/src/main/java/org/tasks/etebase/AddEteBaseAccountViewModel.kt b/app/src/main/java/org/tasks/etebase/AddEteBaseAccountViewModel.kt
index f3658c78b..80a785948 100644
--- a/app/src/main/java/org/tasks/etebase/AddEteBaseAccountViewModel.kt
+++ b/app/src/main/java/org/tasks/etebase/AddEteBaseAccountViewModel.kt
@@ -1,23 +1,15 @@
package org.tasks.etebase
-import androidx.core.util.Pair
import androidx.hilt.lifecycle.ViewModelInject
-import com.etesync.journalmanager.UserInfoManager.UserInfo
import org.tasks.ui.CompletableViewModel
class AddEteBaseAccountViewModel @ViewModelInject constructor(
- private val clientProvider: EteBaseClientProvider): CompletableViewModel>() {
+ private val clientProvider: EteBaseClientProvider): CompletableViewModel() {
suspend fun addAccount(url: String, username: String, password: String) {
run {
- val token =
- clientProvider
- .forUrl(url, username, null, null)
- .setForeground()
- .getToken(password)
- Pair.create(
- clientProvider.forUrl(url, username, null, token!!).userInfo(),
- token
- )
+ clientProvider
+ .forUrl(url, username, password, foreground = true)
+ .getSession()
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/CreateUserInfoViewModel.kt b/app/src/main/java/org/tasks/etebase/CreateUserInfoViewModel.kt
deleted file mode 100644
index d7ca15277..000000000
--- a/app/src/main/java/org/tasks/etebase/CreateUserInfoViewModel.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.tasks.etebase
-
-import androidx.hilt.lifecycle.ViewModelInject
-import org.tasks.data.CaldavAccount
-import org.tasks.ui.CompletableViewModel
-
-class CreateUserInfoViewModel @ViewModelInject constructor(
- private val clientProvider: EteBaseClientProvider): CompletableViewModel() {
- suspend fun createUserInfo(caldavAccount: CaldavAccount, derivedKey: String) {
- run {
- clientProvider.forAccount(caldavAccount).createUserInfo(derivedKey)
- derivedKey
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EncryptionSettingsActivity.kt b/app/src/main/java/org/tasks/etebase/EncryptionSettingsActivity.kt
deleted file mode 100644
index 8a64567f9..000000000
--- a/app/src/main/java/org/tasks/etebase/EncryptionSettingsActivity.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-package org.tasks.etebase
-
-import android.app.Activity
-import android.content.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import androidx.activity.viewModels
-import androidx.appcompat.widget.Toolbar
-import androidx.lifecycle.lifecycleScope
-import at.bitfire.dav4jvm.exception.HttpException
-import butterknife.ButterKnife
-import butterknife.OnTextChanged
-import com.etesync.journalmanager.Constants.Companion.CURRENT_VERSION
-import com.etesync.journalmanager.Crypto.CryptoManager
-import com.etesync.journalmanager.Crypto.deriveKey
-import com.etesync.journalmanager.Exceptions.IntegrityException
-import com.etesync.journalmanager.Exceptions.VersionTooNewException
-import com.etesync.journalmanager.UserInfoManager
-import com.google.android.material.snackbar.Snackbar
-import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
-import org.tasks.R
-import org.tasks.Strings.isNullOrEmpty
-import org.tasks.data.CaldavAccount
-import org.tasks.databinding.ActivityEtesyncEncryptionSettingsBinding
-import org.tasks.injection.ThemedInjectingAppCompatActivity
-import org.tasks.security.KeyStoreEncryption
-import org.tasks.ui.DisplayableException
-import java.net.ConnectException
-import javax.inject.Inject
-
-@AndroidEntryPoint
-class EncryptionSettingsActivity : ThemedInjectingAppCompatActivity(), Toolbar.OnMenuItemClickListener {
- @Inject lateinit var encryption: KeyStoreEncryption
-
- private lateinit var binding: ActivityEtesyncEncryptionSettingsBinding
- private var userInfo: UserInfoManager.UserInfo? = null
- private var caldavAccount: CaldavAccount? = null
- private val createUserInfoViewModel: CreateUserInfoViewModel by viewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityEtesyncEncryptionSettingsBinding.inflate(layoutInflater)
- setContentView(binding.root)
- ButterKnife.bind(this)
- val intent = intent
- caldavAccount = intent.getParcelableExtra(EXTRA_ACCOUNT)
- userInfo = intent.getSerializableExtra(EXTRA_USER_INFO) as UserInfoManager.UserInfo
- if (userInfo == null) {
- binding.description.visibility = View.VISIBLE
- binding.repeatEncryptionPasswordLayout.visibility = View.VISIBLE
- }
- val toolbar = binding.toolbar.toolbar
- toolbar.title = if (caldavAccount == null) getString(R.string.add_account) else caldavAccount!!.name
- toolbar.navigationIcon = getDrawable(R.drawable.ic_outline_save_24px)
- toolbar.setNavigationOnClickListener { save() }
- toolbar.inflateMenu(R.menu.menu_help)
- toolbar.setOnMenuItemClickListener(this)
- themeColor.apply(toolbar)
- createUserInfoViewModel.observe(this, { returnDerivedKey(it) }, this::requestFailed)
- if (createUserInfoViewModel.inProgress) {
- showProgressIndicator()
- }
- }
-
- private fun showProgressIndicator() {
- binding.progressBar.progressBar.visibility = View.VISIBLE
- }
-
- private fun hideProgressIndicator() {
- binding.progressBar.progressBar.visibility = View.GONE
- }
-
- private fun requestInProgress() = binding.progressBar.progressBar.visibility == View.VISIBLE
-
- private fun returnDerivedKey(derivedKey: String) {
- hideProgressIndicator()
- val result = Intent()
- result.putExtra(EXTRA_DERIVED_KEY, derivedKey)
- setResult(Activity.RESULT_OK, result)
- finish()
- return
- }
-
- private fun save() = lifecycleScope.launch {
- if (requestInProgress()) {
- return@launch
- }
- val encryptionPassword = newEncryptionPassword
- val derivedKey = caldavAccount!!.getEncryptionPassword(encryption)
- if (isNullOrEmpty(encryptionPassword) && isNullOrEmpty(derivedKey)) {
- binding.encryptionPasswordLayout.error = getString(R.string.encryption_password_required)
- return@launch
- }
- if (userInfo == null) {
- val repeatEncryptionPassword = binding.repeatEncryptionPassword.text.toString().trim { it <= ' ' }
- if (encryptionPassword != repeatEncryptionPassword) {
- binding.repeatEncryptionPasswordLayout.error = getString(R.string.passwords_do_not_match)
- return@launch
- }
- }
- val key = if (isNullOrEmpty(encryptionPassword)) derivedKey else deriveKey(caldavAccount!!.username!!, encryptionPassword)
- val cryptoManager: CryptoManager
- cryptoManager = try {
- val version = if (userInfo == null) CURRENT_VERSION else userInfo!!.version!!.toInt()
- CryptoManager(version, key, "userInfo")
- } catch (e: VersionTooNewException) {
- requestFailed(e)
- return@launch
- } catch (e: IntegrityException) {
- requestFailed(e)
- return@launch
- }
- if (userInfo == null) {
- showProgressIndicator()
- createUserInfoViewModel.createUserInfo(caldavAccount!!, key)
- } else {
- try {
- userInfo!!.verify(cryptoManager)
- returnDerivedKey(key)
- } catch (e: IntegrityException) {
- binding.encryptionPasswordLayout.error = getString(R.string.encryption_password_wrong)
- }
- }
- }
-
- private fun requestFailed(t: Throwable) {
- hideProgressIndicator()
- when (t) {
- is HttpException -> showSnackbar(t.message)
- is DisplayableException -> showSnackbar(t.resId)
- is ConnectException -> showSnackbar(R.string.network_error)
- else -> showSnackbar(R.string.error_adding_account, t.message!!)
- }
- }
-
- private fun showSnackbar(resId: Int, vararg formatArgs: Any) =
- showSnackbar(getString(resId, *formatArgs))
-
- private fun showSnackbar(message: String?) =
- newSnackbar(message).show()
-
- private fun newSnackbar(message: String?): Snackbar {
- val snackbar = Snackbar.make(binding.rootLayout, message!!, 8000)
- .setTextColor(getColor(R.color.snackbar_text_color))
- .setActionTextColor(getColor(R.color.snackbar_action_color))
- snackbar
- .view
- .setBackgroundColor(getColor(R.color.snackbar_background))
- return snackbar
- }
-
- @OnTextChanged(R.id.repeat_encryption_password)
- fun onRpeatEncryptionPasswordChanged() {
- binding.repeatEncryptionPasswordLayout.error = null
- }
-
- @OnTextChanged(R.id.encryption_password)
- fun onEncryptionPasswordChanged() {
- binding.encryptionPasswordLayout.error = null
- }
-
- private val newEncryptionPassword: String
- get() = binding.encryptionPassword.text.toString().trim { it <= ' ' }
-
- override fun finish() {
- if (!requestInProgress()) {
- super.finish()
- }
- }
-
- override fun onMenuItemClick(item: MenuItem): Boolean {
- return if (item.itemId == R.id.menu_help) {
- startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_etesync))))
- true
- } else {
- onOptionsItemSelected(item)
- }
- }
-
- companion object {
- const val EXTRA_USER_INFO = "extra_user_info"
- const val EXTRA_ACCOUNT = "extra_account"
- const val EXTRA_DERIVED_KEY = "extra_derived_key"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EteBaseAccountSettingsActivity.kt b/app/src/main/java/org/tasks/etebase/EteBaseAccountSettingsActivity.kt
index 6e1091366..fbc59998f 100644
--- a/app/src/main/java/org/tasks/etebase/EteBaseAccountSettingsActivity.kt
+++ b/app/src/main/java/org/tasks/etebase/EteBaseAccountSettingsActivity.kt
@@ -1,28 +1,19 @@
package org.tasks.etebase
import android.app.Activity
-import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
-import androidx.core.util.Pair
-import androidx.lifecycle.lifecycleScope
import butterknife.OnCheckedChanged
-import com.etesync.journalmanager.Crypto.CryptoManager
-import com.etesync.journalmanager.Exceptions.IntegrityException
-import com.etesync.journalmanager.Exceptions.VersionTooNewException
-import com.etesync.journalmanager.UserInfoManager
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Constants
import org.tasks.caldav.BaseCaldavAccountSettingsActivity
import org.tasks.data.CaldavAccount
-import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
@@ -56,52 +47,27 @@ class EteBaseAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
override val description: Int
get() = R.string.etesync_account_description
- private suspend fun addAccount(userInfoAndToken: Pair) {
+ private suspend fun addAccount(session: String) {
caldavAccount = CaldavAccount()
caldavAccount!!.accountType = CaldavAccount.TYPE_ETEBASE
caldavAccount!!.uuid = UUIDHelper.newUUID()
- applyTo(caldavAccount!!, userInfoAndToken)
+ applyTo(caldavAccount!!, session)
}
- private suspend fun updateAccount(userInfoAndToken: Pair) {
+ private suspend fun updateAccount(session: String) {
caldavAccount!!.error = ""
- applyTo(caldavAccount!!, userInfoAndToken)
+ applyTo(caldavAccount!!, session)
}
- private suspend fun applyTo(account: CaldavAccount, userInfoAndToken: Pair) {
+ private suspend fun applyTo(account: CaldavAccount, session: String) {
hideProgressIndicator()
account.name = newName
account.url = newURL
account.username = newUsername
- val token = userInfoAndToken.second
- if (token != account.getPassword(encryption)) {
- account.password = encryption.encrypt(token!!)
+ if (session != account.getPassword(encryption)) {
+ account.password = encryption.encrypt(session)
}
- val userInfo = userInfoAndToken.first
- if (testUserInfo(userInfo)) {
- saveAccountAndFinish()
- } else {
- val intent = Intent(this, EncryptionSettingsActivity::class.java)
- intent.putExtra(EncryptionSettingsActivity.EXTRA_USER_INFO, userInfo)
- intent.putExtra(EncryptionSettingsActivity.EXTRA_ACCOUNT, account)
- startActivityForResult(intent, REQUEST_ENCRYPTION_PASSWORD)
- }
- }
-
- private fun testUserInfo(userInfo: UserInfoManager.UserInfo?): Boolean {
- val encryptionKey = caldavAccount!!.getEncryptionPassword(encryption)
- if (userInfo != null && !isNullOrEmpty(encryptionKey)) {
- try {
- val cryptoManager = CryptoManager(userInfo.version!!.toInt(), encryptionKey, "userInfo")
- userInfo.verify(cryptoManager)
- return true
- } catch (e: IntegrityException) {
- Timber.e(e)
- } catch (e: VersionTooNewException) {
- Timber.e(e)
- }
- }
- return false
+ saveAccountAndFinish()
}
@OnCheckedChanged(R.id.show_advanced)
@@ -133,10 +99,10 @@ class EteBaseAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
}
override val newURL: String
- get() {
- val url = super.newURL
- return if (isNullOrEmpty(url)) getString(R.string.etesync_url) else url // TODO: change to etebase url
- }
+ get() =
+ super.newURL
+ .takeIf { it.isNotBlank() }
+ ?: getString(R.string.etebase_url)
override val newPassword: String
get() = binding.password.text.toString().trim { it <= ' ' }
@@ -144,20 +110,6 @@ class EteBaseAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
override val helpUrl: String
get() = getString(R.string.url_etesync)
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == REQUEST_ENCRYPTION_PASSWORD) {
- if (resultCode == Activity.RESULT_OK) {
- lifecycleScope.launch {
- val key = data!!.getStringExtra(EncryptionSettingsActivity.EXTRA_DERIVED_KEY)!!
- caldavAccount!!.encryptionKey = encryption.encrypt(key)
- saveAccountAndFinish()
- }
- }
- } else {
- super.onActivityResult(requestCode, resultCode, data)
- }
- }
-
private suspend fun saveAccountAndFinish() {
if (caldavAccount!!.id == Task.NO_ID) {
caldavDao.insert(caldavAccount!!)
@@ -173,11 +125,7 @@ class EteBaseAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
}
override suspend fun removeAccount() {
- caldavAccount?.let { clientProvider.forAccount(it).invalidateToken() }
+ caldavAccount?.let { clientProvider.forAccount(it).logout() }
super.removeAccount()
}
-
- companion object {
- private const val REQUEST_ENCRYPTION_PASSWORD = 10101
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EteBaseClient.kt b/app/src/main/java/org/tasks/etebase/EteBaseClient.kt
index ffb567fb8..7ee099ae6 100644
--- a/app/src/main/java/org/tasks/etebase/EteBaseClient.kt
+++ b/app/src/main/java/org/tasks/etebase/EteBaseClient.kt
@@ -1,177 +1,159 @@
package org.tasks.etebase
-import androidx.core.util.Pair
-import at.bitfire.cert4android.CustomCertManager
-import com.etesync.journalmanager.*
-import com.etesync.journalmanager.Constants.Companion.CURRENT_VERSION
-import com.etesync.journalmanager.Crypto.AsymmetricKeyPair
-import com.etesync.journalmanager.Crypto.CryptoManager
+import android.content.Context
+import com.etebase.client.*
+import com.etebase.client.Collection
+import com.etesync.journalmanager.Exceptions
import com.etesync.journalmanager.Exceptions.IntegrityException
import com.etesync.journalmanager.Exceptions.VersionTooNewException
-import com.etesync.journalmanager.JournalManager.Journal
-import com.etesync.journalmanager.UserInfoManager.UserInfo.Companion.generate
-import com.etesync.journalmanager.model.CollectionInfo
-import com.etesync.journalmanager.model.CollectionInfo.Companion.fromJson
-import com.etesync.journalmanager.model.SyncEntry
-import com.google.common.collect.Lists
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import okhttp3.HttpUrl
-import okhttp3.OkHttpClient
import org.tasks.data.CaldavCalendar
+import org.tasks.data.CaldavDao
+import org.tasks.data.CaldavTask
+import org.tasks.time.DateTimeUtils.currentTimeMillis
import timber.log.Timber
import java.io.IOException
-import java.util.*
class EteBaseClient(
- private val customCertManager: CustomCertManager,
- private val username: String?,
- private val encryptionPassword: String?,
- private val token: String?,
- private val httpClient: OkHttpClient,
- private val httpUrl: HttpUrl
+ private val context: Context,
+ private val username: String,
+ private val etebase: Account,
+ private val caldavDao: CaldavDao
) {
- private val journalManager = JournalManager(httpClient, httpUrl)
+ private val cache = EtebaseLocalCache.getInstance(context, username)
@Throws(IOException::class, Exceptions.HttpException::class)
- suspend fun getToken(password: String?): String? = withContext(Dispatchers.IO) {
- JournalAuthenticator(httpClient, httpUrl).getAuthToken(username!!, password!!)
- }
+ fun getSession(): String = etebase.save(null)
@Throws(Exceptions.HttpException::class)
- suspend fun userInfo(): UserInfoManager.UserInfo? = withContext(Dispatchers.IO) {
- val userInfoManager = UserInfoManager(httpClient, httpUrl)
- userInfoManager.fetch(username!!)
- }
-
- @Throws(VersionTooNewException::class, IntegrityException::class)
- fun getCrypto(userInfo: UserInfoManager.UserInfo?, journal: Journal): CryptoManager {
- if (journal.key == null) {
- return CryptoManager(journal.version, encryptionPassword!!, journal.uid!!)
+ suspend fun getCollections(): List {
+ val collectionManager = etebase.collectionManager
+ val response = withContext(Dispatchers.IO) {
+ collectionManager.list(TYPE_TASKS)
}
- if (userInfo == null) {
- throw RuntimeException("Missing userInfo")
+ response.data.forEach {
+ cache.collectionSet(collectionManager, it)
}
- val cryptoManager = CryptoManager(userInfo.version!!.toInt(), encryptionPassword!!, "userInfo")
- val keyPair = AsymmetricKeyPair(userInfo.getContent(cryptoManager)!!, userInfo.pubkey!!)
- return CryptoManager(journal.version, keyPair, journal.key!!)
- }
-
- private fun convertJournalToCollection(userInfo: UserInfoManager.UserInfo?, journal: Journal): CollectionInfo? {
- return try {
- val cryptoManager = getCrypto(userInfo, journal)
- journal.verify(cryptoManager)
- val collection = fromJson(journal.getContent(cryptoManager))
- collection.updateFromJournal(journal)
- collection
- } catch (e: IntegrityException) {
- Timber.e(e)
- null
- } catch (e: VersionTooNewException) {
- Timber.e(e)
- null
+ response.removedMemberships.forEach {
+ cache.collectionUnset(collectionManager, it)
}
- }
-
- @Throws(Exceptions.HttpException::class)
- suspend fun getCalendars(userInfo: UserInfoManager.UserInfo?): Map = withContext(Dispatchers.IO) {
- val result: MutableMap = HashMap()
- for (journal in journalManager.list()) {
- val collection = convertJournalToCollection(userInfo, journal)
- if (collection != null) {
- if (TYPE_TASKS == collection.type) {
- Timber.v("Found %s", collection)
- result[journal] = collection
- } else {
- Timber.v("Ignoring %s", collection)
- }
- }
- }
- result
+ return cache.collectionList(collectionManager)
}
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
- suspend fun getSyncEntries(
- userInfo: UserInfoManager.UserInfo?,
- journal: Journal,
+ suspend fun fetchItems(
+ collection: Collection,
calendar: CaldavCalendar,
- callback: suspend (List>) -> Unit) = withContext(Dispatchers.IO) {
- val journalEntryManager = JournalEntryManager(httpClient, httpUrl, journal.uid!!)
- val crypto = getCrypto(userInfo, journal)
- var journalEntries: List
+ callback: suspend (Pair>) -> Unit
+ ) {
+ val itemManager = etebase.collectionManager.getItemManager(collection)
+ var stoken = calendar.ctag
do {
- journalEntries = journalEntryManager.list(crypto, calendar.ctag, MAX_FETCH)
- callback.invoke(journalEntries.map {
- Pair.create(it, SyncEntry.fromJournalEntry(crypto, it))
- })
- } while (journalEntries.size >= MAX_FETCH)
+ val items = withContext(Dispatchers.IO) {
+ itemManager.list(FetchOptions().stoken(stoken).limit(MAX_FETCH))
+ }
+ stoken = items.stoken
+ callback(Pair(stoken, items.data.toList()))
+ } while (!items.isDone)
}
- @Throws(Exceptions.HttpException::class)
- suspend fun pushEntries(journal: Journal, entries: List?, remoteCtag: String?) = withContext(Dispatchers.IO) {
- var remoteCtag = remoteCtag
- val journalEntryManager = JournalEntryManager(httpClient, httpUrl, journal.uid!!)
- for (partition in Lists.partition(entries!!, MAX_PUSH)) {
- journalEntryManager.create(partition, remoteCtag)
- remoteCtag = partition[partition.size - 1].uid
+ suspend fun updateItem(collection: Collection, task: CaldavTask, content: ByteArray): Item {
+ val itemManager = etebase.collectionManager.getItemManager(collection)
+ val item = cache.itemGet(itemManager, collection.uid, task.`object`!!)
+ ?: itemManager
+ .create(ItemMetadata().apply { name = task.remoteId!! }, "")
+ .apply {
+ task.`object` = uid
+ caldavDao.update(task)
+ }
+ item.meta = item.meta.let { meta ->
+ meta.mtime = currentTimeMillis()
+ meta
}
+ item.content = content
+ return item
}
- fun setForeground(): EteBaseClient {
- customCertManager.appInForeground = true
- return this
+ suspend fun deleteItem(collection: Collection, uid: String): Item? {
+ val itemManager = etebase.collectionManager.getItemManager(collection)
+ return cache.itemGet(itemManager, collection.uid, uid)?.apply { delete() }
}
- suspend fun invalidateToken() = withContext(Dispatchers.IO) {
+ suspend fun updateCache(collection: Collection, items: List- ) {
+ val itemManager = etebase.collectionManager.getItemManager(collection)
+ items.forEach { cache.itemSet(itemManager, collection.uid, it) }
+ }
+
+ suspend fun uploadChanges(collection: Collection, items: List
- ) {
+ val itemManager = etebase.collectionManager.getItemManager(collection)
+ withContext(Dispatchers.IO) {
+ itemManager.batch(items.toTypedArray())
+ }
+ }
+
+ suspend fun getItem(collection: Collection, uid: String): Item? =
+ cache.itemGet(
+ etebase.collectionManager.getItemManager(collection),
+ collection.uid,
+ uid
+ )
+
+ suspend fun logout() {
try {
- JournalAuthenticator(httpClient, httpUrl).invalidateAuthToken(token!!)
+ EtebaseLocalCache.clear(context, username)
+ withContext(Dispatchers.IO) {
+ etebase.logout()
+ }
} catch (e: Exception) {
Timber.e(e)
}
}
@Throws(VersionTooNewException::class, IntegrityException::class, Exceptions.HttpException::class)
- suspend fun makeCollection(name: String?, color: Int): String = withContext(Dispatchers.IO) {
- val uid = Journal.genUid()
- val collectionInfo = CollectionInfo()
- collectionInfo.displayName = name
- collectionInfo.type = TYPE_TASKS
- collectionInfo.uid = uid
- collectionInfo.selected = true
- collectionInfo.color = if (color == 0) null else color
- val crypto = CryptoManager(collectionInfo.version, encryptionPassword!!, uid)
- journalManager.create(Journal(crypto, collectionInfo.toJson(), uid))
- uid
- }
+ suspend fun makeCollection(name: String, color: Int) =
+ etebase
+ .collectionManager
+ .create(TYPE_TASKS, ItemMetadata(), "")
+ .let { setAndUpload(it, name, color) }
@Throws(VersionTooNewException::class, IntegrityException::class, Exceptions.HttpException::class)
- suspend fun updateCollection(calendar: CaldavCalendar, name: String?, color: Int): String = withContext(Dispatchers.IO) {
- val uid = calendar.url
- val journal = journalManager.fetch(uid!!)
- val userInfo = userInfo()
- val crypto = getCrypto(userInfo, journal)
- val collectionInfo = convertJournalToCollection(userInfo, journal)
- collectionInfo!!.displayName = name
- collectionInfo.color = if (color == 0) null else color
- journalManager.update(Journal(crypto, collectionInfo.toJson(), uid))
- uid
- }
+ suspend fun updateCollection(calendar: CaldavCalendar, name: String, color: Int) =
+ cache
+ .collectionGet(etebase.collectionManager, calendar.url!!)
+ .let { setAndUpload(it, name, color) }
@Throws(Exceptions.HttpException::class)
- suspend fun deleteCollection(calendar: CaldavCalendar) = withContext(Dispatchers.IO) {
- journalManager.delete(Journal.fakeWithUid(calendar.url!!))
- }
-
- @Throws(Exceptions.HttpException::class, VersionTooNewException::class, IntegrityException::class, IOException::class)
- suspend fun createUserInfo(derivedKey: String?) = withContext(Dispatchers.IO) {
- val cryptoManager = CryptoManager(CURRENT_VERSION, derivedKey!!, "userInfo")
- val userInfo: UserInfoManager.UserInfo = generate(cryptoManager, username!!)
- UserInfoManager(httpClient, httpUrl).create(userInfo)
+ suspend fun deleteCollection(calendar: CaldavCalendar) =
+ cache
+ .collectionGet(etebase.collectionManager, calendar.url!!)
+ .apply { delete() }
+ .let { setAndUpload(it) }
+
+ private suspend fun setAndUpload(
+ collection: Collection,
+ name: String? = null,
+ color: Int? = null
+ ): String {
+ collection.meta = collection.meta.let { meta ->
+ name?.let { meta.name = it }
+ color?.let { meta.color = it.toHexColor() }
+ meta
+ }
+ val collectionManager = etebase.collectionManager
+ withContext(Dispatchers.IO) {
+ collectionManager.upload(collection)
+ }
+ cache.collectionSet(collectionManager, collection)
+ return collection.uid
}
companion object {
- private const val TYPE_TASKS = "TASKS"
- private const val MAX_FETCH = 50
- private const val MAX_PUSH = 30
+ private const val TYPE_TASKS = "etebase.vtodo"
+ private const val MAX_FETCH = 30L
+
+ private fun Int.toHexColor(): String? = takeIf { this != 0 }?.let {
+ java.lang.String.format("#%06X", 0xFFFFFF and it)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EteBaseClientProvider.kt b/app/src/main/java/org/tasks/etebase/EteBaseClientProvider.kt
index 1c471ba9e..455236bc9 100644
--- a/app/src/main/java/org/tasks/etebase/EteBaseClientProvider.kt
+++ b/app/src/main/java/org/tasks/etebase/EteBaseClientProvider.kt
@@ -1,21 +1,28 @@
package org.tasks.etebase
import android.content.Context
+import android.os.Build
import at.bitfire.cert4android.CustomCertManager
-import com.etesync.journalmanager.util.TokenAuthenticator
+import com.etebase.client.Account
+import com.etebase.client.Client
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Interceptor
import okhttp3.OkHttpClient
+import okhttp3.Response
import okhttp3.internal.tls.OkHostnameVerifier
+import org.tasks.BuildConfig
import org.tasks.DebugNetworkInterceptor
import org.tasks.caldav.MemoryCookieStore
import org.tasks.data.CaldavAccount
+import org.tasks.data.CaldavDao
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption
+import java.io.IOException
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
+import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.net.ssl.SSLContext
@@ -24,46 +31,38 @@ class EteBaseClientProvider @Inject constructor(
@ApplicationContext private val context: Context,
private val encryption: KeyStoreEncryption,
private val preferences: Preferences,
- private val interceptor: DebugNetworkInterceptor
+ private val interceptor: DebugNetworkInterceptor,
+ private val caldavDao: CaldavDao
) {
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun forAccount(account: CaldavAccount): EteBaseClient {
return forUrl(
account.url!!,
- account.username,
- account.getEncryptionPassword(encryption),
+ account.username!!,
+ null,
account.getPassword(encryption))
}
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class)
- suspend fun forUrl(url: String, username: String?, encryptionPassword: String?, token: String?): EteBaseClient = withContext(Dispatchers.IO) {
- val customCertManager = newCertManager()
- EteBaseClient(
- customCertManager,
- username,
- encryptionPassword,
- token,
- createHttpClient(token, customCertManager),
- url.toHttpUrl()
- )
+ suspend fun forUrl(url: String, username: String, password: String?, session: String? = null, foreground: Boolean = false): EteBaseClient = withContext(Dispatchers.IO) {
+ val httpClient = createHttpClient(foreground)
+ val client = Client.create(httpClient, url)
+ val etebase = session
+ ?.let { Account.restore(client, it, null) }
+ ?: Account.login(client, username, password!!)
+ EteBaseClient(context, username, etebase, caldavDao)
}
- private suspend fun newCertManager() = withContext(Dispatchers.Default) {
- CustomCertManager(context)
- }
-
- private fun createHttpClient(
- token: String?,
- customCertManager: CustomCertManager,
- foreground: Boolean = false
- ): OkHttpClient {
- customCertManager.appInForeground = foreground
+ private suspend fun createHttpClient(foreground: Boolean): OkHttpClient {
+ val customCertManager = withContext(Dispatchers.Default) {
+ CustomCertManager(context, foreground)
+ }
val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(customCertManager), null)
val builder = OkHttpClient()
.newBuilder()
- .addNetworkInterceptor(TokenAuthenticator(null, token))
+ .addNetworkInterceptor(UserAgentInterceptor)
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
@@ -77,4 +76,18 @@ class EteBaseClientProvider @Inject constructor(
}
return builder.build()
}
+
+ private object UserAgentInterceptor : Interceptor {
+ private val userAgent = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (okhttp3) Android/${Build.VERSION.RELEASE}"
+
+ @Throws(IOException::class)
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val locale = Locale.getDefault()
+ val request = chain.request().newBuilder()
+ .header("User-Agent", userAgent)
+ .header("Accept-Language", locale.language + "-" + locale.country + ", " + locale.language + ";q=0.7, *;q=0.5")
+ .build()
+ return chain.proceed(request)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EteBaseSynchronizer.kt b/app/src/main/java/org/tasks/etebase/EteBaseSynchronizer.kt
index 184da6cb5..39ae15673 100644
--- a/app/src/main/java/org/tasks/etebase/EteBaseSynchronizer.kt
+++ b/app/src/main/java/org/tasks/etebase/EteBaseSynchronizer.kt
@@ -1,16 +1,13 @@
package org.tasks.etebase
import android.content.Context
-import androidx.core.util.Pair
+import android.graphics.Color
import at.bitfire.ical4android.ICalendar.Companion.prodId
+import com.etebase.client.Collection
+import com.etebase.client.Item
import com.etesync.journalmanager.Exceptions
import com.etesync.journalmanager.Exceptions.IntegrityException
import com.etesync.journalmanager.Exceptions.VersionTooNewException
-import com.etesync.journalmanager.JournalEntryManager
-import com.etesync.journalmanager.JournalEntryManager.Entry.Companion.getFakeWithUid
-import com.etesync.journalmanager.JournalManager.Journal
-import com.etesync.journalmanager.UserInfoManager
-import com.etesync.journalmanager.model.SyncEntry
import com.todoroo.astrid.helper.UUIDHelper
import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -31,7 +28,6 @@ import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.inject.Inject
-import kotlin.collections.HashSet
class EteBaseSynchronizer @Inject constructor(
@param:ApplicationContext private val context: Context,
@@ -58,10 +54,6 @@ class EteBaseSynchronizer @Inject constructor(
setError(account, context.getString(R.string.password_required))
return
}
- if (isNullOrEmpty(account.encryptionKey)) {
- setError(account, context.getString(R.string.encryption_password_required))
- return
- }
try {
synchronize(account)
} catch (e: KeyManagementException) {
@@ -80,36 +72,34 @@ class EteBaseSynchronizer @Inject constructor(
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class, Exceptions.HttpException::class, IntegrityException::class, VersionTooNewException::class)
private suspend fun synchronize(account: CaldavAccount) {
val client = clientProvider.forAccount(account)
- val userInfo = client.userInfo()
- val resources = client.getCalendars(userInfo)
- val uids: Set = resources.values.mapNotNull { it.uid }.toHashSet()
+ val collections = client.getCollections()
+ val uids = collections.map { it.uid }
Timber.d("Found uids: %s", uids)
- for (calendar in caldavDao.findDeletedCalendars(account.uuid!!, uids.toList())) {
+ for (calendar in caldavDao.findDeletedCalendars(account.uuid!!, uids)) {
taskDeleter.delete(calendar)
}
- for ((key, collection) in resources) {
+ for (collection in collections) {
val uid = collection.uid
- var calendar = caldavDao.getCalendarByUrl(account.uuid!!, uid!!)
- val colorInt = collection.color
- val color = colorInt ?: 0
+ var calendar = caldavDao.getCalendarByUrl(account.uuid!!, uid)
+ val meta = collection.meta
+ val color = meta.color?.let { Color.parseColor(it) } ?: 0
if (calendar == null) {
calendar = CaldavCalendar()
- calendar.name = collection.displayName
+ calendar.name = meta.name
calendar.account = account.uuid
calendar.url = collection.uid
calendar.uuid = UUIDHelper.newUUID()
calendar.color = color
caldavDao.insert(calendar)
} else {
- if (calendar.name != collection.displayName
- || calendar.color != color) {
- calendar.name = collection.displayName
+ if (calendar.name != meta.name || calendar.color != color) {
+ calendar.name = meta.name
calendar.color = color
caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList()
}
}
- sync(client, userInfo!!, calendar, key)
+ sync(client, calendar, collection)
}
setError(account, "")
}
@@ -126,59 +116,47 @@ class EteBaseSynchronizer @Inject constructor(
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
private suspend fun sync(
client: EteBaseClient,
- userInfo: UserInfoManager.UserInfo,
caldavCalendar: CaldavCalendar,
- journal: Journal) {
+ collection: Collection
+ ) {
Timber.d("sync(%s)", caldavCalendar)
val localChanges = HashMap()
for (task in caldavDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) {
localChanges[task.remoteId] = task
}
- var remoteCtag = journal.lastUid
+ val remoteCtag = collection.stoken
if (isNullOrEmpty(remoteCtag) || remoteCtag != caldavCalendar.ctag) {
- Timber.v("Applying remote changes")
- client.getSyncEntries(userInfo, journal, caldavCalendar) {
+ Timber.d("${caldavCalendar.name}: Applying remote changes")
+ client.fetchItems(collection, caldavCalendar) {
applyEntries(caldavCalendar, it, localChanges.keys)
+ client.updateCache(collection, it.second)
}
} else {
- Timber.d("%s up to date", caldavCalendar.name)
+ Timber.d("${caldavCalendar.name} up to date")
}
- val changes: MutableList = ArrayList()
+ val changes = ArrayList
- ()
for (task in caldavDao.getMoved(caldavCalendar.uuid!!)) {
- val vtodo = task.vtodo
- if (!isNullOrEmpty(vtodo)) {
- changes.add(SyncEntry(vtodo!!, SyncEntry.Actions.DELETE))
- }
+ client.deleteItem(collection, task.remoteId!!)?.let { changes.add(it) }
}
for (task in localChanges.values) {
val vtodo = task.vtodo
val existingTask = !isNullOrEmpty(vtodo)
if (task.isDeleted) {
if (existingTask) {
- changes.add(SyncEntry(vtodo!!, SyncEntry.Actions.DELETE))
+ client.deleteItem(collection, task.remoteId!!)?.let { changes.add(it) }
}
} else {
- changes.add(
- SyncEntry(
- String(iCal.toVtodo(task.caldavTask, task.task)),
- if (existingTask) SyncEntry.Actions.CHANGE else SyncEntry.Actions.ADD))
+ changes.add(client.updateItem(
+ collection,
+ task.caldavTask,
+ iCal.toVtodo(task.caldavTask, task.task)
+ ))
}
}
- remoteCtag = caldavCalendar.ctag
- val crypto = client.getCrypto(userInfo, journal)
- val updates: MutableList> = ArrayList()
- var previous: JournalEntryManager.Entry? = if (isNullOrEmpty(remoteCtag)) null else getFakeWithUid(remoteCtag!!)
- for (syncEntry in changes) {
- val entry = JournalEntryManager.Entry()
- entry.update(crypto, syncEntry.toJson(), previous)
- updates.add(Pair.create(entry, syncEntry))
- previous = entry
- }
- if (updates.size > 0) {
- Timber.v("Pushing local changes")
- client.pushEntries(journal, updates.mapNotNull { it.first }, remoteCtag)
- Timber.v("Applying local changes")
- applyEntries(caldavCalendar, updates, HashSet())
+ if (changes.isNotEmpty()) {
+ client.uploadChanges(collection, changes)
+ applyEntries(caldavCalendar, Pair(caldavCalendar.ctag, changes), HashSet())
+ client.updateCache(collection, changes)
}
Timber.d("UPDATE %s", caldavCalendar)
caldavDao.update(caldavCalendar)
@@ -188,37 +166,34 @@ class EteBaseSynchronizer @Inject constructor(
private suspend fun applyEntries(
caldavCalendar: CaldavCalendar,
- syncEntries: List>,
+ items: Pair>,
dirty: MutableSet) {
- for (entry in syncEntries) {
- val journalEntry = entry.first
- val syncEntry = entry.second
- val action = syncEntry!!.action
- val vtodo = syncEntry.content
- Timber.v("%s: %s", action, vtodo)
+ for (item in items.second) {
+ val vtodo = item.contentString
val task = fromVtodo(vtodo) ?: continue
val remoteId = task.uid
val caldavTask = caldavDao.getTaskByRemoteId(caldavCalendar.uuid!!, remoteId!!)
- when (action) {
- SyncEntry.Actions.ADD, SyncEntry.Actions.CHANGE -> if (dirty.contains(remoteId)) {
+ if (item.isDeleted) {
+ dirty.remove(remoteId)
+ if (caldavTask != null) {
+ if (caldavTask.isDeleted()) {
+ caldavDao.delete(caldavTask)
+ } else {
+ taskDeleter.delete(caldavTask.task)
+ }
+ }
+ } else {
+ caldavTask?.`object` = item.uid
+ if (dirty.contains(remoteId)) {
caldavTask!!.vtodo = vtodo
caldavDao.update(caldavTask)
} else {
- iCal.fromVtodo(caldavCalendar, caldavTask, task, vtodo, null, null)
- }
- SyncEntry.Actions.DELETE -> {
- dirty.remove(remoteId)
- if (caldavTask != null) {
- if (caldavTask.isDeleted()) {
- caldavDao.delete(caldavTask)
- } else {
- taskDeleter.delete(caldavTask.task)
- }
- }
+ iCal.fromVtodo(caldavCalendar, caldavTask, task, vtodo, item.uid, null)
}
}
- caldavCalendar.ctag = journalEntry!!.uid
- caldavDao.update(caldavCalendar)
}
+ caldavCalendar.ctag = items.first
+ Timber.d("Setting stoken to ${caldavCalendar.ctag}")
+ caldavDao.update(caldavCalendar)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/EtebaseLocalCache.kt b/app/src/main/java/org/tasks/etebase/EtebaseLocalCache.kt
new file mode 100644
index 000000000..de43fdf57
--- /dev/null
+++ b/app/src/main/java/org/tasks/etebase/EtebaseLocalCache.kt
@@ -0,0 +1,105 @@
+package org.tasks.etebase
+
+import android.content.Context
+import com.etebase.client.*
+import com.etebase.client.Collection
+import com.etebase.client.exceptions.EtebaseException
+import com.etebase.client.exceptions.UrlParseException
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import java.util.*
+
+class EtebaseLocalCache private constructor(context: Context, username: String) {
+ private val fsCache: FileSystemCache = FileSystemCache.create(context.filesDir.absolutePath, username)
+
+ private suspend fun clearUserCache() {
+ withContext(Dispatchers.IO) {
+ fsCache.clearUserCache()
+ }
+ }
+
+ suspend fun collectionList(colMgr: CollectionManager): List =
+ withContext(Dispatchers.IO) {
+ fsCache._unstable_collectionList(colMgr).filter { !it.isDeleted }
+ }
+
+ suspend fun collectionGet(colMgr: CollectionManager, colUid: String): Collection =
+ withContext(Dispatchers.IO) {
+ fsCache.collectionGet(colMgr, colUid)
+ }
+
+ suspend fun collectionSet(colMgr: CollectionManager, collection: Collection) {
+ if (collection.isDeleted) {
+ collectionUnset(colMgr, collection.uid)
+ } else {
+ withContext(Dispatchers.IO) {
+ fsCache.collectionSet(colMgr, collection)
+ }
+ }
+ }
+
+ suspend fun collectionUnset(colMgr: CollectionManager, collection: RemovedCollection) {
+ collectionUnset(colMgr, collection.uid())
+ }
+
+ private suspend fun collectionUnset(colMgr: CollectionManager, colUid: String) {
+ withContext(Dispatchers.IO) {
+ try {
+ fsCache.collectionUnset(colMgr, colUid)
+ } catch (e: UrlParseException) {
+ // Ignore, as it just means the file doesn't exist
+ }
+ }
+ }
+
+ suspend fun itemGet(itemMgr: ItemManager, colUid: String, itemUid: String): Item? =
+ withContext(Dispatchers.IO) {
+ // Need the try because the inner call doesn't return null on missing, but an error
+ try {
+ fsCache.itemGet(itemMgr, colUid, itemUid)
+ } catch (e: EtebaseException) {
+ null
+ }
+ }
+
+ suspend fun itemSet(itemMgr: ItemManager, colUid: String, item: Item) {
+ withContext(Dispatchers.IO) {
+ if (item.isDeleted) {
+ fsCache.itemUnset(itemMgr, colUid, item.uid)
+ } else {
+ fsCache.itemSet(itemMgr, colUid, item)
+ }
+ }
+ }
+
+ companion object {
+ private val localCacheCache: HashMap = HashMap()
+
+ fun getInstance(context: Context, username: String): EtebaseLocalCache {
+ synchronized(localCacheCache) {
+ val cached = localCacheCache[username]
+ return if (cached != null) {
+ cached
+ } else {
+ val ret = EtebaseLocalCache(context, username)
+ localCacheCache[username] = ret
+ ret
+ }
+ }
+ }
+
+ fun clear(context: Context) = runBlocking {
+ val users = synchronized(localCacheCache) {
+ localCacheCache.keys.toList()
+ }
+ users.forEach { clear(context, it) }
+ }
+
+ suspend fun clear(context: Context, username: String) {
+ val localCache = getInstance(context, username)
+ localCache.clearUserCache()
+ localCacheCache.remove(username)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/etebase/UpdateEteBaseAccountViewModel.kt b/app/src/main/java/org/tasks/etebase/UpdateEteBaseAccountViewModel.kt
index 80cacab90..ea100f5d0 100644
--- a/app/src/main/java/org/tasks/etebase/UpdateEteBaseAccountViewModel.kt
+++ b/app/src/main/java/org/tasks/etebase/UpdateEteBaseAccountViewModel.kt
@@ -1,30 +1,19 @@
package org.tasks.etebase
-import androidx.core.util.Pair
import androidx.hilt.lifecycle.ViewModelInject
-import com.etesync.journalmanager.UserInfoManager.UserInfo
import org.tasks.Strings.isNullOrEmpty
import org.tasks.ui.CompletableViewModel
class UpdateEteBaseAccountViewModel @ViewModelInject constructor(
- private val clientProvider: EteBaseClientProvider) : CompletableViewModel>() {
- suspend fun updateAccount(url: String, user: String, pass: String?, token: String) {
+ private val clientProvider: EteBaseClientProvider) : CompletableViewModel() {
+ suspend fun updateAccount(url: String, user: String, pass: String?, session: String) {
run {
if (isNullOrEmpty(pass)) {
- Pair.create(
- clientProvider.forUrl(url, user, null, token).setForeground().userInfo(),
- token
- )
+ clientProvider.forUrl(url, user, null, session, true).getSession()
} else {
- val newToken =
- clientProvider
- .forUrl(url, user, null, null)
- .setForeground()
- .getToken(pass)!!
- Pair.create(
- clientProvider.forUrl(url, user, null, newToken).userInfo(),
- newToken
- )
+ clientProvider
+ .forUrl(url, user, pass, foreground = true)
+ .getSession()
}
}
}
diff --git a/app/src/main/java/org/tasks/preferences/fragments/Advanced.kt b/app/src/main/java/org/tasks/preferences/fragments/Advanced.kt
index 0bfa85737..937827e1b 100644
--- a/app/src/main/java/org/tasks/preferences/fragments/Advanced.kt
+++ b/app/src/main/java/org/tasks/preferences/fragments/Advanced.kt
@@ -15,6 +15,7 @@ import org.tasks.PermissionUtil
import org.tasks.R
import org.tasks.calendars.CalendarEventProvider
import org.tasks.data.TaskDao
+import org.tasks.etebase.EtebaseLocalCache
import org.tasks.files.FileHelper
import org.tasks.injection.InjectingPreferenceFragment
import org.tasks.preferences.FragmentPermissionRequestor
@@ -205,8 +206,9 @@ class Advanced : InjectingPreferenceFragment() {
.newDialog()
.setMessage(R.string.EPr_delete_task_data_warning)
.setPositiveButton(R.string.EPr_delete_task_data) { _, _ ->
- context?.deleteDatabase(database.name)
- requireContext().deleteDatabase(database.name)
+ val context = requireContext()
+ context.deleteDatabase(database.name)
+ EtebaseLocalCache.clear(context)
restart()
}
.setNegativeButton(R.string.cancel, null)