Remove native EteSync v1 support

EteSync v1 accounts can still be used with the EteSync app
pull/1715/head
Alex Baker 3 years ago
parent 462ec04091
commit f906b57d4e

@ -221,7 +221,6 @@ dependencies {
implementation("com.google.auth:google-auth-library-oauth2-http:0.26.0")
implementation("androidx.work:work-runtime:${Versions.work}")
implementation("androidx.work:work-runtime-ktx:${Versions.work}")
implementation("com.etesync:journalmanager:1.1.1")
implementation("com.etebase:client:2.3.2")
implementation("com.github.QuadFlask:colorpicker:0.0.15")
implementation("net.openid:appauth:0.8.1")

@ -497,24 +497,6 @@
copyrightHolder: Android Open Source Project
license: The Apache Software License, Version 2.0
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
- artifact: com.madgag.spongycastle:core:+
name: Spongy Castle Core
copyrightHolder: The Legion of the Bouncy Castle Inc.
license: Bouncy Castle Licence
licenseUrl: http://www.bouncycastle.org/licence.html
url: http://rtyley.github.io/spongycastle/
- artifact: com.etesync:journalmanager:+
name: EteSync JVM
copyrightHolder: Tom Hacohen
license: LGPL-3.0-only
licenseUrl: https://spdx.org/licenses/LGPL-3.0-only.html
url: https://www.etesync.com
- artifact: com.madgag.spongycastle:prov:+
name: Spongy Castle
copyrightHolder: The Legion of the Bouncy Castle Inc.
license: Bouncy Castle Licence
licenseUrl: http://www.bouncycastle.org/licence.html
url: http://rtyley.github.io/spongycastle/
- artifact: androidx.preference:preference:+
name: AndroidX Preference
copyrightHolder: Android Open Source Project

@ -638,10 +638,6 @@
<activity android:name=".tags.TagPickerActivity" />
<activity
android:name=".etesync.EncryptionSettingsActivity"
android:theme="@style/Tasks" />
<activity
android:name=".etebase.EtebaseCalendarSettingsActivity"
android:theme="@style/Tasks" />

@ -1186,48 +1186,6 @@
"normalizedLicense": "apache2",
"libraryName": "viewbinding"
},
{
"artifactId": {
"name": "core",
"group": "com.madgag.spongycastle",
"version": "+"
},
"copyrightHolder": "The Legion of the Bouncy Castle Inc.",
"copyrightStatement": "Copyright &copy; The Legion of the Bouncy Castle Inc. All rights reserved.",
"license": "Bouncy Castle Licence",
"licenseUrl": "http://www.bouncycastle.org/licence.html",
"normalizedLicense": "Bouncy Castle Licence",
"url": "http://rtyley.github.io/spongycastle/",
"libraryName": "Spongy Castle Core"
},
{
"artifactId": {
"name": "journalmanager",
"group": "com.etesync",
"version": "+"
},
"copyrightHolder": "Tom Hacohen",
"copyrightStatement": "Copyright &copy; Tom Hacohen. All rights reserved.",
"license": "LGPL-3.0-only",
"licenseUrl": "https://spdx.org/licenses/LGPL-3.0-only.html",
"normalizedLicense": "lgpl3",
"url": "https://www.etesync.com",
"libraryName": "EteSync JVM"
},
{
"artifactId": {
"name": "prov",
"group": "com.madgag.spongycastle",
"version": "+"
},
"copyrightHolder": "The Legion of the Bouncy Castle Inc.",
"copyrightStatement": "Copyright &copy; The Legion of the Bouncy Castle Inc. All rights reserved.",
"license": "Bouncy Castle Licence",
"licenseUrl": "http://www.bouncycastle.org/licence.html",
"normalizedLicense": "Bouncy Castle Licence",
"url": "http://rtyley.github.io/spongycastle/",
"libraryName": "Spongy Castle"
},
{
"artifactId": {
"name": "preference",

@ -51,11 +51,8 @@ internal class SubheaderViewHolder(
this.subheader = subheader
text.text = subheader.listingTitle
when {
subheader.error -> with(errorIcon) {
setColorFilter(ContextCompat.getColor(activity, R.color.overdue))
visibility = View.VISIBLE
}
subheader.subheaderType == SubheaderType.ETESYNC -> with(errorIcon) {
subheader.error || subheader.subheaderType == SubheaderType.ETESYNC ->
with(errorIcon) {
setColorFilter(ContextCompat.getColor(activity, R.color.overdue))
visibility = View.VISIBLE
}

@ -2,7 +2,6 @@ package org.tasks.analytics
object Constants {
const val SYNC_TYPE_CALDAV = "caldav"
const val SYNC_TYPE_ETESYNC = "etesync"
const val SYNC_TYPE_DAVX5 = "davx5"
const val SYNC_TYPE_GOOGLE_TASKS = "google_tasks"
const val SYNC_TYPE_ETESYNC_OT = "etesync_ot"

@ -1,20 +0,0 @@
package org.tasks.etesync
import androidx.core.util.Pair
import com.etesync.journalmanager.UserInfoManager.UserInfo
import dagger.hilt.android.lifecycle.HiltViewModel
import org.tasks.ui.CompletableViewModel
import javax.inject.Inject
@Deprecated("use etebase")
@HiltViewModel
class AddEteSyncAccountViewModel @Inject constructor(
private val client: EteSyncClient): CompletableViewModel<Pair<UserInfo, String>>() {
suspend fun addAccount(url: String, username: String, password: String) {
run {
client.setForeground()
val token = client.forUrl(url, username, null, null).getToken(password)
Pair.create(client.forUrl(url, username, null, token!!).userInfo(), token)
}
}
}

@ -1,18 +0,0 @@
package org.tasks.etesync
import dagger.hilt.android.lifecycle.HiltViewModel
import org.tasks.data.CaldavAccount
import org.tasks.ui.CompletableViewModel
import javax.inject.Inject
@Deprecated("use etebase")
@HiltViewModel
class CreateUserInfoViewModel @Inject constructor(
private val client: EteSyncClient): CompletableViewModel<String>() {
suspend fun createUserInfo(caldavAccount: CaldavAccount, derivedKey: String) {
run {
client.forAccount(caldavAccount).createUserInfo(derivedKey)
derivedKey
}
}
}

@ -1,191 +0,0 @@
package org.tasks.etesync
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import at.bitfire.dav4jvm.exception.HttpException
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.extensions.Context.openUri
import org.tasks.injection.ThemedInjectingAppCompatActivity
import org.tasks.security.KeyStoreEncryption
import org.tasks.ui.DisplayableException
import java.net.ConnectException
import javax.inject.Inject
@Deprecated("use etebase")
@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)
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()
}
binding.repeatEncryptionPassword.addTextChangedListener(
onTextChanged = { _, _, _, _ -> onRepeatEncryptionPasswordChanged() }
)
binding.encryptionPassword.addTextChangedListener(
onTextChanged = { _, _, _, _ -> onEncryptionPasswordChanged() }
)
}
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
}
private fun onRepeatEncryptionPasswordChanged() {
binding.repeatEncryptionPasswordLayout.error = null
}
private 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) {
openUri(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"
}
}

@ -1,42 +1,21 @@
package org.tasks.etesync
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.content.ContextCompat
import androidx.core.util.Pair
import androidx.lifecycle.lifecycleScope
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
@Deprecated("use etebase")
@AndroidEntryPoint
class EteSyncAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Toolbar.OnMenuItemClickListener {
@Inject lateinit var eteSyncClient: EteSyncClient
private val addAccountViewModel: AddEteSyncAccountViewModel by viewModels()
private val updateAccountViewModel: UpdateEteSyncAccountViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.repeat.visibility = View.GONE
binding.showAdvanced.visibility = View.VISIBLE
binding.showAdvanced.visibility = View.GONE
binding.description.visibility = View.VISIBLE
binding.description.setTextColor(ContextCompat.getColor(this, R.color.overdue))
binding.description.setText(description)
@ -46,90 +25,19 @@ class EteSyncAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
override val description = R.string.etesync_deprecated
override fun onResume() {
super.onResume()
if (!isFinishing) {
addAccountViewModel.observe(this, this::addAccount, this::requestFailed)
updateAccountViewModel.observe(this, this::updateAccount, this::requestFailed)
}
}
override fun onPause() {
super.onPause()
addAccountViewModel.removeObserver(this)
updateAccountViewModel.removeObserver(this)
}
private suspend fun addAccount(userInfoAndToken: Pair<UserInfoManager.UserInfo, String>) {
caldavAccount = CaldavAccount()
caldavAccount!!.accountType = CaldavAccount.TYPE_ETESYNC
caldavAccount!!.uuid = UUIDHelper.newUUID()
applyTo(caldavAccount!!, userInfoAndToken)
}
private suspend fun updateAccount(userInfoAndToken: Pair<UserInfoManager.UserInfo, String>) {
caldavAccount!!.error = ""
applyTo(caldavAccount!!, userInfoAndToken)
}
private suspend fun applyTo(account: CaldavAccount, userInfoAndToken: Pair<UserInfoManager.UserInfo, 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!!)
}
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
}
private fun updateUrlVisibility() {
binding.urlLayout.visibility = if (binding.showAdvanced.isChecked) View.VISIBLE else View.GONE
binding.urlLayout.visibility = View.GONE
}
override fun needsValidation(): Boolean {
return super.needsValidation() || isNullOrEmpty(caldavAccount!!.encryptionKey)
}
override fun needsValidation() = false
override suspend fun addAccount(url: String, username: String, password: String) =
addAccountViewModel.addAccount(url, username, password)
override fun hasChanges() = false
override suspend fun updateAccount(url: String, username: String, password: String) =
updateAccountViewModel.updateAccount(
url,
username,
if (PASSWORD_MASK == password) null else password,
caldavAccount!!.getPassword(encryption))
override suspend fun addAccount(url: String, username: String, password: String) {}
override suspend fun updateAccount() {
caldavAccount!!.name = newName
saveAccountAndFinish()
}
override suspend fun updateAccount(url: String, username: String, password: String) {}
override suspend fun updateAccount() {}
override val newURL: String
get() {
@ -141,41 +49,4 @@ class EteSyncAccountSettingsActivity : BaseCaldavAccountSettingsActivity(), Tool
get() = binding.password.text.toString().trim { it <= ' ' }
override val helpUrl = 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!!)
firebase.logEvent(
R.string.event_sync_add_account,
R.string.param_type to Constants.SYNC_TYPE_ETESYNC
)
} else {
caldavDao.update(caldavAccount!!)
}
setResult(Activity.RESULT_OK)
finish()
}
override suspend fun removeAccount() {
caldavAccount?.let { eteSyncClient.forAccount(it).invalidateToken() }
super.removeAccount()
}
companion object {
private const val REQUEST_ENCRYPTION_PASSWORD = 10101
}
}

@ -1,277 +0,0 @@
package org.tasks.etesync
import android.content.Context
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 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.etesync.journalmanager.util.TokenAuthenticator
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.internal.tls.OkHostnameVerifier
import org.tasks.DebugNetworkInterceptor
import org.tasks.caldav.MemoryCookieStore
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.preferences.Preferences
import org.tasks.security.KeyStoreEncryption
import timber.log.Timber
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
@Deprecated("use etebase")
class EteSyncClient {
private val encryption: KeyStoreEncryption
private val preferences: Preferences
private val interceptor: DebugNetworkInterceptor
private val username: String?
private val token: String?
private val encryptionPassword: String?
private val httpClient: OkHttpClient?
private val httpUrl: HttpUrl?
private val context: Context
private val journalManager: JournalManager?
private var foreground = false
@Inject
constructor(
@ApplicationContext context: Context,
encryption: KeyStoreEncryption,
preferences: Preferences,
interceptor: DebugNetworkInterceptor) {
this.context = context
this.encryption = encryption
this.preferences = preferences
this.interceptor = interceptor
username = null
token = null
encryptionPassword = null
httpClient = null
httpUrl = null
journalManager = null
}
private constructor(
context: Context,
encryption: KeyStoreEncryption,
preferences: Preferences,
interceptor: DebugNetworkInterceptor,
url: String?,
username: String?,
encryptionPassword: String?,
token: String?,
foreground: Boolean) {
this.context = context
this.encryption = encryption
this.preferences = preferences
this.interceptor = interceptor
this.username = username
this.encryptionPassword = encryptionPassword
this.token = token
this.foreground = foreground
val customCertManager = CustomCertManager(context)
customCertManager.appInForeground = 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))
.cookieJar(MemoryCookieStore())
.followRedirects(false)
.followSslRedirects(true)
.sslSocketFactory(sslContext.socketFactory, customCertManager)
.hostnameVerifier(hostnameVerifier)
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
if (preferences.isFlipperEnabled) {
interceptor.apply(builder)
}
httpClient = builder.build()
httpUrl = url?.toHttpUrlOrNull()
journalManager = JournalManager(httpClient, httpUrl!!)
}
@Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
suspend fun forAccount(account: CaldavAccount): EteSyncClient {
return forUrl(
account.url,
account.username,
account.getEncryptionPassword(encryption),
account.getPassword(encryption))
}
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class)
suspend fun forUrl(url: String?, username: String?, encryptionPassword: String?, token: String?): EteSyncClient = withContext(Dispatchers.IO) {
EteSyncClient(
context,
encryption,
preferences,
interceptor,
url,
username,
encryptionPassword,
token,
foreground)
}
@Throws(IOException::class, Exceptions.HttpException::class)
suspend fun getToken(password: String?): String? = withContext(Dispatchers.IO) {
JournalAuthenticator(httpClient!!, httpUrl!!).getAuthToken(username!!, password!!)
}
@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!!)
}
if (userInfo == null) {
throw RuntimeException("Missing userInfo")
}
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
}
}
@Throws(Exceptions.HttpException::class)
suspend fun getCalendars(userInfo: UserInfoManager.UserInfo?): Map<Journal, CollectionInfo> = withContext(Dispatchers.IO) {
val result: MutableMap<Journal, CollectionInfo> = 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
}
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
suspend fun getSyncEntries(
userInfo: UserInfoManager.UserInfo?,
journal: Journal,
calendar: CaldavCalendar,
callback: suspend (List<Pair<JournalEntryManager.Entry, SyncEntry>>) -> Unit) = withContext(Dispatchers.IO) {
val journalEntryManager = JournalEntryManager(httpClient!!, httpUrl!!, journal.uid!!)
val crypto = getCrypto(userInfo, journal)
var journalEntries: List<JournalEntryManager.Entry>
do {
journalEntries = journalEntryManager.list(crypto, calendar.ctag, MAX_FETCH)
callback(journalEntries.map {
Pair.create(it, SyncEntry.fromJournalEntry(crypto, it))
})
} while (journalEntries.size >= MAX_FETCH)
}
@Throws(Exceptions.HttpException::class)
suspend fun pushEntries(journal: Journal, entries: List<JournalEntryManager.Entry>?, remoteCtag: String?) = withContext(Dispatchers.IO) {
var remoteCtag = remoteCtag
val journalEntryManager = JournalEntryManager(httpClient!!, httpUrl!!, journal.uid!!)
for (partition in entries!!.chunked(MAX_PUSH)) {
journalEntryManager.create(partition, remoteCtag)
remoteCtag = partition[partition.size - 1].uid
}
}
fun setForeground() {
foreground = true
}
suspend fun invalidateToken() = withContext(Dispatchers.IO) {
try {
JournalAuthenticator(httpClient!!, httpUrl!!).invalidateAuthToken(token!!)
} 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
}
@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
}
@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)
}
companion object {
private const val TYPE_TASKS = "TASKS"
private const val MAX_FETCH = 50
private const val MAX_PUSH = 30
}
}

@ -1,225 +0,0 @@
package org.tasks.etesync
import android.content.Context
import androidx.core.util.Pair
import at.bitfire.ical4android.ICalendar.Companion.prodId
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
import net.fortuna.ical4j.model.property.ProdId
import org.tasks.BuildConfig
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.billing.Inventory
import org.tasks.caldav.iCalendar
import org.tasks.caldav.iCalendar.Companion.fromVtodo
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTaskContainer
import timber.log.Timber
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.inject.Inject
import kotlin.collections.HashSet
@Deprecated("use etebase")
class EteSynchronizer @Inject constructor(
@param:ApplicationContext private val context: Context,
private val caldavDao: CaldavDao,
private val localBroadcastManager: LocalBroadcastManager,
private val taskDeleter: TaskDeleter,
private val inventory: Inventory,
private val client: EteSyncClient,
private val iCal: iCalendar) {
companion object {
init {
prodId = ProdId("+//IDN tasks.org//android-" + BuildConfig.VERSION_CODE + "//EN")
}
}
suspend fun sync(account: CaldavAccount) {
Thread.currentThread().contextClassLoader = context.classLoader
if (!inventory.hasPro) {
setError(account, context.getString(R.string.requires_pro_subscription))
return
}
if (isNullOrEmpty(account.password)) {
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) {
setError(account, e.message)
} catch (e: NoSuchAlgorithmException) {
setError(account, e.message)
} catch (e: Exceptions.HttpException) {
setError(account, e.message)
} catch (e: IntegrityException) {
setError(account, e.message)
} catch (e: VersionTooNewException) {
setError(account, e.message)
}
}
@Throws(KeyManagementException::class, NoSuchAlgorithmException::class, Exceptions.HttpException::class, IntegrityException::class, VersionTooNewException::class)
private suspend fun synchronize(account: CaldavAccount) {
val client = client.forAccount(account)
val userInfo = client.userInfo()
val resources = client.getCalendars(userInfo)
val uids: Set<String> = resources.values.mapNotNull { it.uid }.toHashSet()
Timber.d("Found uids: %s", uids)
for (calendar in caldavDao.findDeletedCalendars(account.uuid!!, uids.toList())) {
taskDeleter.delete(calendar)
}
for ((key, collection) in resources) {
val uid = collection.uid
var calendar = caldavDao.getCalendarByUrl(account.uuid!!, uid!!)
val colorInt = collection.color
val color = colorInt ?: 0
if (calendar == null) {
calendar = CaldavCalendar()
calendar.name = collection.displayName
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
calendar.color = color
caldavDao.update(calendar)
localBroadcastManager.broadcastRefreshList()
}
}
sync(client, userInfo!!, calendar, key)
}
setError(account, "")
}
private suspend fun setError(account: CaldavAccount, message: String?) {
account.error = message
caldavDao.update(account)
localBroadcastManager.broadcastRefreshList()
if (!isNullOrEmpty(message)) {
Timber.e(message)
}
}
@Throws(IntegrityException::class, Exceptions.HttpException::class, VersionTooNewException::class)
private suspend fun sync(
client: EteSyncClient,
userInfo: UserInfoManager.UserInfo,
caldavCalendar: CaldavCalendar,
journal: Journal) {
Timber.d("sync(%s)", caldavCalendar)
val localChanges = HashMap<String?, CaldavTaskContainer>()
for (task in caldavDao.getCaldavTasksToPush(caldavCalendar.uuid!!)) {
localChanges[task.remoteId] = task
}
var remoteCtag = journal.lastUid
if (isNullOrEmpty(remoteCtag) || remoteCtag != caldavCalendar.ctag) {
Timber.v("Applying remote changes")
client.getSyncEntries(userInfo, journal, caldavCalendar) {
applyEntries(caldavCalendar, it, localChanges.keys)
}
} else {
Timber.d("%s up to date", caldavCalendar.name)
}
val changes: MutableList<SyncEntry> = ArrayList()
for (task in caldavDao.getMoved(caldavCalendar.uuid!!)) {
val vtodo = task.vtodo
if (!isNullOrEmpty(vtodo)) {
changes.add(SyncEntry(vtodo!!, SyncEntry.Actions.DELETE))
}
}
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))
}
} else {
changes.add(
SyncEntry(
String(iCal.toVtodo(task.caldavTask, task.task)),
if (existingTask) SyncEntry.Actions.CHANGE else SyncEntry.Actions.ADD))
}
}
remoteCtag = caldavCalendar.ctag
val crypto = client.getCrypto(userInfo, journal)
val updates: MutableList<Pair<JournalEntryManager.Entry, SyncEntry>> = 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())
}
Timber.d("UPDATE %s", caldavCalendar)
caldavDao.update(caldavCalendar)
caldavDao.updateParents(caldavCalendar.uuid!!)
localBroadcastManager.broadcastRefresh()
}
private suspend fun applyEntries(
caldavCalendar: CaldavCalendar,
syncEntries: List<Pair<JournalEntryManager.Entry, SyncEntry>>,
dirty: MutableSet<String?>) {
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)
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)) {
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)
}
}
}
}
caldavCalendar.ctag = journalEntry!!.uid
caldavDao.update(caldavCalendar)
}
}
}

@ -1,25 +0,0 @@
package org.tasks.etesync
import androidx.core.util.Pair
import com.etesync.journalmanager.UserInfoManager.UserInfo
import dagger.hilt.android.lifecycle.HiltViewModel
import org.tasks.Strings.isNullOrEmpty
import org.tasks.ui.CompletableViewModel
import javax.inject.Inject
@Deprecated("use etebase")
@HiltViewModel
class UpdateEteSyncAccountViewModel @Inject constructor(
private val client: EteSyncClient) : CompletableViewModel<Pair<UserInfo, String>>() {
suspend fun updateAccount(url: String, user: String, pass: String?, token: String) {
run {
client.setForeground()
if (isNullOrEmpty(pass)) {
Pair.create(client.forUrl(url, user, null, token).userInfo(), token)
} else {
val newToken = client.forUrl(url, user, null, null).getToken(pass)
Pair.create(client.forUrl(url, user, null, newToken).userInfo(), newToken)
}
}
}
}

@ -1,46 +0,0 @@
package org.tasks.jobs
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.WorkerParameters
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.*
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.analytics.Firebase
import org.tasks.data.CaldavAccount.Companion.TYPE_ETESYNC
import org.tasks.data.CaldavDao
import org.tasks.etesync.EteSynchronizer
import org.tasks.preferences.Preferences
@Deprecated("use etebase")
@HiltWorker
class SyncEteSyncWork @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
firebase: Firebase,
localBroadcastManager: LocalBroadcastManager,
preferences: Preferences,
private val caldavDao: CaldavDao,
private val eteSynchronizer: EteSynchronizer
) : SyncWork(context, workerParams, firebase, localBroadcastManager, preferences) {
override suspend fun enabled() = caldavDao.getAccounts(TYPE_ETESYNC).isNotEmpty()
override val syncStatus = R.string.p_sync_ongoing_etesync
override suspend fun doSync() {
firebase.logEvent(R.string.legacy_etesync)
etesyncJobs().awaitAll()
}
private suspend fun etesyncJobs(): List<Deferred<Unit>> = coroutineScope {
caldavDao.getAccounts(TYPE_ETESYNC)
.map {
async(Dispatchers.IO) {
eteSynchronizer.sync(it)
}
}
}
}

@ -20,9 +20,6 @@ interface WorkManager {
suspend fun caldavSync(immediate: Boolean)
@Deprecated("use etebase")
suspend fun eteSync(immediate: Boolean)
suspend fun eteBaseSync(immediate: Boolean)
suspend fun openTaskSync(immediate: Boolean)
@ -55,7 +52,6 @@ interface WorkManager {
const val TAG_MIDNIGHT_REFRESH = "tag_midnight_refresh"
const val TAG_SYNC_GOOGLE_TASKS = "tag_sync_google_tasks"
const val TAG_SYNC_CALDAV = "tag_sync_caldav"
@Deprecated("use etebase") const val TAG_SYNC_ETESYNC = "tag_sync_etesync"
const val TAG_SYNC_ETEBASE = "tag_sync_etebase"
const val TAG_SYNC_OPENTASK = "tag_sync_opentask"
const val TAG_BACKGROUND_SYNC_GOOGLE_TASKS = "tag_background_sync_google_tasks"

@ -39,7 +39,6 @@ import org.tasks.jobs.WorkManager.Companion.TAG_REFRESH
import org.tasks.jobs.WorkManager.Companion.TAG_REMOTE_CONFIG
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_CALDAV
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETEBASE
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETESYNC
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_GOOGLE_TASKS
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_OPENTASK
import org.tasks.jobs.WorkManager.Companion.TAG_UPDATE_PURCHASES
@ -103,9 +102,6 @@ class WorkManagerImpl constructor(
override suspend fun caldavSync(immediate: Boolean) =
sync(immediate, TAG_SYNC_CALDAV, SyncCaldavWork::class.java)
override suspend fun eteSync(immediate: Boolean) =
sync(immediate, TAG_SYNC_ETESYNC, SyncEteSyncWork::class.java)
override suspend fun eteBaseSync(immediate: Boolean) =
sync(immediate, TAG_SYNC_ETEBASE, SyncEtebaseWork::class.java)

@ -514,7 +514,6 @@ class Preferences @JvmOverloads constructor(
private val syncFlags = listOf(
R.string.p_sync_ongoing_google_tasks,
R.string.p_sync_ongoing_caldav,
R.string.p_sync_ongoing_etesync,
R.string.p_sync_ongoing_android)
}
}

@ -7,7 +7,6 @@ import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.data.CaldavAccount.Companion.TYPE_CALDAV
import org.tasks.data.CaldavAccount.Companion.TYPE_ETEBASE
import org.tasks.data.CaldavAccount.Companion.TYPE_ETESYNC
import org.tasks.data.CaldavAccount.Companion.TYPE_OPENTASKS
import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS
import org.tasks.data.CaldavDao
@ -17,7 +16,6 @@ import org.tasks.data.OpenTaskDao
import org.tasks.jobs.WorkManager
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_CALDAV
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETEBASE
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_ETESYNC
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_GOOGLE_TASKS
import org.tasks.jobs.WorkManager.Companion.TAG_SYNC_OPENTASK
import org.tasks.preferences.Preferences
@ -38,7 +36,6 @@ class SyncAdapters @Inject constructor(
private val scope = CoroutineScope(newSingleThreadExecutor().asCoroutineDispatcher() + SupervisorJob())
private val googleTasks = Debouncer(TAG_SYNC_GOOGLE_TASKS) { workManager.googleTaskSync(it) }
private val caldav = Debouncer(TAG_SYNC_CALDAV) { workManager.caldavSync(it) }
@Deprecated("use etebase") private val eteSync = Debouncer(TAG_SYNC_ETESYNC) { workManager.eteSync(it) }
private val eteBaseSync = Debouncer(TAG_SYNC_ETEBASE) { workManager.eteBaseSync(it) }
private val opentasks = Debouncer(TAG_SYNC_OPENTASK) { workManager.openTaskSync(it) }
private val syncStatus = Debouncer("sync_status") {
@ -62,9 +59,6 @@ class SyncAdapters @Inject constructor(
|| caldavDao.isAccountType(task.id, TYPE_TASKS)) {
caldav.sync(false)
}
if (caldavDao.isAccountType(task.id, TYPE_ETESYNC)) {
eteSync.sync(false)
}
if (caldavDao.isAccountType(task.id, TYPE_ETEBASE)) {
eteBaseSync.sync(false)
}
@ -89,7 +83,6 @@ class SyncAdapters @Inject constructor(
fun sync(immediate: Boolean) = scope.launch {
val googleTasksEnabled = async { isGoogleTaskSyncEnabled() }
val caldavEnabled = async { isCaldavSyncEnabled() }
val eteSyncEnabled = async { isEteSyncEnabled() }
val eteBaseEnabled = async { isEtebaseEnabled() }
val opentasksEnabled = async { isOpenTaskSyncEnabled() }
@ -101,10 +94,6 @@ class SyncAdapters @Inject constructor(
caldav.sync(immediate)
}
if (eteSyncEnabled.await()) {
eteSync.sync(immediate)
}
if (eteBaseEnabled.await()) {
eteBaseSync.sync(immediate)
}
@ -119,9 +108,6 @@ class SyncAdapters @Inject constructor(
private suspend fun isCaldavSyncEnabled() =
caldavDao.getAccounts(TYPE_CALDAV, TYPE_TASKS).isNotEmpty()
@Deprecated("use etebase")
private suspend fun isEteSyncEnabled() = caldavDao.getAccounts(TYPE_ETESYNC).isNotEmpty()
private suspend fun isEtebaseEnabled() = caldavDao.getAccounts(TYPE_ETEBASE).isNotEmpty()
private suspend fun isOpenTaskSyncEnabled() =

@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:descendantFocusability="beforeDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
<include
android:id="@+id/progress_bar"
layout="@layout/progress_view" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
android:orientation="vertical">
<TextView
android:id="@+id/description"
style="@style/TagSettingsRow"
android:text="@string/etesync_encryption_description"
android:visibility="gone"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/encryption_password_layout"
style="@style/TagSettingsRow"
android:hint="@string/encryption_password"
app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/encryption_password"
style="@style/TagSettingsEditText"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/repeat_encryption_password_layout"
style="@style/TagSettingsRow"
android:visibility="gone"
app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/repeat_encryption_password"
style="@style/TagSettingsEditText"
android:hint="@string/reenter_encryption_password"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -310,13 +310,11 @@
<string name="gcal_completed_title">%s (تم)</string>
<string name="CFC_importance_name">الأولوية…</string>
<string name="list">قائمة</string>
<string name="encryption_password">كلمة سرّ التشفير</string>
<string name="widget_due_date_below_title">تحت العنوان</string>
<string name="widget_due_date_after_title">بعد العنوان</string>
<string name="default_recurrence">التكرار الافتراضي</string>
<string name="default_tags">العلامات الافتراضيّة</string>
<string name="no_title">(لا عنوان)</string>
<string name="encryption_password_required">كلمة سرّ التشفير مطلوبة</string>
<string name="password_required">كلمة السرّ مطلوبة</string>
<string name="username_required">اسم المستعمل مطلوب</string>
<string name="pick_from_storage">انتقِ من التخزين</string>
@ -376,13 +374,10 @@
<string name="troubleshooting">استكشاف الأخطاء وإصلاحها</string>
<string name="documentation">التوثيق</string>
<string name="preferences_advanced">متقدم</string>
<string name="etesync_encryption_description">تذكّر كلمة سرّ التشفير التي تختارها، لا يمكن استرجاعها!</string>
<string name="choose_synchronization_service">اختر منصّة</string>
<string name="create_new_tag">أنشأ \"%s\"</string>
<string name="no_calendars_found">لم يوجد أيّ تقويم</string>
<string name="filter">تصفية</string>
<string name="passwords_do_not_match">كلمات السرّ لا تتّفق</string>
<string name="encryption_password_wrong">كلمة سر تشفير خاطئة</string>
<string name="move">انقل</string>
<string name="subtasks">مهام فرعيّة</string>
<plurals name="list_count">
@ -453,7 +448,6 @@
<string name="badges">الشارات</string>
<string name="error_adding_account">خطأ: %s</string>
<string name="url">رابط</string>
<string name="reenter_encryption_password">أكّد كلمة سرّ التشفير</string>
<string name="widget_due_date_hidden">مخفي</string>
<string name="widget_footer">تذييل</string>
<string name="reverse">عكس</string>

@ -544,12 +544,10 @@
<string name="upgrade_blurb_2">Прекарах хиляди часове в работа по Tasks и публикувам целия изходен код безплатно. За да поддържам работата си, някои възможности изискват абонамент</string>
<string name="widget_due_date_hidden">Скрито</string>
<string name="icon">Икона</string>
<string name="reenter_encryption_password">Потвърждаване на паролата за криптиране</string>
<string name="got_it">Добре</string>
<string name="select_all">Избор на всички</string>
<string name="name_your_price">Посочете своя цена</string>
<string name="decsync_selection_description">Синхронизация чрез файлове</string>
<string name="encryption_password">Парола за криптиране</string>
<string name="lists">Списъци</string>
<string name="filter_low_priority">Нисък приоритет</string>
<string name="filter_after_today">След днес</string>
@ -576,16 +574,13 @@
<string name="widget_on_click">При щракване</string>
<string name="whats_new">Какво е новото</string>
<string name="no_app_found">Няма приложение, което да обработи тази заявка</string>
<string name="encryption_password_wrong">Грешна парола за криптиране</string>
<string name="etesync_selection_description">Синхронизация, шифрована от край до край</string>
<string name="etesync_encryption_description">Запомнете паролата за шифроване, защото не може да бъде възстановена!</string>
<string name="preferences_look_and_feel">Външен вид и усещане</string>
<string name="troubleshooting">Отстраняване на неизправности</string>
<string name="subscription_benefits">Ползи за абонамент</string>
<string name="back">Назад</string>
<string name="now">Сега</string>
<string name="filter_criteria_unstarted">Незапочнати</string>
<string name="encryption_password_required">Изисква се парола за криптиране</string>
<string name="opacity_row">Непрозрачност на реда</string>
<string name="widget_footer">Долен колонтитул</string>
<string name="widget_due_date_below_title">Под заглавието</string>
@ -596,7 +591,6 @@
<string name="opacity_header">Непрозрачност на заглавието</string>
<string name="widget_due_date_reschedule">Пренасрочване на задачата</string>
<string name="widget_due_date_after_title">След заглавието</string>
<string name="passwords_do_not_match">Паролите не съвпадат</string>
<string name="widget_open_list">Отваряне на списък</string>
<string name="url">Адрес</string>
<string name="upgrade_customization_themes">Тапети и теми за ден/нощ</string>

@ -421,11 +421,6 @@
<string name="icon">Ikona</string>
<string name="button_upgrade">Zvýšit předplatné</string>
<string name="button_unsubscribe">Zrušit předplatné</string>
<string name="encryption_password_required">Zadejte heslo pro šifrování</string>
<string name="encryption_password">Heslo pro šifrování</string>
<string name="passwords_do_not_match">Zadání hesla se neshodují</string>
<string name="reenter_encryption_password">Zopakujte heslo pro šifrování</string>
<string name="encryption_password_wrong">Nesprávné heslo pro šifrování</string>
<string name="help_and_feedback">Nápověda a zpětná vazba</string>
<string name="pro_dashclock_extension">Dashclock rozšíření</string>
<string name="requires_pro_subscription">Vyžaduje PRO předplatné</string>
@ -472,7 +467,6 @@
<string name="show_advanced_settings">Zobrazit pokročilé možnosti</string>
<string name="caldav_account_description">Vyžaduje účet u poskytovatele s CalDAV službou nebo vlastní server. Vhodné poskytovatele najdete na tasks.org/caldav</string>
<string name="etesync_account_description">Vyžaduje účet na EteSync.com nebo vlastní server</string>
<string name="etesync_encryption_description">Dobře si zapamatujte své šifrovací heslo, protože jej není možné obnovit!</string>
<string name="preferences_look_and_feel">Vzhled a chování</string>
<string name="documentation">Dokumentace</string>
<string name="preferences_advanced">Pokročilé možnosti</string>

@ -174,7 +174,6 @@
<string name="documentation">Dokumentation</string>
<string name="preferences_advanced">Avanceret</string>
<string name="preferences_look_and_feel">Udseende</string>
<string name="etesync_encryption_description">Husk det krypteringskodeord, du vælger. Det kan ikke gendannes!</string>
<string name="etesync_account_description">Det kræver en konto hos EteSync.com eller en selv-hostet server</string>
<string name="caldav_account_description">Det kræver en konto hos en CalDAV-tjenesteudbyder eller en selv-hostet server. Find en tjenesteudbyder ved at gå til tasks.org/caldav</string>
<string name="show_advanced_settings">Vis avancerede indstillinger</string>
@ -264,8 +263,6 @@
<string name="notification_disable_battery_optimizations_description">Strømsparetilstand kan forsinke notifikationer</string>
<string name="error_adding_account">Fejl: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Bekræft krypteringskodeord</string>
<string name="encryption_password">Krypteringskodeord</string>
<string name="password">Kodeord</string>
<string name="user">Bruger</string>
<string name="add_account">Tilføj konto</string>
@ -316,11 +313,8 @@
<string name="url_invalid_scheme">Skal begynde med http(s)://</string>
<string name="url_host_name_required">Hostname er nødvendigt</string>
<string name="url_required">URL er nødvendig</string>
<string name="passwords_do_not_match">Kodeordene er ikke ens</string>
<string name="encryption_password_wrong">Forkert krypteringskodeord</string>
<string name="username_required">Brugernavn er nødvendigt</string>
<string name="password_required">Kodeord er nødvendigt</string>
<string name="encryption_password_required">Krypteringskodeord er nødvendigt</string>
<string name="name_cannot_be_empty">Navnet må ikke være tomt</string>
<string name="tag_already_exists">Tagget findes allerede</string>
<string name="send_anonymous_statistics_summary">Send anonymiseret brugerstatistik og fejlrapporter for at hjælpe med at forbedre Tasks. Ingen personlig data bliver indsamlet.</string>

@ -458,13 +458,8 @@
<string name="subtasks">Teilaufgaben</string>
<string name="subtasks_multilevel_google_task">Mehrstufige Teilaufgaben werden von Google Tasks nicht unterstützt</string>
<string name="enter_title_hint">Titel eingeben</string>
<string name="encryption_password_required">Passwort zur Verschlüsselung erforderlich</string>
<string name="encryption_password">Verschlüsselungspasswort</string>
<string name="display_name">Anzeigename</string>
<string name="this_feature_requires_a_subscription">Diese Funktion benötigt ein Abonnement</string>
<string name="passwords_do_not_match">Passwörter stimmen nicht überein</string>
<string name="reenter_encryption_password">Verschlüsselungspasswort bestätigen</string>
<string name="encryption_password_wrong">Falsches Passwort zur Verschlüsselung</string>
<string name="help_and_feedback">Hilfe und Rückmeldung</string>
<string name="theme_system_default">Systemstandard</string>
<string name="enter_tag_name">Schlagwort eingeben</string>
@ -476,7 +471,6 @@
<string name="show_advanced_settings">Erweiterte Einstellungen anzeigen</string>
<string name="caldav_account_description">Erfordert ein Konto bei einem CalDAV-Dienstanbieter oder einem selbst gehosteten Server. Finden Sie einen Dienstanbieter, indem Sie tasks.org/caldav besuchen</string>
<string name="etesync_account_description">Erfordert ein Konto bei EteSync.com oder einen selbst gehosteten Server</string>
<string name="etesync_encryption_description">Merken Sie sich das von Ihnen gewählte Passwort! Es kann nicht wiederhergestellt werden.</string>
<string name="preferences_look_and_feel">Erscheinungsbild</string>
<string name="preferences_advanced">Erweitert</string>
<string name="documentation">Dokumentation</string>

@ -260,7 +260,6 @@
<string name="default_list">Defaŭlta listo</string>
<string name="no_title">(Sen titolo)</string>
<string name="url_required">Ligilo bezonata</string>
<string name="passwords_do_not_match">Pasvortoj ne samas</string>
<string name="password_required">Pasvorto bezonata</string>
<string name="username_required">Uzantnomo bezonata</string>
<string name="name_cannot_be_empty">Nomo ne povas malpleni</string>

@ -459,21 +459,15 @@
<string name="enter_title_hint">Introducir título</string>
<string name="enter_tag_name">Introduzca el nombre de la etiqueta</string>
<string name="create_new_tag">Crear «%s»</string>
<string name="encryption_password_required">Se necesita una contraseña de cifrado</string>
<string name="encryption_password">Contraseña de cifrado</string>
<string name="display_name">Mostrar el nombre</string>
<string name="this_feature_requires_a_subscription">Esta característica requiere una suscripción</string>
<string name="choose_synchronization_service">Seleccione una plataforma</string>
<string name="google_tasks_selection_description">Servicio básico que se sincroniza con tu cuenta de Google</string>
<string name="caldav_selection_description">Sincronización basada en estándares abiertos de Internet</string>
<string name="etesync_selection_description">Sincronización cifrada de extremo a extremo</string>
<string name="passwords_do_not_match">Las contraseñas no coinciden</string>
<string name="reenter_encryption_password">Confirmar la contraseña de cifrado</string>
<string name="show_advanced_settings">Mostrar la configuración avanzada</string>
<string name="caldav_account_description">Requiere una cuenta con un proveedor de servicios CalDAV o un servidor autoalojado. Encuentre un proveedor de servicios visitando tasks.org/caldav</string>
<string name="etesync_account_description">Requiere una cuenta en EteSync.com o un servidor autoalojado</string>
<string name="etesync_encryption_description">Recuerde la contraseña de cifrado que elija. ¡No puede ser recuperada!</string>
<string name="encryption_password_wrong">Contraseña de cifrado errónea</string>
<string name="help_and_feedback">Ayuda y retroalimentación</string>
<string name="documentation">Documentación</string>
<string name="preferences_look_and_feel">Apariencia</string>

@ -459,21 +459,15 @@
<string name="enter_title_hint">Sartu izenburua</string>
<string name="enter_tag_name">Idatzi etiketaren izena</string>
<string name="create_new_tag">Sortu \"%s\"</string>
<string name="encryption_password_required">Zifratze pasahitza behar da</string>
<string name="encryption_password">Zifratze pasahitza</string>
<string name="display_name">Erakutsi izena</string>
<string name="this_feature_requires_a_subscription">Ezaugarri honek harpidetza eskatzen du</string>
<string name="choose_synchronization_service">Hautatu plataforma</string>
<string name="google_tasks_selection_description">Zure Google kontuarekin sinkronizatzen den oinarrizko zerbitzua</string>
<string name="caldav_selection_description">Internet estandar irekietan oinarritutako sinkronizazioa</string>
<string name="etesync_selection_description">Muturretik-muturrera zifratutako sinkronizazioa</string>
<string name="passwords_do_not_match">Pasahitzak ez datoz bat</string>
<string name="reenter_encryption_password">Baieztatu zifratze pasahitza</string>
<string name="show_advanced_settings">Erakutsi ezarpen aurreratuak</string>
<string name="caldav_account_description">CalDAV zerbitzu hornitzaile bateko edo zuk zeuk ostatatutako zerbitzu bateko kontua behar du. Aurkitu zerbitzu hornitzaile bat tasks.org/caldav helbidean</string>
<string name="etesync_account_description">EteSync.com zerbitzuko edo zuk zeuk ostatatutako zerbitzari bateko kontua behar du</string>
<string name="etesync_encryption_description">Gogoratu aukeratutako zifratze-pasahitza, ezin baita berreskuratu!</string>
<string name="encryption_password_wrong">Zifratze-pasahitz okerra</string>
<string name="help_and_feedback">Feedback eta laguntza</string>
<string name="documentation">Dokumentazioa</string>
<string name="preferences_look_and_feel">Itxura</string>

@ -403,7 +403,6 @@
<string name="notification_troubleshooting_summary">Paina tästä jos ilmoitusten kanssa on ongelmia</string>
<string name="documentation">Dokumentaatio</string>
<string name="preferences_look_and_feel">Ulkonäkö ja tunnelma</string>
<string name="etesync_encryption_description">Painathan salaukselle asettamasti salasanan muistiin, sillä sitä ei voi palauttaa tai nollata!</string>
<string name="etesync_selection_description">End-to-end-salattu synkronointi</string>
<string name="caldav_selection_description">Avoimiin verkkostandardeihin pohjautuva synkronointi</string>
<string name="choose_synchronization_service">Valitse alusta</string>
@ -449,8 +448,6 @@
<string name="list_separator_with_space">", "</string>
<string name="error_adding_account">Virhe: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Vahvista salauksen salasana</string>
<string name="encryption_password">Salauksen salasana</string>
<string name="password">Salasana</string>
<string name="user">Käyttäjä</string>
<string name="add_account">Lisää tili</string>
@ -459,9 +456,6 @@
<string name="icon">Kuvake</string>
<string name="url_invalid_scheme">On alettava http(s)://</string>
<string name="url_required">URL vaaditaan</string>
<string name="passwords_do_not_match">Salasanat eivät täsmää</string>
<string name="encryption_password_wrong">Väärä salauksen salasana</string>
<string name="encryption_password_required">Salauksen salasana vaaditaan</string>
<string name="password_required">Salasana vaaditaan</string>
<string name="username_required">Käyttäjänimi vaaditaan</string>
<string name="move">Siirrä</string>

@ -454,21 +454,15 @@
<string name="enter_title_hint">Entrer le titre</string>
<string name="enter_tag_name">Entrez le nom de l\'étiquette</string>
<string name="create_new_tag">Créer « %s »</string>
<string name="encryption_password_required">Mot de passe de chiffrement requis</string>
<string name="encryption_password">Mot de passe de chiffrement</string>
<string name="display_name">Afficher le nom</string>
<string name="this_feature_requires_a_subscription">Cette fonctionnalité nécessite un abonnement</string>
<string name="choose_synchronization_service">Sélectionnez une plate-forme</string>
<string name="google_tasks_selection_description">Service de base qui se synchronise avec votre compte Google</string>
<string name="caldav_selection_description">Synchronisation basée sur des normes d\'Internet ouvertes</string>
<string name="etesync_selection_description">Synchronisation du chiffrement de bout en bout</string>
<string name="passwords_do_not_match">Les mots de passe ne correspondent pas</string>
<string name="reenter_encryption_password">Confirmer le mot de passe de chiffrement</string>
<string name="show_advanced_settings">Afficher les paramètres avancés</string>
<string name="caldav_account_description">Nécessite un compte auprès d\'un fournisseur de services CalDAV ou un serveur auto-hébergé. Trouvez un fournisseur de services en visitant tasks.org/caldav</string>
<string name="etesync_account_description">Nécessite un compte sur EteSync.com ou un serveur auto-hébergé</string>
<string name="etesync_encryption_description">N\'oubliez pas le mot de passe de chiffrement que vous choisissez, il ne peut pas être récupéré !</string>
<string name="encryption_password_wrong">Mot de passe de chiffrement erroné</string>
<string name="help_and_feedback">Aide et remarques</string>
<string name="documentation">Documentation</string>
<string name="preferences_look_and_feel">Apparence</string>

@ -364,8 +364,6 @@
<string name="notification_disable_battery_optimizations_description">Optimiranje baterije može odgoditi obavijesti</string>
<string name="error_adding_account">Greška: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Potvrdi lozinku za šifriranje</string>
<string name="encryption_password">Lozinka za šifriranje</string>
<string name="password">Lozinka</string>
<string name="user">Korisnik</string>
<string name="add_account">Dodaj račun</string>
@ -417,9 +415,6 @@
<string name="url_invalid_scheme">Mora početi s http(s)://</string>
<string name="url_host_name_required">Naziv računala je obavezno</string>
<string name="url_required">URL-adresa je obavezna</string>
<string name="passwords_do_not_match">Lozinke se ne poklapaju</string>
<string name="encryption_password_wrong">Kriva lozinka za šifriranje</string>
<string name="encryption_password_required">Lozinka za šifriranje je obavezna</string>
<string name="password_required">Lozinka je obavezna</string>
<string name="username_required">Korisničko ime je obavezno</string>
<string name="name_cannot_be_empty">Ime ne smije biti prazno</string>
@ -634,7 +629,6 @@
<string name="troubleshooting">Rješavanje problema</string>
<string name="wearable_notifications_summary">Prikaži obavijesti na nosivim uređajima</string>
<string name="wearable_notifications">Obavijesti nosivih uređaja</string>
<string name="etesync_encryption_description">Zapamti odabranu lozinku za šifriranje, ne može se obnoviti!</string>
<string name="etesync_account_description">Zahtijeva račun na EteSync.com ili vlastiti poslužitelj</string>
<string name="widget_show_header">Pokaži zaglavlje</string>
<string name="widget_show_checkboxes">Pokaži potvrdne okvire</string>

@ -457,21 +457,15 @@
<string name="enter_title_hint">Cím megadása</string>
<string name="enter_tag_name">Címke neve</string>
<string name="create_new_tag">\"%s\" létrehozása</string>
<string name="encryption_password_required">A titkosításhoz jelszó szükséges</string>
<string name="encryption_password">Jelszó titkosításhoz</string>
<string name="display_name">Megjelenített név</string>
<string name="this_feature_requires_a_subscription">Ez a funkció csak előfizetéssel vehető igénybe</string>
<string name="choose_synchronization_service">Platform választása</string>
<string name="google_tasks_selection_description">Alapvető szolgáltatás, ami a Google accounttal szinkronizál</string>
<string name="caldav_selection_description">Nyílt Internetes szabványon alapuló szinkronizáció</string>
<string name="etesync_selection_description">Végponttól végpontig titkosított szinkronizáció</string>
<string name="passwords_do_not_match">A jelszavak nem egyeznek</string>
<string name="reenter_encryption_password">Titkosítás jelszavának megerősítése</string>
<string name="show_advanced_settings">Haladó beállítások mutatása</string>
<string name="caldav_account_description">CalDAV szolgáltatással rendelkező accountot, vagy saját szervert igényel. A szolgáltatók listája a tasks.org/caldav oldalon található</string>
<string name="etesync_account_description">EteSync.com accountot, vagy saját szervert igényel</string>
<string name="etesync_encryption_description">Jól jegyezd meg a titkosítás jelszavát! A jelszó elfelejtés esetén nem visszaállítható!</string>
<string name="encryption_password_wrong">Hibás titkosító jelszó</string>
<string name="help_and_feedback">Segítség és visszajelzés</string>
<string name="documentation">Dokumentáció</string>
<string name="preferences_look_and_feel">Kinézet</string>

@ -299,8 +299,6 @@
<string name="list_separator_with_space">", "</string>
<string name="dont_add_to_calendar">Jangan tambahkan ke kalender</string>
<string name="default_calendar">Bawaan kalender</string>
<string name="encryption_password_required">Kata sandi enkripsi diperlukan</string>
<string name="encryption_password">Kata sandi enkripsi</string>
<string name="import_summary_message">Berkas %1$s berisi %2$s.
\n
\n %3$s diimpor,
@ -375,13 +373,9 @@
<string name="caldav_selection_description">Sinkronisasi yang berdasarkan pada standar internet terbuka</string>
<string name="etesync_selection_description">Enkripsi klien-ke-klien bersumber terbuka</string>
<string name="theme_wallpaper">Latar</string>
<string name="passwords_do_not_match">Sandi tidak sama</string>
<string name="reenter_encryption_password">Konfirmasi sandi enkripsi</string>
<string name="show_advanced_settings">Tampilkan pengaturan lanjutan</string>
<string name="caldav_account_description">Membutuhkan akun penyedia layanan CalDAV atau server host-mandiri. Temukan penyedia layanan dengan mengunjungi tasks.org/caldav</string>
<string name="etesync_account_description">Membutuhkan akun EteSync.com atau server hosting-mandiri</string>
<string name="etesync_encryption_description">Ingat kata sandi enkripsi yang Anda pilih, sandi tidak akan dapat dipulihkan!</string>
<string name="encryption_password_wrong">Sandi enkripsi salah</string>
<string name="help_and_feedback">Bantuan &amp; umpanbalik</string>
<string name="documentation">Dokumentasi</string>
<string name="preferences_look_and_feel">Tampilan dan gaya</string>

@ -434,12 +434,7 @@
<string name="SSD_sort_my_order">Personalizzato</string>
<string name="EPr_temp_completed_tasks_not_showing">Le attività spariranno immediatamente dalla lista dopo il completamento</string>
<string name="desaturate_colors">Riduci saturazione colori</string>
<string name="etesync_encryption_description">Ricorda la password di cifratura che hai scelto, non può essere recuperata!</string>
<string name="button_upgrade">Passa alla versione premium</string>
<string name="reenter_encryption_password">Conferma password di cifratura</string>
<string name="encryption_password">Password di cifratura</string>
<string name="encryption_password_wrong">Password di cifratura sbagliata</string>
<string name="encryption_password_required">È richiesta la password di cifratura</string>
<string name="share">Condividi</string>
<string name="select_all">Seleziona tutto</string>
<string name="calendar_event_created">Creato evento nel calendario per %s</string>
@ -495,7 +490,6 @@
<string name="password">Password</string>
<string name="theme_system_default">Predefinito del sistema</string>
<string name="icon">Icona</string>
<string name="passwords_do_not_match">Le password non corrispondono</string>
<string name="menu_discard_changes">Ignora le modifiche</string>
<plurals name="subtask_count">
<item quantity="one">%d attività secondaria</item>

@ -493,8 +493,6 @@
<string name="this_feature_requires_a_subscription">תכונה זו דורשת מנוי</string>
<string name="help_and_feedback">עזרה ומשוב</string>
<string name="error_adding_account">שגיאה: %s</string>
<string name="reenter_encryption_password">אשר את סיסמת ההצפנה</string>
<string name="encryption_password">סיסמת הצפנה</string>
<string name="widget_due_date_hidden">מוסתר</string>
<string name="widget_due_date_below_title">מתחת לכותרת</string>
<string name="widget_due_date_after_title">לאחר הכותרת</string>
@ -525,8 +523,6 @@
<string name="auto_dismiss_datetime_widget">יישומון</string>
<string name="shortcut_pick_time">בחירת זמן</string>
<string name="chip_appearance_text_and_icon">טקסט וסמל</string>
<string name="encryption_password_wrong">סיסמת הצפנה שגויה</string>
<string name="encryption_password_required">דרושה סיסמת הצפנה</string>
<string name="menu_discard_changes">בטל שינויים</string>
<string name="subtasks">תתי משימות</string>
<string name="widget_show_dividers">הצג חוצצים</string>
@ -557,7 +553,6 @@
<string name="opacity_footer">אטימות הכותרת התחתונה</string>
<string name="opacity_header">אטימות כותרת</string>
<string name="opacity_row">אטימות שורה</string>
<string name="passwords_do_not_match">הססמאות אינן תואמות</string>
<string name="date_shortcut_tomorrow_night">מחר בלילה</string>
<string name="date_shortcut_tomorrow_evening">מחר בערב</string>
<string name="yesterday_lowercase">אתמול</string>
@ -580,7 +575,6 @@
<string name="wearable_notifications_summary">הצגת התראות על המחשוב הלביש שלך</string>
<string name="wearable_notifications">התראות על מחשוב לביש</string>
<string name="documentation">תיעוד</string>
<string name="etesync_encryption_description">עליך לזכור את ססמת ההצפנה שבחרת, אי אפשר לשחזר אותה!</string>
<string name="etesync_account_description">דורש חשבון אצל EteSync.com או שרת באירוח עצמי</string>
<string name="caldav_account_description">דורש חשבון עם ספק שירות CalDAV או שרת באירוח עצמי. ניתן למצוא ספק שירות תחת tasks.org/caldav</string>
<string name="tell_me_how_im_doing">נשמח לשמוע איך החוויה שלך</string>

@ -466,7 +466,6 @@
<string name="opacity_header">ヘッダーの透明度</string>
<string name="map_theme">地図のテーマ</string>
<string name="default_tags">デフォルトのタグ</string>
<string name="encryption_password_required">暗号化パスワードが必要です</string>
<string name="menu_discard_changes">変更を破棄</string>
<string name="date_shortcut_tomorrow_night">明日の夜</string>
<string name="date_shortcut_tomorrow_evening">明日の夕方</string>
@ -514,8 +513,6 @@
<string name="manage_drawer">ドロワーメニューをカスタマイズ</string>
<string name="display_name">表示名</string>
<string name="error_adding_account">エラー:%s</string>
<string name="reenter_encryption_password">暗号化パスワードを確認</string>
<string name="encryption_password">暗号化パスワード</string>
<string name="tasks_org_account">Tasks.org アカウント</string>
<string name="copied_to_clipboard">%sをクリップボードにコピーしました</string>
<string name="add_place">場所を追加</string>

@ -454,20 +454,14 @@
<string name="enter_title_hint">제목 입력</string>
<string name="enter_tag_name">태그 이름 입력</string>
<string name="create_new_tag">\"%s\" 생성</string>
<string name="encryption_password_required">암호화 비밀번호 필수</string>
<string name="encryption_password">암호화 비밀번호</string>
<string name="this_feature_requires_a_subscription">이 기능을 사용하려면 구독해야 합니다</string>
<string name="choose_synchronization_service">플랫폼 선택</string>
<string name="google_tasks_selection_description">기본 서비스는 구글 계정과 동기화합니다</string>
<string name="caldav_selection_description">개방형 인터넷 표준을 바탕으로 한 동기화</string>
<string name="etesync_selection_description">종단간 암호화된 동기화</string>
<string name="passwords_do_not_match">비밀번호가 일치하지 않습니다</string>
<string name="reenter_encryption_password">암호화 비밀번호 확인</string>
<string name="show_advanced_settings">고급 설정 표시</string>
<string name="caldav_account_description">CalDAV 서비스 제공업체나 자체호스팅 서버의 계정이 필요합니다. tasks.org/caldav에 방문하여 서비스 제공업체를 찾아보세요</string>
<string name="etesync_account_description">EteSync.com이나 자체호스팅 서버의 계정 필요</string>
<string name="etesync_encryption_description">선택하신 암호화 비밀번호를 기억하세요. 복구가 불가능합니다!</string>
<string name="encryption_password_wrong">잘못된 암호화 비밀번호</string>
<string name="share">공유</string>
<string name="select_all">모두 선택</string>
<string name="no_time">마감시간 없음</string>

@ -487,7 +487,6 @@
<string name="more_settings">Papildomi nustatymai</string>
<string name="disable_battery_optimizations">Išjungti baterijos optimizaciją</string>
<string name="preferences_look_and_feel">Išvaizda</string>
<string name="passwords_do_not_match">Slaptažodžiai nesutampa</string>
<string name="this_feature_requires_a_subscription">Šiai funkcijai reikalinga prenumerata</string>
<string name="icon">Piktograma</string>
<string name="chip_appearance_icon_only">Tik piktograma</string>
@ -620,13 +619,9 @@
<string name="button_upgrade">Atnaujinti prenumeratą</string>
<string name="help_and_feedback">Pagalba ir atsiliepimai</string>
<string name="error_adding_account">Klaida: %s</string>
<string name="reenter_encryption_password">Patvirtinkite šifravimo slaptažodį</string>
<string name="encryption_password">Šifravimo slaptažodis</string>
<string name="widget_due_date_reschedule">Perplanuoti užduotį</string>
<string name="widget_on_click">Paspaudus</string>
<string name="default_tags">Numatytosios etiketės</string>
<string name="encryption_password_wrong">Neteisingas šifravimo slaptažodis</string>
<string name="encryption_password_required">Reikalingas šifravimo slaptažodis</string>
<string name="location_radius_meters">%s m.</string>
<string name="subtasks">Antrinės užduotys</string>
<string name="widget_show_dividers">Rodyti skirtukus</string>
@ -724,7 +719,6 @@
<string name="wearable_notifications">Dėvimo įrenginio pranešimai</string>
<string name="google_tasks_selection_description">Paprasta paslauga, kuri atlieka sinchronizaciją su jūsų Google paskyra</string>
<string name="notification_troubleshooting_summary">Bakstelėkite čia, jei kyla problemų dėl pranešimų</string>
<string name="etesync_encryption_description">Prisiminkite pasirinktą šifravimo slaptažodį, jo negalima bus atkurti!</string>
<string name="etesync_account_description">Reikia paskyros EteSync.com arba asmeninis serveris</string>
<string name="caldav_account_description">Reikia paskyros pas CalDAV paslaugų teikėją arba turėti asmeninį serverį. Paslaugų teikėją raskite apsilankę tasks.org/caldav</string>
<string name="decsync_selection_description">Failais paremta sinchronizacija</string>

@ -459,18 +459,12 @@
<string name="subtasks_multilevel_google_task">Gjøremålssteg med flere nivåer støttes ikke av Google Tasks</string>
<string name="enter_tag_name">Skriv inn etikettnavn</string>
<string name="create_new_tag">Opprett «%s»</string>
<string name="encryption_password_required">Krypteringspassord kreves</string>
<string name="encryption_password">Krypteringspassord</string>
<string name="display_name">Visningsnavn</string>
<string name="this_feature_requires_a_subscription">Denne funksjonen krever abonnement</string>
<string name="choose_synchronization_service">Velg en plattform</string>
<string name="google_tasks_selection_description">Enkel tjeneste som synkroniserer med Google-kontoen din</string>
<string name="passwords_do_not_match">Passordene samsvarer ikke</string>
<string name="reenter_encryption_password">Bekreft krypteringspassord</string>
<string name="show_advanced_settings">Vis avanserte innstillinger</string>
<string name="etesync_account_description">Krever en konto hos EteSync.com eller selvdrevet tjener</string>
<string name="etesync_encryption_description">Husk krypteringspassordet du velger, det kan ikke gjenopprettes!</string>
<string name="encryption_password_wrong">Feil krypteringspassord</string>
<string name="help_and_feedback">Hjelp og tilbakemelding</string>
<string name="documentation">Dokumentasjon</string>
<string name="preferences_advanced">Avansert</string>

@ -454,21 +454,15 @@
<string name="enter_title_hint">Voer titel in</string>
<string name="enter_tag_name">Voer label naam in</string>
<string name="create_new_tag">Maak \"%s\" aan</string>
<string name="encryption_password_required">Coderingswachtwoord vereist</string>
<string name="encryption_password">Coderingswachtwoord</string>
<string name="display_name">Weergavenaam</string>
<string name="this_feature_requires_a_subscription">Deze functionaliteit vereist een abonnement</string>
<string name="choose_synchronization_service">Selecteer een platform</string>
<string name="google_tasks_selection_description">Basisservice die synchroniseert met uw Google-account</string>
<string name="caldav_selection_description">Synchronisatie gebaseerd op open internetstandaarden</string>
<string name="etesync_selection_description">End-to-end versleutelde synchronisatie</string>
<string name="passwords_do_not_match">Wachtwoorden komen niet overeen</string>
<string name="reenter_encryption_password">Bevestig coderingswachtwoord</string>
<string name="show_advanced_settings">Toon geavanceerde instellingen</string>
<string name="caldav_account_description">Vereist een account bij een CalDAV dienstverlener of een eigen server. Vind een dienstverlener door tasks.org/caldav te bezoeken</string>
<string name="etesync_account_description">Vereist een account op EteSync.com of een eigen server</string>
<string name="etesync_encryption_description">Onthoud het coderingswachtwoord dat je kiest, want het kan niet worden hersteld!</string>
<string name="encryption_password_wrong">Fout coderingswachtwoord</string>
<string name="help_and_feedback">Hulp &amp; feedback</string>
<string name="documentation">Documentatie</string>
<string name="preferences_look_and_feel">Uiterlijk</string>

@ -470,21 +470,15 @@
<string name="enter_title_hint">Wprowadź tytuł</string>
<string name="enter_tag_name">Wprowadź nazwę tagu</string>
<string name="create_new_tag">Stwórz \"%s\"</string>
<string name="encryption_password_required">Wymagane hasło szyfrowania</string>
<string name="encryption_password">Hasło szyfrowania</string>
<string name="display_name">Imię</string>
<string name="this_feature_requires_a_subscription">Ta funkcja wymaga subskrypcji</string>
<string name="choose_synchronization_service">Wybierz platformę</string>
<string name="google_tasks_selection_description">Podstawowy serwis, synchronizujący dane z Twoim kontem Google</string>
<string name="caldav_selection_description">Synchronizacja oparta na otwartych standardach</string>
<string name="etesync_selection_description">Szyfrowana synchronizacja end-to-end</string>
<string name="passwords_do_not_match">Hasła nie zgadzają się</string>
<string name="reenter_encryption_password">Potwierdź hasło szyfrowania</string>
<string name="show_advanced_settings">Pokaż ustawienia zaawansowane</string>
<string name="caldav_account_description">Wymaga konta u dostawcy usług CalDAV lub własnego serwera. Znajdź dostawcę usług na tasks.org/caldav</string>
<string name="etesync_account_description">Wymaga konta na EteSync.com lub własnego serwera</string>
<string name="etesync_encryption_description">Zapamiętaj wybrane hasło szyfrowania, nie da się go odzyskać!</string>
<string name="encryption_password_wrong">Nieprawidłowe hasło szyfrowania</string>
<string name="help_and_feedback">Pomoc i opinie</string>
<string name="documentation">Dokumentacja</string>
<string name="preferences_look_and_feel">Wygląd</string>

@ -471,7 +471,6 @@
<string name="documentation">Documentação</string>
<string name="preferences_advanced">Avançado</string>
<string name="preferences_look_and_feel">Interface e experiência do usuário</string>
<string name="etesync_encryption_description">Lembre-se da senha de encriptação que você escolheu, ela não pode ser recuperada!</string>
<string name="etesync_account_description">Precisa de uma conta com EteSync.com ou uma instância desse serviço auto-hospedada</string>
<string name="caldav_account_description">Precisa de uma conta com um provedor CalDAV. Encontre um provedor visitando tasks.org/caldav</string>
<string name="show_advanced_settings">Mostrar configurações avançadas</string>
@ -503,8 +502,6 @@
<string name="list_separator_with_space">", "</string>
<string name="error_adding_account">Erro: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Confirme a senha de criptografia</string>
<string name="encryption_password">Senha de criptografia</string>
<string name="widget_due_date_hidden">Escondido</string>
<string name="widget_due_date_below_title">Abaixo do título</string>
<string name="widget_due_date_after_title">Depois do título</string>
@ -520,9 +517,6 @@
<string name="opacity_footer">Opacidade do rodapé</string>
<string name="opacity_row">Opacidade da Linha</string>
<string name="opacity_header">Opacidade do Cabeçalho</string>
<string name="passwords_do_not_match">Senhas não conferem</string>
<string name="encryption_password_wrong">Senha de encriptação errada</string>
<string name="encryption_password_required">Senha de encriptação necessária</string>
<string name="date_shortcut_tomorrow_night">Amanhã à noite</string>
<string name="date_shortcut_tomorrow_evening">Amanhã à tardinha</string>
<string name="location_radius_meters">%s m</string>

@ -531,20 +531,14 @@
<string name="preferences_look_and_feel">Aparência</string>
<string name="documentation">Documentação</string>
<string name="help_and_feedback">Ajuda e comentários</string>
<string name="encryption_password_wrong">A palavra-passe de encriptação está errada</string>
<string name="etesync_encryption_description">Lembre-se da palavra-passe de encriptação que escolheu, ela não pode ser recuperada!</string>
<string name="etesync_account_description">Precisa de uma conta no EteSync.com ou de um servidor auto-alojado</string>
<string name="caldav_account_description">Precisa de uma conta com um fornecedor CalDAV ou de um servidor auto-alojado. Encontre um fornecedor visitando tasks.org/caldav</string>
<string name="show_advanced_settings">Mostrar configurações avançadas</string>
<string name="reenter_encryption_password">Confirme a palavra-passe de encriptação</string>
<string name="passwords_do_not_match">As palavras-passe não coincidem</string>
<string name="etesync_selection_description">Sincronização com encriptação ponta-a-ponta</string>
<string name="google_tasks_selection_description">Serviço básico que sincroniza com a sua conta Google</string>
<string name="choose_synchronization_service">Selecione uma plataforma</string>
<string name="this_feature_requires_a_subscription">Este recurso necessita de uma subscrição</string>
<string name="display_name">Mostrar nome</string>
<string name="encryption_password">Palavra-passe de encriptação</string>
<string name="encryption_password_required">É necessária uma palavra-passe de encriptação</string>
<string name="create_new_tag">Criar \"%s\"</string>
<string name="enter_tag_name">Digite o nome da etiqueta</string>
<string name="enter_title_hint">Digite um título</string>

@ -155,8 +155,6 @@
<string name="notification_disable_battery_optimizations_description">Optimizările bateriei pot întârzia notificările</string>
<string name="error_adding_account">Eroare: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Confirmați parola de criptare</string>
<string name="encryption_password">Parola de criptare</string>
<string name="password">Parola</string>
<string name="user">Utilizator</string>
<string name="add_account">Adăugați un cont</string>
@ -211,8 +209,6 @@
<string name="url_invalid_scheme">Trebuie să înceapă cu http(s)://</string>
<string name="url_host_name_required">Numele de gazdă necesar</string>
<string name="url_required">URL necesar</string>
<string name="encryption_password_wrong">Parolă de criptare greșită</string>
<string name="encryption_password_required">Parolă de criptare necesară</string>
<string name="password_required">Parolă necesară</string>
<string name="username_required">Nume de utilizator necesar</string>
<string name="name_cannot_be_empty">Numele nu poate fi gol</string>
@ -644,7 +640,6 @@
<string name="documentation">Documentație</string>
<string name="preferences_advanced">Avansat</string>
<string name="preferences_look_and_feel">Aspect și senzație</string>
<string name="etesync_encryption_description">Țineți minte parola de criptare pe care o alegeți, aceasta nu poate fi recuperată!</string>
<string name="etesync_account_description">Necesită un cont cu EteSync.com sau un server auto-găzduit</string>
<string name="caldav_account_description">Necesită un cont la un furnizor de servicii CalDAV sau un server găzduit de sine stătător. Găsiți un furnizor de servicii vizitând tasks.org/caldav</string>
<string name="show_advanced_settings">Afișați setările avansate</string>
@ -700,7 +695,6 @@
<string name="requires_pro_subscription">Necesită un abonament pro</string>
<string name="pro_dashclock_extension">Extinderea ceasului de bord</string>
<string name="license_summary">Tasks este un software libre open-source, licențiat sub licența GNU General Public License v3.0</string>
<string name="passwords_do_not_match">Parolele nu se potrivesc</string>
<string name="TPl_notification">Cronometre active pentru %s!</string>
<string name="delete_tag_confirmation">Ștergeți %s\?</string>
<string name="new_list">Creați o nouă listă</string>

@ -475,21 +475,15 @@
<string name="enter_title_hint">Введите название</string>
<string name="enter_tag_name">Введите имя тега</string>
<string name="create_new_tag">Создать \"%s\"</string>
<string name="encryption_password_required">Требуется пароль шифрования</string>
<string name="encryption_password">Пароль шифрования</string>
<string name="display_name">Отображаемое название</string>
<string name="this_feature_requires_a_subscription">Эта функция требует подписки</string>
<string name="choose_synchronization_service">Выберите платформу</string>
<string name="google_tasks_selection_description">Простой сервис, который синхронизируется с Вашим аккаунтом Google</string>
<string name="caldav_selection_description">Синхронизация, основанная на открытых стандартах</string>
<string name="etesync_selection_description">Сквозная зашифрованная синхронизация</string>
<string name="passwords_do_not_match">Пароли не совпадают</string>
<string name="reenter_encryption_password">Подтвердите пароль шифрования</string>
<string name="show_advanced_settings">Показать расширенные настройки</string>
<string name="caldav_account_description">Требует аккаунта у провайдера CalDAV или собственного сервера. Список провайдеров можно найти на tasks.org/caldav</string>
<string name="etesync_account_description">Требует аккаунта на EteSync.com или собственного сервера</string>
<string name="etesync_encryption_description">Запомните выбранный пароль шифрования, его нельзя будет восстановить!</string>
<string name="encryption_password_wrong">Неверный пароль шифрования</string>
<string name="help_and_feedback">Помощь и обратная связь</string>
<string name="documentation">Документация</string>
<string name="preferences_look_and_feel">Внешний вид</string>

@ -186,8 +186,6 @@
<string name="badges">ලාංඡන</string>
<string name="notification_disable_battery_optimizations_description">බැටරි ප්‍රශස්තිකරණය මඟින් දැනුම්දීම් ප්‍රමාද විය හැක</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">සංකේතන මුරපදය තහවුරු කරන්න</string>
<string name="encryption_password">සංකේතන මුරපදය</string>
<string name="password">මුරපදය</string>
<string name="user">පරිශීලක</string>
<string name="add_account">ගිණුම එක් කරන්න</string>
@ -241,7 +239,6 @@
<string name="documentation">ප්‍රලේඛනය</string>
<string name="preferences_advanced">උසස්</string>
<string name="preferences_look_and_feel">පෙනුම හා පහසුව</string>
<string name="etesync_encryption_description">ඔබ තෝරාගත් සංකේතාත්මක මුරපදය මතක තබා ගන්න, එය නැවත ලබා ගත නොහැක!</string>
<string name="etesync_account_description">EteSync.com හෝ ස්වයං සත්කාරක සේවාදායකයක් සමඟ ගිණුමක් අවශ්‍ය වේ</string>
<string name="caldav_account_description">CalDAV සේවා සැපයුම්කරුවෙකු හෝ ස්වයං සත්කාරක සේවාදායකයක් සමඟ ගිණුමක් අවශ්‍ය වේ. tasks.org/caldav වෙත පිවිසීමෙන් සේවා සපයන්නෙකු සොයා ගන්න</string>
<string name="show_advanced_settings">උසස් සැකසුම් පෙන්වන්න</string>
@ -629,9 +626,6 @@
<string name="url_invalid_scheme">http(s):// වලින් ආරම්භවිය යුතුය</string>
<string name="url_host_name_required">ධාරක නාමය (Hostname) අවශ්‍යයි</string>
<string name="url_required">URL අවශ්‍යයි</string>
<string name="passwords_do_not_match">මුර පද නොගැලපේ</string>
<string name="encryption_password_wrong">වැරදි සංකේතාත්මක මුරපදයකි</string>
<string name="encryption_password_required">සංකේතාත්මක මුරපදය අවශ්‍යයි</string>
<string name="password_required">මුරපදය අවශ්‍යයි</string>
<string name="username_required">පරිශීලක නාමය අවශ්‍යයි</string>
<string name="name_cannot_be_empty">නම හිස්ව තැබිය නොහැකිය</string>

@ -632,7 +632,6 @@
<string name="wearable_notifications_summary">Visa meddelanden på din wearable</string>
<string name="wearable_notifications">Meddelanden som kan bäras</string>
<string name="preferences_look_and_feel">Utseende</string>
<string name="etesync_encryption_description">Kom ihåg krypteringslösenordet du väljer, det kan inte återställas!</string>
<string name="davx5_selection_description">Synkronisera dina uppgifter med DAVx⁵-appen</string>
<string name="decsync_selection_description">Filbaserad synkronisering</string>
<string name="etesync_selection_description">Krypterad synkronisering från slut till slut</string>
@ -649,8 +648,6 @@
<string name="whats_new">Vad är nytt</string>
<string name="this_feature_requires_a_subscription">Denna funktion kräver en prenumeration</string>
<string name="subscription">Prenumeration</string>
<string name="reenter_encryption_password">Bekräfta krypteringslösenordet</string>
<string name="encryption_password">Krypteringslösenord</string>
<string name="widget_due_date_below_title">Under rubriken</string>
<string name="widget_due_date_after_title">Efter titeln</string>
<string name="widget_due_date_reschedule">Omplanera uppgiften</string>
@ -663,9 +660,6 @@
<string name="opacity_header">Opacitet för rubriken</string>
<string name="default_recurrence">Standardåterkomst</string>
<string name="default_tags">Standardtaggar</string>
<string name="passwords_do_not_match">Lösenorden stämmer inte överens</string>
<string name="encryption_password_wrong">Felaktigt krypteringslösenord</string>
<string name="encryption_password_required">Krypteringslösenord krävs</string>
<string name="no_app_found">Ingen app kunde hantera den här begäran</string>
<string name="filter_criteria_unstarted">Ej påbörjad</string>
<string name="show_unstarted">Visa ostartat</string>

@ -233,7 +233,6 @@
<string name="documentation">ஆவணம்</string>
<string name="preferences_advanced">மேம்படுத்தபட்ட</string>
<string name="preferences_look_and_feel">பார்க்கவும் உணரவும்</string>
<string name="etesync_encryption_description">நீங்கள் தேர்ந்தெடுத்த குறியாக்க கடவுச்சொல்லை நினைவில் கொள்ளுங்கள், அதை மீட்டெடுக்க முடியாது!</string>
<string name="etesync_account_description">EteSync.com அல்லது சுய ஹோஸ்ட் செய்த சேவையகத்துடன் கணக்கு தேவை</string>
<string name="caldav_account_description">CalDAV சேவை வழங்குநர் அல்லது சுய ஹோஸ்ட் செய்த சேவையகத்துடன் கணக்கு தேவை. Tasks.org/caldav ஐப் பார்வையிடுவதன் மூலம் ஒரு சேவை வழங்குநரைக் கண்டறியவும்</string>
<string name="show_advanced_settings">மேம்பட்ட அமைப்புகளைக் காட்டு</string>
@ -324,8 +323,6 @@
<string name="notification_disable_battery_optimizations_description">பேட்டரி மேம்படுத்தல்கள் அறிவிப்புகளை தாமதப்படுத்தக்கூடும்</string>
<string name="error_adding_account">பிழை: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">குறியாக்க கடவுச்சொல்லை உறுதிப்படுத்தவும்</string>
<string name="encryption_password">குறியாக்க கடவுச்சொல்</string>
<string name="password">கடவுச்சொல்</string>
<string name="user">பயனர்</string>
<string name="add_account">கணக்கு சேர்க்க</string>
@ -377,9 +374,6 @@
<string name="url_invalid_scheme">Http (கள்) உடன் தொடங்க வேண்டும்: //</string>
<string name="url_host_name_required">ஹோஸ்ட்பெயர் தேவை</string>
<string name="url_required">URL தேவை</string>
<string name="passwords_do_not_match">கடவுச்சொற்கள் பொருந்தவில்லை</string>
<string name="encryption_password_wrong">தவறான குறியாக்க கடவுச்சொல்</string>
<string name="encryption_password_required">குறியாக்க கடவுச்சொல் தேவை</string>
<string name="password_required">கடவுச்சொல் தேவை</string>
<string name="username_required">பயனர்பெயர் தேவை</string>
<string name="name_cannot_be_empty">பெயர் காலியாக இருக்க முடியாது</string>

@ -177,7 +177,6 @@
<string name="documentation">เอกสาร</string>
<string name="preferences_advanced">ดึก</string>
<string name="preferences_look_and_feel">ลักษณะและความรู้สึก</string>
<string name="etesync_encryption_description">จํารหัสผ่านการเข้ารหัสที่คุณเลือกมันไม่สามารถกู้คืนได้!</string>
<string name="etesync_account_description">ต้องการบัญชีผู้ใช้ที่มี EteSync.com หรือเซิร์ฟเวอร์ที่โฮสต์ด้วยตนเอง</string>
<string name="caldav_account_description">จําเป็นต้องมีบัญชีผู้ใช้กับผู้ให้บริการ CalDAV หรือเซิร์ฟเวอร์ที่โฮสต์ด้วยตนเอง ค้นหาผู้ให้บริการโดยไปที่ tasks.org/caldav</string>
<string name="show_advanced_settings">แสดงการตั้งค่าขั้นสูง</string>
@ -287,8 +286,6 @@
<string name="notification_disable_battery_optimizations_description">การเพิ่มประสิทธิภาพแบตเตอรี่อาจทําให้การแจ้งเตือนล่าช้า</string>
<string name="error_adding_account">ข้อผิดพลาด: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">ยืนยันรหัสผ่านการเข้ารหัส</string>
<string name="encryption_password">รหัสผ่านการเข้ารหัสลับ</string>
<string name="password">รหัสผ่าน</string>
<string name="user">ผู้ใช้</string>
<string name="add_account">เพิ่มบัญชี</string>
@ -344,9 +341,6 @@
<string name="url_invalid_scheme">ต้องเริ่มต้นด้วย http(s)://</string>
<string name="url_host_name_required">ต้องการชื่อโฮสต์</string>
<string name="url_required">URL ที่ต้องการ</string>
<string name="passwords_do_not_match">รหัสผ่านไม่ตรงกัน</string>
<string name="encryption_password_wrong">รหัสผ่านการเข้ารหัสลับไม่ถูกต้อง</string>
<string name="encryption_password_required">ต้องใช้รหัสผ่านการเข้ารหัส</string>
<string name="password_required">ต้องใช้รหัสผ่าน</string>
<string name="username_required">ต้องการชื่อผู้ใช้</string>
<string name="name_cannot_be_empty">ชื่อไม่สามารถปล่อยให้ว่างได้</string>

@ -459,21 +459,15 @@
<string name="enter_title_hint">Başlık girin</string>
<string name="enter_tag_name">Etiket adı girin</string>
<string name="create_new_tag">\"%s\" oluştur</string>
<string name="encryption_password_required">Şifreleme parolası gerekli</string>
<string name="encryption_password">Şifreleme parolası</string>
<string name="display_name">Gösterim adı</string>
<string name="this_feature_requires_a_subscription">Bu özellik abonelik gerektiriyor</string>
<string name="choose_synchronization_service">Platform seç</string>
<string name="google_tasks_selection_description">Google hesabınızla eşzamanlayan basit servis</string>
<string name="caldav_selection_description">ık internet standartlarında eşzamanlama</string>
<string name="etesync_selection_description">Uçtan uca şifreli eşzamanlama</string>
<string name="passwords_do_not_match">Parolalar eşleşmiyor</string>
<string name="reenter_encryption_password">Şifreleme parolasını doğrula</string>
<string name="show_advanced_settings">Gelişmiş ayarları göster</string>
<string name="caldav_account_description">CalDAV servis sağlayıcılı hesap veya kendi kendine barındırılan sunucu gerekir. tasks.org/caldav adresinden servis sağlayıcı bulun</string>
<string name="etesync_account_description">EteSync.com hesabı veya kendi kendine barındırılan sunucu gerektirir</string>
<string name="etesync_encryption_description">Seçtiğiniz şifreleme parolasını anımsayın, bu kurtarılamazdır!</string>
<string name="encryption_password_wrong">Yanlış şifreleme parolası</string>
<string name="help_and_feedback">Yardım ve geri bildirim</string>
<string name="documentation">Belgelendirme</string>
<string name="preferences_look_and_feel">Görünüm ve his</string>

@ -455,7 +455,6 @@
<string name="error_adding_account">Помилка: %s</string>
<string name="url">URL</string>
<string name="icon">Піктограма</string>
<string name="passwords_do_not_match">Паролі не співпадають</string>
<string name="menu_discard_changes">Скасувати зміни</string>
<string name="subtasks">Підзадачі</string>
<string name="EPr_temp_completed_tasks_not_showing">Завдання негайно зникатимуть зі списку після їх завершення</string>
@ -478,14 +477,9 @@
<item quantity="other">%d підзавдань</item>
</plurals>
<string name="gtasks_GPr_header">Google Tasks</string>
<string name="reenter_encryption_password">Підтвердіть пароль шифрування</string>
<string name="encryption_password">Пароль шифрування</string>
<string name="encryption_password_required">Потрібен пароль шифрування</string>
<string name="encryption_password_wrong">Невірний пароль шифрування</string>
<string name="wearable_notifications_summary">Показувати повідомлення на натільних пристроях</string>
<string name="wearable_notifications">Повідомлення на натільні пристрої</string>
<string name="documentation">Документація</string>
<string name="etesync_encryption_description">Запам\'ятайте свій пароль шифрування. Його неможливо відновити!</string>
<string name="etesync_account_description">Потрібний обліковий запис у EteSync.com або власний сервер</string>
<string name="caldav_account_description">Потрібний обліковий запис у провайдера сервісу CalDAV або власний сервер. Відвідайте tasks.org/caldav, щоб переглянути список провайдерів</string>
<string name="etesync_selection_description">Захищена наскрізним шифруванням синхронізація</string>

@ -404,9 +404,6 @@
<string name="url_invalid_scheme">http(s):// کے ساتھ شروع ہونا ضروری ہے</string>
<string name="url_host_name_required">ہوسٹ کا نام مطلوب ہے</string>
<string name="url_required">یو آر ایل مطلوب ہے</string>
<string name="passwords_do_not_match">پاسورڈز ایک جیسے نہیں ہیں</string>
<string name="encryption_password_wrong">غلط انکرپشن پاسورڈ</string>
<string name="encryption_password_required">انکرپشن پاسورڈ مطلوب ہے</string>
<string name="password_required">پاسورڈ مطلوب ہے</string>
<string name="username_required">صارف کا نام مطلوب ہے</string>
<string name="name_cannot_be_empty">نام خالی نہیں ہو سکتا</string>
@ -418,8 +415,6 @@
<string name="notification_disable_battery_optimizations_description">بیٹری کی بچت نوٹیفیکیشنز کو ملتوی کر سکتی ہے</string>
<string name="error_adding_account">ایرر:%s</string>
<string name="url">یو آر ایل</string>
<string name="reenter_encryption_password">انکرپشن پاسورڈ کی تصدیق کریں</string>
<string name="encryption_password">انکرپشن پاسورڈٖ</string>
<string name="password">پاسورڈ</string>
<string name="user">صارف</string>
<string name="add_account">اکاؤنٹ داخل کریں</string>

@ -348,8 +348,6 @@
<string name="notification_disable_battery_optimizations_description">Tối ưu hoá pin có thể sẽ gây trì hoãn các thông báo</string>
<string name="error_adding_account">Lỗi: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">Xác nhận mật khẩu mã hoá</string>
<string name="encryption_password">Mật khẩu mã hoá</string>
<string name="password">Mật khẩu</string>
<string name="user">Người dùng</string>
<string name="add_account">Thêm tài khoản</string>
@ -405,9 +403,6 @@
<string name="url_invalid_scheme">Phải bắt đầu bằng http(s)://</string>
<string name="url_host_name_required">Yêu cầu tên miền máy chủ</string>
<string name="url_required">Yêu cầu URL</string>
<string name="passwords_do_not_match">Mật khẩu không trùng khớp</string>
<string name="encryption_password_wrong">Sai mật khẩu mã hoá</string>
<string name="encryption_password_required">Yêu cầu mật khẩu mã hoá</string>
<string name="password_required">Yêu cầu mật khẩu</string>
<string name="username_required">Yêu cầu tên người dùng</string>
<string name="name_cannot_be_empty">Tên không thể trống</string>
@ -656,7 +651,6 @@
<string name="no_reminders">Không có lời nhắc</string>
<string name="preferences_advanced">Nâng cao</string>
<string name="preferences_look_and_feel">Ngoại hình</string>
<string name="etesync_encryption_description">Hãy nhớ mật khẩu mã hoá mà bạn chọn, nó không thể được phục hồi!</string>
<string name="etesync_account_description">Yêu cầu một tài khoản trên EteSync.com hoặc một máy chủ mà bạn vận hành</string>
<string name="caldav_account_description">Yêu cầu một tài khoản với một nhà cung cấp dịch vụ CalDAV hoặc một máy chủ mà bạn vận hành. Hãy tìm một nhà cung cấp dịch vụ bằng cách đi đến tasks.org/caldav</string>
<string name="next_week">Tuần sau</string>

@ -449,21 +449,15 @@
<string name="enter_title_hint">输入标题</string>
<string name="enter_tag_name">输入标签名称</string>
<string name="create_new_tag">创建\"%s\"</string>
<string name="encryption_password_required">需要加密密码</string>
<string name="encryption_password">加密密码</string>
<string name="display_name">显示名称</string>
<string name="this_feature_requires_a_subscription">该功能需要订阅</string>
<string name="choose_synchronization_service">选择平台</string>
<string name="google_tasks_selection_description">基本服务,用你的谷歌账户进行数据同步</string>
<string name="caldav_selection_description">基于开放的互联网标准的同步</string>
<string name="etesync_selection_description">端到端加密的同步</string>
<string name="passwords_do_not_match">密码不匹配</string>
<string name="reenter_encryption_password">确认加密密码</string>
<string name="show_advanced_settings">显示高级设置</string>
<string name="caldav_account_description">需要一个 CalDAV 服务供应商账户或一个自托管服务器。访问 tasks.org/caldav 寻找服务供应商</string>
<string name="etesync_account_description">需要一个EteSync.com账户或一个自托管服务器</string>
<string name="etesync_encryption_description">记住你挑选的密码,它不能被恢复!</string>
<string name="encryption_password_wrong">错误的加密密码</string>
<string name="help_and_feedback">帮助&amp;反馈</string>
<string name="documentation">文档</string>
<string name="preferences_look_and_feel">外观和感觉</string>

@ -468,8 +468,6 @@
<string name="notification_disable_battery_optimizations_description">電池最佳化可能使通知延遲</string>
<string name="error_adding_account">錯誤: %s</string>
<string name="url">URL</string>
<string name="reenter_encryption_password">確認加密密碼</string>
<string name="encryption_password">加密密碼</string>
<string name="password">密碼</string>
<string name="user">使用者名稱</string>
<string name="add_account">新增帳號</string>
@ -513,9 +511,6 @@
<string name="url_invalid_scheme">必須由 http(s):// 開頭</string>
<string name="url_host_name_required">需要主機名稱</string>
<string name="url_required">需要URL</string>
<string name="passwords_do_not_match">密目不相符</string>
<string name="encryption_password_wrong">加密密碼錯誤</string>
<string name="encryption_password_required">需要加密密碼</string>
<string name="password_required">需要密碼</string>
<string name="username_required">需要使用者名稱</string>
<string name="filter_criteria_unstarted">尚未開始</string>

@ -15,7 +15,7 @@
<string name="decsync">DecSync CC</string>
<string name="tasks_org">Tasks.org</string>
<string name="etesync_url">https://api.etesync.com</string>
<string name="etesync_deprecated">Native EteSync v1 support is deprecated and will be removed soon! Please switch to the EteSync client to continue synchronizing your EteSync v1 account, or migrate to EteSync v2</string>
<string name="etesync_deprecated">Native EteSync v1 support has been removed. Please switch to the EteSync client to continue synchronizing your EteSync v1 account, or migrate to EteSync v2</string>
<string name="etebase_url">https://api.etebase.com/partner/tasksorg/</string>
<string name="help_url_sync">https://tasks.org/sync</string>
@ -384,7 +384,6 @@
<string name="p_crash_main_queries">debug_crash_main_queries</string>
<string name="p_sync_ongoing_google_tasks">sync_ongoing_google_tasks</string>
<string name="p_sync_ongoing_caldav">sync_ongoing_caldav</string>
<string name="p_sync_ongoing_etesync">sync_ongoing_etesync</string>
<string name="p_sync_ongoing_etebase">sync_ongoing_etebase</string>
<string name="p_sync_ongoing_opentasks">sync_ongoing_opentasks</string>
<string name="p_sync_ongoing_android">sync_ongoing_android</string>
@ -443,7 +442,6 @@
<string name="event_astrid2taskprovider">cp_astrid2taskprovider</string>
<string name="event_sync_add_account">sync_add_account</string>
<string name="event_sync_unknown_access">sync_unknown_access</string>
<string name="legacy_etesync">legacy_etesync</string>
<string name="param_type">type</string>
<string name="p_map_theme">map_theme</string>
<string name="p_picker_mode_date">picker_mode_date</string>

@ -379,9 +379,6 @@ File %1$s contained %2$s.\n\n
<string name="name_cannot_be_empty">Name cannot be empty</string>
<string name="username_required">Username required</string>
<string name="password_required">Password required</string>
<string name="encryption_password_required">Encryption password required</string>
<string name="encryption_password_wrong">Wrong encryption password</string>
<string name="passwords_do_not_match">Passwords do not match</string>
<string name="url_required">URL required</string>
<string name="url_host_name_required">Hostname required</string>
<string name="url_invalid_scheme">Must begin with http(s)://</string>
@ -437,8 +434,6 @@ File %1$s contained %2$s.\n\n
<string name="add_account">Add account</string>
<string name="user">User</string>
<string name="password">Password</string>
<string name="encryption_password">Encryption password</string>
<string name="reenter_encryption_password">Confirm encryption password</string>
<string name="url">URL</string>
<string name="error_adding_account">Error: %s</string>
<string name="notification_disable_battery_optimizations_description">Battery optimizations may delay notifications</string>
@ -547,7 +542,6 @@ File %1$s contained %2$s.\n\n
<string name="show_advanced_settings">Show advanced settings</string>
<string name="caldav_account_description">Requires an account with a CalDAV service provider or a self-hosted server. Find a service provider by visiting tasks.org/caldav</string>
<string name="etesync_account_description">Requires an account with EteSync.com or a self-hosted server</string>
<string name="etesync_encryption_description">Remember the encryption password you pick, it cannot be recovered!</string>
<string name="preferences_look_and_feel">Look and feel</string>
<string name="preferences_advanced">Advanced</string>
<string name="documentation">Documentation</string>

@ -374,20 +374,10 @@
+| +--- androidx.work:work-runtime:2.7.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.5.31 (*)
+| \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0 -> 1.5.2 (*)
++--- com.etesync:journalmanager:1.1.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71 -> 1.5.31 (*)
+| +--- com.madgag.spongycastle:core:1.54.0.0
+| +--- com.madgag.spongycastle:prov:1.54.0.0
+| | \--- com.madgag.spongycastle:core:1.54.0.0
+| +--- com.squareup.okhttp3:logging-interceptor:3.12.1
+| | \--- com.squareup.okhttp3:okhttp:3.12.1 -> 4.8.1 (*)
+| +--- com.google.code.gson:gson:1.7.2 -> 2.8.8
+| +--- org.apache.commons:commons-collections4:4.1
+| +--- org.apache.commons:commons-lang3:3.8.1
+| \--- commons-codec:commons-codec:1.7 -> 1.11
++--- com.etebase:client:2.3.2
+| +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
+| \--- com.squareup.okhttp3:logging-interceptor:3.12.1 (*)
+| \--- com.squareup.okhttp3:logging-interceptor:3.12.1
+| \--- com.squareup.okhttp3:okhttp:3.12.1 -> 4.8.1 (*)
++--- com.github.QuadFlask:colorpicker:0.0.15
+| \--- androidx.appcompat:appcompat:1.1.0 -> 1.3.1 (*)
++--- net.openid:appauth:0.8.1

@ -493,20 +493,10 @@
+| +--- androidx.work:work-runtime:2.7.0 (*)
+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.5.31 (*)
+| \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0 -> 1.5.2 (*)
++--- com.etesync:journalmanager:1.1.1
+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71 -> 1.5.31 (*)
+| +--- com.madgag.spongycastle:core:1.54.0.0
+| +--- com.madgag.spongycastle:prov:1.54.0.0
+| | \--- com.madgag.spongycastle:core:1.54.0.0
+| +--- com.squareup.okhttp3:logging-interceptor:3.12.1
+| | \--- com.squareup.okhttp3:okhttp:3.12.1 -> 4.8.1 (*)
+| +--- com.google.code.gson:gson:1.7.2 -> 2.8.8
+| +--- org.apache.commons:commons-collections4:4.1
+| +--- org.apache.commons:commons-lang3:3.8.1
+| \--- commons-codec:commons-codec:1.7 -> 1.11
++--- com.etebase:client:2.3.2
+| +--- androidx.annotation:annotation:1.1.0 -> 1.2.0
+| \--- com.squareup.okhttp3:logging-interceptor:3.12.1 (*)
+| \--- com.squareup.okhttp3:logging-interceptor:3.12.1
+| \--- com.squareup.okhttp3:okhttp:3.12.1 -> 4.8.1 (*)
++--- com.github.QuadFlask:colorpicker:0.0.15
+| \--- androidx.appcompat:appcompat:1.1.0 -> 1.3.1 (*)
++--- net.openid:appauth:0.8.1

Loading…
Cancel
Save