Attachment changes

* Add attachment table to map attachment to file
* Convert TaskAttachment to immutable data class
* Copy attachments when duplicating tasks
pull/1967/head
Alex Baker 2 years ago
parent 226687fee8
commit 2ac6c2413b

File diff suppressed because it is too large Load Diff

@ -76,6 +76,7 @@ open class BaseTaskEditViewModelTest : InjectingTestCase() {
MutableSharedFlow(), MutableSharedFlow(),
MutableSharedFlow(), MutableSharedFlow(),
userActivityDao = userActivityDao, userActivityDao = userActivityDao,
taskAttachmentDao = db.taskAttachmentDao,
) )
} }

@ -30,12 +30,13 @@ import org.tasks.notifications.NotificationDao
CaldavAccount::class, CaldavAccount::class,
GoogleTaskAccount::class, GoogleTaskAccount::class,
Principal::class, Principal::class,
PrincipalAccess::class PrincipalAccess::class,
Attachment::class,
], ],
autoMigrations = [ autoMigrations = [
AutoMigration(from = 83, to = 84, spec = Migrations.AutoMigrate83to84::class), AutoMigration(from = 83, to = 84, spec = Migrations.AutoMigrate83to84::class),
], ],
version = 85 version = 86
) )
abstract class Database : RoomDatabase() { abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao abstract fun notificationDao(): NotificationDao

@ -15,16 +15,15 @@ import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.tasks.R import org.tasks.R
import org.tasks.Strings
import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.AttachmentRow import org.tasks.compose.edit.AttachmentRow
import org.tasks.data.TaskAttachment import org.tasks.data.TaskAttachment
import org.tasks.data.TaskAttachmentDao import org.tasks.data.TaskAttachmentDao
import org.tasks.dialogs.AddAttachmentDialog import org.tasks.dialogs.AddAttachmentDialog
import org.tasks.dialogs.DialogBuilder
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.ui.TaskEditControlFragment import org.tasks.ui.TaskEditControlFragment
@ -33,7 +32,6 @@ import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class FilesControlSet : TaskEditControlFragment() { class FilesControlSet : TaskEditControlFragment() {
@Inject lateinit var taskAttachmentDao: TaskAttachmentDao @Inject lateinit var taskAttachmentDao: TaskAttachmentDao
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var preferences: Preferences @Inject lateinit var preferences: Preferences
override fun createView(savedInstanceState: Bundle?) { override fun createView(savedInstanceState: Bundle?) {
@ -52,10 +50,12 @@ class FilesControlSet : TaskEditControlFragment() {
setContent { setContent {
MdcTheme { MdcTheme {
AttachmentRow( AttachmentRow(
attachments = taskAttachmentDao.watchAttachments(viewModel.task.uuid) attachments = viewModel.selectedAttachments.collectAsStateLifecycleAware().value,
.collectAsStateLifecycleAware(initial = emptyList()).value,
openAttachment = { openAttachment = {
FileHelper.startActionView(requireActivity(), it.parseUri()) FileHelper.startActionView(
requireActivity(),
if (Strings.isNullOrEmpty(it.uri)) null else Uri.parse(it.uri)
)
}, },
deleteAttachment = this@FilesControlSet::deleteAttachment, deleteAttachment = this@FilesControlSet::deleteAttachment,
addAttachment = { addAttachment = {
@ -94,18 +94,9 @@ class FilesControlSet : TaskEditControlFragment() {
} }
private fun deleteAttachment(attachment: TaskAttachment) { private fun deleteAttachment(attachment: TaskAttachment) {
dialogBuilder viewModel.selectedAttachments.update {
.newDialog(R.string.premium_remove_file_confirm) it.minus(attachment)
.setPositiveButton(R.string.ok) { _, _ -> }
lifecycleScope.launch {
withContext(NonCancellable) {
taskAttachmentDao.delete(attachment)
FileHelper.delete(context, attachment.parseUri())
}
}
}
.setNegativeButton(R.string.cancel, null)
.show()
} }
private fun copyToAttachmentDirectory(input: Uri?) { private fun copyToAttachmentDirectory(input: Uri?) {
@ -114,11 +105,16 @@ class FilesControlSet : TaskEditControlFragment() {
private fun newAttachment(output: Uri) { private fun newAttachment(output: Uri) {
val attachment = TaskAttachment( val attachment = TaskAttachment(
viewModel.task.uuid, uri = output.toString(),
output, name = FileHelper.getFilename(requireContext(), output)!!,
FileHelper.getFilename(requireContext(), output)!!) )
lifecycleScope.launch { lifecycleScope.launch {
taskAttachmentDao.createNew(attachment) taskAttachmentDao.insert(attachment)
viewModel.selectedAttachments.update {
it.plus(
taskAttachmentDao.getAttachment(attachment.remoteId) ?: return@launch
)
}
} }
} }

@ -20,7 +20,9 @@ class TaskDuplicator @Inject constructor(
private val caldavDao: CaldavDao, private val caldavDao: CaldavDao,
private val locationDao: LocationDao, private val locationDao: LocationDao,
private val alarmDao: AlarmDao, private val alarmDao: AlarmDao,
private val preferences: Preferences) { private val preferences: Preferences,
private val taskAttachmentDao: TaskAttachmentDao,
) {
suspend fun duplicate(taskIds: List<Long>): List<Task> { suspend fun duplicate(taskIds: List<Long>): List<Task> {
val result: MutableList<Task> = ArrayList() val result: MutableList<Task> = ArrayList()
@ -79,6 +81,16 @@ class TaskDuplicator @Inject constructor(
} }
gcalHelper.createTaskEventIfEnabled(clone) gcalHelper.createTaskEventIfEnabled(clone)
taskDao.save(clone, null) // TODO: delete me taskDao.save(clone, null) // TODO: delete me
taskAttachmentDao
.getAttachmentsForTask(originalId)
.map {
Attachment(
task = clone.id,
fileId = it.fileId,
attachmentUid = it.attachmentUid
)
}
.let { taskAttachmentDao.insert(it) }
getDirectChildren(originalId).forEach { subtask -> getDirectChildren(originalId).forEach { subtask ->
clone(subtask, newId) clone(subtask, newId)
} }

@ -1,6 +1,7 @@
package com.todoroo.astrid.service package com.todoroo.astrid.service
import android.content.Context import android.content.Context
import android.net.Uri
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import com.google.common.collect.ImmutableListMultimap import com.google.common.collect.ImmutableListMultimap
import com.google.common.collect.ListMultimap import com.google.common.collect.ListMultimap
@ -266,8 +267,11 @@ class Upgrader @Inject constructor(
userActivityDao.update(userActivity) userActivityDao.update(userActivity)
} }
for (attachment in taskAttachmentDao.getAttachments()) { for (attachment in taskAttachmentDao.getAttachments()) {
attachment.convertPathUri() taskAttachmentDao.update(
taskAttachmentDao.update(attachment) attachment.copy(
uri = Uri.fromFile(File(attachment.uri)).toString()
)
)
} }
} }

@ -2,21 +2,7 @@ package org.tasks.backup
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import org.tasks.backup.TasksJsonImporter.LegacyLocation import org.tasks.backup.TasksJsonImporter.LegacyLocation
import org.tasks.data.Alarm import org.tasks.data.*
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavTask
import org.tasks.data.Filter
import org.tasks.data.Geofence
import org.tasks.data.GoogleTask
import org.tasks.data.GoogleTaskAccount
import org.tasks.data.GoogleTaskList
import org.tasks.data.Place
import org.tasks.data.Tag
import org.tasks.data.TagData
import org.tasks.data.TaskAttachment
import org.tasks.data.TaskListMetadata
import org.tasks.data.UserActivity
class BackupContainer( class BackupContainer(
val tasks: List<TaskBackup>?, val tasks: List<TaskBackup>?,
@ -28,6 +14,7 @@ class BackupContainer(
val caldavAccounts: List<CaldavAccount>?, val caldavAccounts: List<CaldavAccount>?,
val caldavCalendars: List<CaldavCalendar>?, val caldavCalendars: List<CaldavCalendar>?,
val taskListMetadata: List<TaskListMetadata>?, val taskListMetadata: List<TaskListMetadata>?,
val taskAttachments: List<TaskAttachment>,
val intPrefs: Map<String, Integer>?, val intPrefs: Map<String, Integer>?,
val longPrefs: Map<String, java.lang.Long>?, val longPrefs: Map<String, java.lang.Long>?,
val stringPrefs: Map<String, String>?, val stringPrefs: Map<String, String>?,
@ -41,7 +28,7 @@ class BackupContainer(
val tags: List<Tag>, val tags: List<Tag>,
val google: List<GoogleTask>, val google: List<GoogleTask>,
val comments: List<UserActivity>, val comments: List<UserActivity>,
val attachments: List<TaskAttachment>?, val attachments: List<Attachment>?,
val caldavTasks: List<CaldavTask>?, val caldavTasks: List<CaldavTask>?,
val vtodo: String?, val vtodo: String?,
) { ) {

@ -15,18 +15,7 @@ import org.tasks.BuildConfig
import org.tasks.R import org.tasks.R
import org.tasks.backup.BackupContainer.TaskBackup import org.tasks.backup.BackupContainer.TaskBackup
import org.tasks.caldav.VtodoCache import org.tasks.caldav.VtodoCache
import org.tasks.data.AlarmDao import org.tasks.data.*
import org.tasks.data.CaldavDao
import org.tasks.data.FilterDao
import org.tasks.data.GoogleTaskDao
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.LocationDao
import org.tasks.data.TagDao
import org.tasks.data.TagDataDao
import org.tasks.data.TaskAttachmentDao
import org.tasks.data.TaskDao
import org.tasks.data.TaskListMetadataDao
import org.tasks.data.UserActivityDao
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.extensions.Context.toast import org.tasks.extensions.Context.toast
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
@ -130,7 +119,7 @@ class TasksJsonExporter @Inject constructor(
tagDao.getTagsForTask(taskId), tagDao.getTagsForTask(taskId),
googleTaskDao.getAllByTaskId(taskId), googleTaskDao.getAllByTaskId(taskId),
userActivityDao.getComments(taskId), userActivityDao.getComments(taskId),
taskAttachmentDao.getAttachments(task.uuid), taskAttachmentDao.getAttachmentsForTask(taskId),
caldavTasks, caldavTasks,
vtodoCache.getVtodo( caldavTasks.firstOrNull { !it.isDeleted() }) vtodoCache.getVtodo( caldavTasks.firstOrNull { !it.isDeleted() })
)) ))
@ -148,6 +137,7 @@ class TasksJsonExporter @Inject constructor(
caldavDao.getAccounts(), caldavDao.getAccounts(),
caldavDao.getCalendars(), caldavDao.getCalendars(),
taskListMetadataDao.getAll(), taskListMetadataDao.getAll(),
taskAttachmentDao.getAttachments(),
preferences.getPrefs(Integer::class.java), preferences.getPrefs(Integer::class.java),
preferences.getPrefs(java.lang.Long::class.java), preferences.getPrefs(java.lang.Long::class.java),
preferences.getPrefs(String::class.java), preferences.getPrefs(String::class.java),

@ -119,6 +119,11 @@ class TasksJsonImporter @Inject constructor(
taskListMetadataDao.insert(tlm) taskListMetadataDao.insert(tlm)
} }
} }
backupContainer.taskAttachments.forEach { attachment ->
if (taskAttachmentDao.getAttachment(attachment.remoteId) == null) {
taskAttachmentDao.insert(attachment)
}
}
backupContainer.tasks?.forEach { backup -> backupContainer.tasks?.forEach { backup ->
result.taskCount++ result.taskCount++
setProgressMessage( setProgressMessage(
@ -198,13 +203,16 @@ class TasksJsonImporter @Inject constructor(
geofence.task = taskId geofence.task = taskId
locationDao.insert(geofence) locationDao.insert(geofence)
} }
backup.attachments?.forEach { attachment -> backup.attachments
attachment.taskId = taskUuid ?.mapNotNull { taskAttachmentDao.getAttachment(it.attachmentUid) }
if (version < V6_4) { ?.map {
attachment.convertPathUri() Attachment(
task = taskId,
fileId = it.id!!,
attachmentUid = it.remoteId,
)
} }
taskAttachmentDao.insert(attachment) ?.let { taskAttachmentDao.insert(it) }
}
backup.caldavTasks?.forEach { caldavTask -> backup.caldavTasks?.forEach { caldavTask ->
caldavTask.task = taskId caldavTask.task = taskId
caldavDao.insert(caldavTask) caldavDao.insert(caldavTask)

@ -11,7 +11,7 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton import androidx.compose.material.IconButton
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Clear
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -19,7 +19,6 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import org.tasks.R import org.tasks.R
import org.tasks.compose.DisabledText import org.tasks.compose.DisabledText
@ -46,12 +45,12 @@ fun AttachmentRow(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text( Text(
text = it.name!!, text = it.name,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) )
IconButton(onClick = { deleteAttachment(it) }) { IconButton(onClick = { deleteAttachment(it) }) {
Icon( Icon(
imageVector = Icons.Outlined.Delete, imageVector = Icons.Outlined.Clear,
contentDescription = stringResource( contentDescription = stringResource(
id = R.string.delete id = R.string.delete
), ),
@ -96,7 +95,10 @@ fun AttachmentPreview() {
MdcTheme { MdcTheme {
AttachmentRow( AttachmentRow(
attachments = listOf( attachments = listOf(
TaskAttachment("", "file://attachment.txt".toUri(), "attachment.txt") TaskAttachment(
uri = "file://attachment.txt",
name = "attachment.txt",
)
), ),
openAttachment = {}, openAttachment = {},
deleteAttachment = {}, deleteAttachment = {},

@ -0,0 +1,37 @@
package org.tasks.data
import androidx.room.*
import com.todoroo.astrid.data.Task
@Entity(
tableName = "attachment",
foreignKeys = [
ForeignKey(
entity = Task::class,
parentColumns = ["_id"],
childColumns = ["task"],
onDelete = ForeignKey.CASCADE,
),
ForeignKey(
entity = TaskAttachment::class,
parentColumns = ["file_id"],
childColumns = ["file"],
onDelete = ForeignKey.CASCADE,
)
],
indices = [Index(value = ["task", "file"], unique = true)],
)
data class Attachment(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "attachment_id")
@Transient
val id: Long? = null,
@ColumnInfo(name = "task", index = true)
@Transient
val task: Long,
@ColumnInfo(name = "file", index = true)
@Transient
val fileId: Long,
@ColumnInfo(name = "file_uuid")
val attachmentUid: String,
)

@ -1,61 +1,50 @@
package org.tasks.data package org.tasks.data
import android.net.Uri import android.os.Parcel
import android.os.Parcelable
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.todoroo.astrid.data.Task import com.todoroo.astrid.helper.UUIDHelper
import org.tasks.Strings
import java.io.File
@Entity(tableName = "task_attachments") @Entity(tableName = "attachment_file")
class TaskAttachment { data class TaskAttachment(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id") @ColumnInfo(name = "file_id")
@Transient @Transient
var id: Long? = null val id: Long? = null,
@ColumnInfo(name = "file_uuid")
@ColumnInfo(name = "remoteId") val remoteId: String = UUIDHelper.newUUID(),
var remoteId: String? = Task.NO_UUID @ColumnInfo(name = "filename")
val name: String,
// -- Constants @ColumnInfo(name = "uri")
@ColumnInfo(name = "task_id") val uri: String,
var taskId: String? = Task.NO_UUID ) : Parcelable {
@ColumnInfo(name = "name")
var name: String? = ""
@ColumnInfo(name = "path")
var uri: String? = ""
private set
@ColumnInfo(name = "content_type")
var contentType: String? = ""
constructor()
@Ignore @Ignore
constructor(taskUuid: String, uri: Uri?, fileName: String) { constructor(parcel: Parcel) : this(
taskId = taskUuid remoteId = parcel.readString()!!,
name = fileName name = parcel.readString()!!,
setUri(uri?.toString()) uri = parcel.readString()!!,
)
override fun describeContents() = 0
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(remoteId)
parcel.writeString(name)
parcel.writeString(uri)
} }
fun setUri(uri: String?) {
this.uri = uri
}
fun convertPathUri() {
setUri(Uri.fromFile(File(uri!!)).toString())
}
fun parseUri(): Uri? = if (Strings.isNullOrEmpty(uri)) null else Uri.parse(uri)
companion object { companion object {
const val KEY = "attachment" const val KEY = "attachment"
const val FILES_DIRECTORY_DEFAULT = "attachments"
@JvmField
val CREATOR = object : Parcelable.Creator<TaskAttachment> {
override fun createFromParcel(parcel: Parcel) = TaskAttachment(parcel)
/** default directory for files on external storage */ override fun newArray(size: Int): Array<TaskAttachment?> = arrayOfNulls(size)
const val FILES_DIRECTORY_DEFAULT = "attachments" // $NON-NLS-1$ }
} }
} }

@ -1,37 +1,36 @@
package org.tasks.data package org.tasks.data
import androidx.room.* import androidx.room.*
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.helper.UUIDHelper
import kotlinx.coroutines.flow.Flow
@Dao @Dao
abstract class TaskAttachmentDao { interface TaskAttachmentDao {
@Query("SELECT * FROM task_attachments WHERE task_id = :taskUuid") @Query("SELECT * FROM attachment WHERE task = :task")
abstract suspend fun getAttachments(taskUuid: String): List<TaskAttachment> suspend fun getAttachmentsForTask(task: Long): List<Attachment>
@Query("SELECT task_attachments.* FROM task_attachments INNER JOIN tasks ON tasks._id = :task WHERE task_id = tasks.remoteId") @Query("SELECT attachment_file.* FROM attachment_file INNER JOIN attachment ON attachment_file.file_uuid = attachment.file_uuid WHERE task = :task")
abstract suspend fun getAttachments(task: Long): List<TaskAttachment> suspend fun getAttachments(task: Long): List<TaskAttachment>
@Query("SELECT * FROM task_attachments") @Query("SELECT * FROM attachment_file")
abstract suspend fun getAttachments(): List<TaskAttachment> suspend fun getAttachments(): List<TaskAttachment>
@Query("SELECT * FROM task_attachments WHERE task_id = :taskUuid") @Query("SELECT * FROM attachment_file WHERE file_uuid = :remoteId")
abstract fun watchAttachments(taskUuid: String): Flow<List<TaskAttachment>> suspend fun getAttachment(remoteId: String): TaskAttachment?
@Delete @Query("DELETE FROM attachment WHERE task = :taskId AND file_uuid = :attachment")
abstract suspend fun delete(taskAttachment: TaskAttachment) suspend fun delete(taskId: Long, attachment: String)
@Query("DELETE FROM attachment WHERE task = :taskId AND file_uuid IN (:attachments)")
suspend fun delete(taskId: Long, attachments: List<String>)
@Insert
suspend fun insert(attachments: List<Attachment>)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
abstract suspend fun insert(attachment: TaskAttachment) suspend fun insert(attachment: TaskAttachment)
@Update @Update
abstract suspend fun update(attachment: TaskAttachment) suspend fun update(attachment: TaskAttachment)
suspend fun createNew(attachment: TaskAttachment) { @Delete
if (Task.isUuidEmpty(attachment.remoteId)) { fun delete(value: List<TaskAttachment>)
attachment.remoteId = UUIDHelper.newUUID()
}
insert(attachment)
}
} }

@ -562,6 +562,24 @@ object Migrations {
} }
} }
private val MIGRATION_85_86 = object : Migration(85, 86) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `attachment_file` (`file_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_uuid` TEXT NOT NULL, `filename` TEXT NOT NULL, `uri` TEXT NOT NULL)")
database.execSQL("INSERT INTO `attachment_file` (`file_id`, `uri`,`filename`,`file_id`,`file_uuid`) SELECT `_id`, `path`,`name`,`_id`,`remoteId` FROM `task_attachments`")
database.execSQL("CREATE TABLE IF NOT EXISTS `attachment` (`attachment_id` INTEGER PRIMARY KEY AUTOINCREMENT, `task` INTEGER NOT NULL, `file` INTEGER NOT NULL, `file_uuid` TEXT NOT NULL, FOREIGN KEY(`task`) REFERENCES `tasks`(`_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`file`) REFERENCES `attachment_file`(`file_id`) ON UPDATE NO ACTION ON DELETE CASCADE)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_task` ON `attachment` (`task`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_attachment_file` ON `attachment` (`file`)")
database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_attachment_task_file` ON `attachment` (`task`, `file`)")
database.execSQL(
"INSERT INTO `attachment` (`task`, `file`, `file_uuid`) SELECT `tasks`.`_id`, `task_attachments`.`_id`, `task_attachments`.`remoteId` FROM `task_attachments` JOIN `tasks` ON `tasks`.`remoteId` = `task_attachments`.`task_id`"
)
database.execSQL("DROP TABLE `task_attachments`")
}
}
fun migrations(fileStorage: FileStorage) = arrayOf( fun migrations(fileStorage: FileStorage) = arrayOf(
MIGRATION_35_36, MIGRATION_35_36,
MIGRATION_36_37, MIGRATION_36_37,
@ -603,6 +621,7 @@ object Migrations {
migration_81_82(fileStorage), migration_81_82(fileStorage),
MIGRATION_82_83, MIGRATION_82_83,
MIGRATION_84_85, MIGRATION_84_85,
MIGRATION_85_86,
) )
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) { private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {

@ -11,7 +11,6 @@ import kotlinx.coroutines.runBlocking
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.data.DeletionDao import org.tasks.data.DeletionDao
import org.tasks.data.LocationDao import org.tasks.data.LocationDao
import org.tasks.data.TaskAttachmentDao
import org.tasks.data.UserActivityDao import org.tasks.data.UserActivityDao
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.injection.BaseWorker import org.tasks.injection.BaseWorker
@ -28,7 +27,6 @@ class CleanupWork @AssistedInject constructor(
private val geofenceApi: GeofenceApi, private val geofenceApi: GeofenceApi,
private val timerPlugin: TimerPlugin, private val timerPlugin: TimerPlugin,
private val alarmService: AlarmService, private val alarmService: AlarmService,
private val taskAttachmentDao: TaskAttachmentDao,
private val userActivityDao: UserActivityDao, private val userActivityDao: UserActivityDao,
private val locationDao: LocationDao, private val locationDao: LocationDao,
private val deletionDao: DeletionDao) : BaseWorker(context, workerParams, firebase) { private val deletionDao: DeletionDao) : BaseWorker(context, workerParams, firebase) {
@ -48,10 +46,6 @@ class CleanupWork @AssistedInject constructor(
locationDao.delete(it) locationDao.delete(it)
geofenceApi.update(it.place!!) geofenceApi.update(it.place!!)
} }
taskAttachmentDao.getAttachments(task).forEach {
FileHelper.delete(context, it.parseUri())
taskAttachmentDao.delete(it)
}
userActivityDao.getComments(task).forEach { userActivityDao.getComments(task).forEach {
FileHelper.delete(context, it.pictureUri) FileHelper.delete(context, it.pictureUri)
userActivityDao.delete(it) userActivityDao.delete(it)

@ -3,6 +3,7 @@ package org.tasks.ui
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.core.net.toUri
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -75,6 +76,7 @@ class TaskEditViewModel @Inject constructor(
private val firebase: Firebase? = null, private val firebase: Firebase? = null,
private val userActivityDao: UserActivityDao, private val userActivityDao: UserActivityDao,
private val alarmDao: AlarmDao, private val alarmDao: AlarmDao,
private val taskAttachmentDao: TaskAttachmentDao,
) : ViewModel() { ) : ViewModel() {
private val resources = context.resources private val resources = context.resources
private var cleared = false private var cleared = false
@ -136,6 +138,9 @@ class TaskEditViewModel @Inject constructor(
savedStateHandle.get<ArrayList<TagData>>(TaskEditFragment.EXTRA_TAGS) ?: emptyList() savedStateHandle.get<ArrayList<TagData>>(TaskEditFragment.EXTRA_TAGS) ?: emptyList()
val selectedTags = MutableStateFlow(ArrayList(originalTags)) val selectedTags = MutableStateFlow(ArrayList(originalTags))
private lateinit var originalAttachments: List<TaskAttachment>
val selectedAttachments = MutableStateFlow(emptyList<TaskAttachment>())
private val originalAlarms: List<Alarm> = if (isNew) { private val originalAlarms: List<Alarm> = if (isNew) {
ArrayList<Alarm>().apply { ArrayList<Alarm>().apply {
if (task.isNotifyAtStart) { if (task.isNotifyAtStart) {
@ -201,6 +206,7 @@ class TaskEditViewModel @Inject constructor(
originalList != selectedList.value || originalList != selectedList.value ||
originalLocation != selectedLocation.value || originalLocation != selectedLocation.value ||
originalTags.toHashSet() != selectedTags.value.toHashSet() || originalTags.toHashSet() != selectedTags.value.toHashSet() ||
originalAttachments.toHashSet() != selectedAttachments.value.toHashSet() ||
newSubtasks.value.isNotEmpty() || newSubtasks.value.isNotEmpty() ||
getRingFlags() != when { getRingFlags() != when {
task.isNotifyModeFive -> NOTIFY_MODE_FIVE task.isNotifyModeFive -> NOTIFY_MODE_FIVE
@ -318,6 +324,23 @@ class TaskEditViewModel @Inject constructor(
task.modificationDate = now() task.modificationDate = now()
} }
if (selectedAttachments.value.toHashSet() != originalAttachments.toHashSet()) {
originalAttachments
.minus(selectedAttachments.value.toSet())
.map { it.remoteId }
.let { taskAttachmentDao.delete(task.id, it) }
selectedAttachments.value
.minus(originalAttachments.toSet())
.map {
Attachment(
task = task.id,
fileId = it.id!!,
attachmentUid = it.remoteId,
)
}
.let { taskAttachmentDao.insert(it) }
}
if (task.isCompleted != completed) { if (task.isCompleted != completed) {
taskCompleter.setComplete(task, completed) taskCompleter.setComplete(task, completed)
} }
@ -366,6 +389,9 @@ class TaskEditViewModel @Inject constructor(
suspend fun discard(remove: Boolean = true) { suspend fun discard(remove: Boolean = true) {
if (isNew) { if (isNew) {
timerPlugin.stopTimer(task) timerPlugin.stopTimer(task)
originalAttachments.plus(selectedAttachments.value).toSet().takeIf { it.isNotEmpty() }
?.onEach { FileHelper.delete(context, it.uri.toUri()) }
?.let { taskAttachmentDao.delete(it.toList()) }
} }
clear(remove) clear(remove)
} }
@ -417,6 +443,13 @@ class TaskEditViewModel @Inject constructor(
} }
} }
init {
viewModelScope.launch {
originalAttachments = taskAttachmentDao.getAttachments(task.id)
selectedAttachments.update { originalAttachments }
}
}
companion object { companion object {
fun String?.stripCarriageReturns(): String? = this?.replace("\\r\\n?".toRegex(), "\n") fun String?.stripCarriageReturns(): String? = this?.replace("\\r\\n?".toRegex(), "\n")
} }

@ -69,7 +69,6 @@
<string name="gtasks_GLA_errorIOAuth">عذرا، لم نتمكن من الاتصال مع مزود الخدمة جوجل. أعد المحاولة مرة أخرى لاحقًًا.</string> <string name="gtasks_GLA_errorIOAuth">عذرا، لم نتمكن من الاتصال مع مزود الخدمة جوجل. أعد المحاولة مرة أخرى لاحقًًا.</string>
<string name="gtasks_GPr_header">مهام قوقل</string> <string name="gtasks_GPr_header">مهام قوقل</string>
<string name="premium_record_audio">تسجيل ملاحظة</string> <string name="premium_record_audio">تسجيل ملاحظة</string>
<string name="premium_remove_file_confirm">هل أنت متأكد؟ لا يمكن التراجع</string>
<string name="rmd_NoA_done">أكمل</string> <string name="rmd_NoA_done">أكمل</string>
<string name="rmd_NoA_snooze">غفوة</string> <string name="rmd_NoA_snooze">غفوة</string>
<string name="rmd_EPr_quiet_hours_start_title">ابتداء ساعات الهدوء</string> <string name="rmd_EPr_quiet_hours_start_title">ابتداء ساعات الهدوء</string>

@ -130,7 +130,6 @@
<string name="gtasks_GLA_errorIOAuth">Има проблем с връзката до сървърите на Google. Опитайте отново по-късно.</string> <string name="gtasks_GLA_errorIOAuth">Има проблем с връзката до сървърите на Google. Опитайте отново по-късно.</string>
<string name="gtasks_error_accountNotFound">Профилът %s не е намерен. Излезте и влезте отново от настройките на Google Tasks.</string> <string name="gtasks_error_accountNotFound">Профилът %s не е намерен. Излезте и влезте отново от настройките на Google Tasks.</string>
<string name="premium_record_audio">Записване на бележка</string> <string name="premium_record_audio">Записване на бележка</string>
<string name="premium_remove_file_confirm">Сигурни ли сте? Не може да бъде отменено</string>
<string name="ring_once">Звънене веднъж</string> <string name="ring_once">Звънене веднъж</string>
<string name="ring_five_times">Звънене пет пъти</string> <string name="ring_five_times">Звънене пет пъти</string>
<string name="ring_nonstop">Звънене без прекъсване</string> <string name="ring_nonstop">Звънене без прекъсване</string>

@ -172,7 +172,6 @@
<string name="repeat_option_every_day">Cada dia</string> <string name="repeat_option_every_day">Cada dia</string>
<string name="filter_any_start_date">Qualsevol data d\'inici</string> <string name="filter_any_start_date">Qualsevol data d\'inici</string>
<string name="CFC_tag_name">Etiqueta…</string> <string name="CFC_tag_name">Etiqueta…</string>
<string name="premium_remove_file_confirm">Estàs segur\? No pot ser desfet</string>
<string name="default_random_reminder_hourly">Cada hora</string> <string name="default_random_reminder_hourly">Cada hora</string>
<string name="repeat_option_every_month">Mensualment</string> <string name="repeat_option_every_month">Mensualment</string>
<string name="default_random_reminder_disabled">Deshabilitada</string> <string name="default_random_reminder_disabled">Deshabilitada</string>

@ -105,7 +105,6 @@
<string name="gtasks_GPr_header">Google Úkoly</string> <string name="gtasks_GPr_header">Google Úkoly</string>
<string name="gtasks_error_accountNotFound">Účet %s nebyl nalezen — přes nastavení Google Tasks se odhlaste a znovu přihlaste.</string> <string name="gtasks_error_accountNotFound">Účet %s nebyl nalezen — přes nastavení Google Tasks se odhlaste a znovu přihlaste.</string>
<string name="premium_record_audio">Nahrát poznámku</string> <string name="premium_record_audio">Nahrát poznámku</string>
<string name="premium_remove_file_confirm">Opravdu\? Nelze vrátit zpět</string>
<string name="rmd_NoA_done">Dokončeno</string> <string name="rmd_NoA_done">Dokončeno</string>
<string name="rmd_NoA_snooze">Odložit</string> <string name="rmd_NoA_snooze">Odložit</string>
<string name="rmd_EPr_quiet_hours_start_title">Nerušit od</string> <string name="rmd_EPr_quiet_hours_start_title">Nerušit od</string>

@ -482,7 +482,6 @@
<string name="ring_five_times">Ring fem gange</string> <string name="ring_five_times">Ring fem gange</string>
<string name="ring_once">Ring én gang</string> <string name="ring_once">Ring én gang</string>
<string name="premium_record_audio">Optag en note</string> <string name="premium_record_audio">Optag en note</string>
<string name="premium_remove_file_confirm">Er du sikker\? Det kan ikke fortrydes</string>
<string name="gtasks_GLA_errorIOAuth">Beklager, noget gik galt i kommunikationen med Googles servere. Prøv igen senere.</string> <string name="gtasks_GLA_errorIOAuth">Beklager, noget gik galt i kommunikationen med Googles servere. Prøv igen senere.</string>
<string name="gtasks_GLA_authenticating">Godkender…</string> <string name="gtasks_GLA_authenticating">Godkender…</string>
<string name="gtasks_GTA_clear_completed">Ryd udførte</string> <string name="gtasks_GTA_clear_completed">Ryd udførte</string>

@ -124,7 +124,6 @@
<string name="gtasks_GLA_errorIOAuth">Leider ist während der Kommunikation mit den Google-Servern ein Problem aufgetreten. Bitte versuchen Sie es später noch einmal.</string> <string name="gtasks_GLA_errorIOAuth">Leider ist während der Kommunikation mit den Google-Servern ein Problem aufgetreten. Bitte versuchen Sie es später noch einmal.</string>
<string name="gtasks_error_accountNotFound">Konto %s nicht gefunden. Bitte abmelden und erneut über die Einstellungen von Google Tasks anmelden.</string> <string name="gtasks_error_accountNotFound">Konto %s nicht gefunden. Bitte abmelden und erneut über die Einstellungen von Google Tasks anmelden.</string>
<string name="premium_record_audio">Notiz aufzeichnen</string> <string name="premium_record_audio">Notiz aufzeichnen</string>
<string name="premium_remove_file_confirm">Sind Sie sicher? Das kann nicht rückgängig gemacht werden</string>
<string name="ring_once">Einmal klingeln</string> <string name="ring_once">Einmal klingeln</string>
<string name="ring_five_times">Fünfmal klingeln</string> <string name="ring_five_times">Fünfmal klingeln</string>
<string name="ring_nonstop">Ununterbrochen klingeln</string> <string name="ring_nonstop">Ununterbrochen klingeln</string>

@ -99,7 +99,6 @@
<string name="gtasks_GPr_header">Εργασίες Google</string> <string name="gtasks_GPr_header">Εργασίες Google</string>
<string name="gtasks_error_accountNotFound">Λογαριασμός %s δεν βρέθηκε - παρακαλώ αποσυνδεθείτε και συνδεθείτε πάλι απο τις ρυθμίσεις του Google Tasks.</string> <string name="gtasks_error_accountNotFound">Λογαριασμός %s δεν βρέθηκε - παρακαλώ αποσυνδεθείτε και συνδεθείτε πάλι απο τις ρυθμίσεις του Google Tasks.</string>
<string name="premium_record_audio">Ηχογράφηση σημείωσης</string> <string name="premium_record_audio">Ηχογράφηση σημείωσης</string>
<string name="premium_remove_file_confirm">Είστε σίγουρος; Δεν μπορεί να ακυρωθεί</string>
<string name="rmd_NoA_done">Ολοκληρωμένο</string> <string name="rmd_NoA_done">Ολοκληρωμένο</string>
<string name="rmd_NoA_snooze">Αναβολή</string> <string name="rmd_NoA_snooze">Αναβολή</string>
<string name="rmd_EPr_quiet_hours_start_title">Ξεκίνησε η ώρα ησυχίας </string> <string name="rmd_EPr_quiet_hours_start_title">Ξεκίνησε η ώρα ησυχίας </string>

@ -146,7 +146,6 @@
<string name="default_random_reminder_disabled">Malŝaltitaj</string> <string name="default_random_reminder_disabled">Malŝaltitaj</string>
<string name="rmd_EPr_defaultRemind_title">Hazardaj memorigiloj</string> <string name="rmd_EPr_defaultRemind_title">Hazardaj memorigiloj</string>
<string name="rmd_EPr_rmd_time_title">Defaŭlta memorigilo</string> <string name="rmd_EPr_rmd_time_title">Defaŭlta memorigilo</string>
<string name="premium_remove_file_confirm">Ĉu vi certas\? Ne povos malfari</string>
<string name="CFC_list_name">En listo…</string> <string name="CFC_list_name">En listo…</string>
<string name="CFC_gtasks_list_name">En GTasks Listo…</string> <string name="CFC_gtasks_list_name">En GTasks Listo…</string>
<string name="CFC_gtasks_list_text">En Listo: \?</string> <string name="CFC_gtasks_list_text">En Listo: \?</string>

@ -124,7 +124,6 @@
<string name="gtasks_GLA_errorIOAuth">Perdón, hubo un problema al comunicarse con los servidores de Google. Por favor inténtalo mas tarde.</string> <string name="gtasks_GLA_errorIOAuth">Perdón, hubo un problema al comunicarse con los servidores de Google. Por favor inténtalo mas tarde.</string>
<string name="gtasks_error_accountNotFound">No se ha encontrado la cuenta %s — por favor, cierra sesión y vuelve a iniciarla desde la configuración de Google Tasks.</string> <string name="gtasks_error_accountNotFound">No se ha encontrado la cuenta %s — por favor, cierra sesión y vuelve a iniciarla desde la configuración de Google Tasks.</string>
<string name="premium_record_audio">Grabar una nota</string> <string name="premium_record_audio">Grabar una nota</string>
<string name="premium_remove_file_confirm">Está seguro? No se puede deshacer</string>
<string name="ring_once">Sonar una vez</string> <string name="ring_once">Sonar una vez</string>
<string name="ring_five_times">Sonar cinco veces</string> <string name="ring_five_times">Sonar cinco veces</string>
<string name="ring_nonstop">Sonar sin parar</string> <string name="ring_nonstop">Sonar sin parar</string>

@ -98,7 +98,6 @@
<string name="gtasks_GTA_clear_completed">Eemalda lõpetatud</string> <string name="gtasks_GTA_clear_completed">Eemalda lõpetatud</string>
<string name="gtasks_GLA_authenticating">Autentimine…</string> <string name="gtasks_GLA_authenticating">Autentimine…</string>
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="premium_remove_file_confirm">Oled sa kindel\? Seda ei saa tühistada</string>
<string name="ring_once">Helista üks kord</string> <string name="ring_once">Helista üks kord</string>
<string name="ring_five_times">Helista 5 korda</string> <string name="ring_five_times">Helista 5 korda</string>
<string name="ring_nonstop">Helista lõputult</string> <string name="ring_nonstop">Helista lõputult</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Ez da %s kontua aurkitu, amaitu saioa eta hasi saioa berriro Google Tasks ezarpenetatik.</string> <string name="gtasks_error_accountNotFound">Ez da %s kontua aurkitu, amaitu saioa eta hasi saioa berriro Google Tasks ezarpenetatik.</string>
<string name="premium_record_audio">Grabatu ohar bat</string> <string name="premium_record_audio">Grabatu ohar bat</string>
<string name="premium_remove_file_confirm">Ziur al zaude\? Hau ezin da desegin</string>
<string name="ring_once">Doinua behin</string> <string name="ring_once">Doinua behin</string>
<string name="ring_five_times">Doinua bost aldiz</string> <string name="ring_five_times">Doinua bost aldiz</string>
<string name="ring_nonstop">Doinua etengabe</string> <string name="ring_nonstop">Doinua etengabe</string>

@ -85,7 +85,6 @@
<string name="gtasks_GLA_errorIOAuth">پوزش، ارتباط با سرورهای گوگل میسر نشد. لطفا دوباره تلاش نمایید.</string> <string name="gtasks_GLA_errorIOAuth">پوزش، ارتباط با سرورهای گوگل میسر نشد. لطفا دوباره تلاش نمایید.</string>
<string name="gtasks_GPr_header">وظایف گوگل</string> <string name="gtasks_GPr_header">وظایف گوگل</string>
<string name="premium_record_audio">ذخیره یادداشت</string> <string name="premium_record_audio">ذخیره یادداشت</string>
<string name="premium_remove_file_confirm">آیا مطمن هستید؟ قادر به برگرداندن نخواهید بود</string>
<string name="ring_once">یک بار زنگ بزن</string> <string name="ring_once">یک بار زنگ بزن</string>
<string name="ring_five_times">پنج بار زنگ بزن</string> <string name="ring_five_times">پنج بار زنگ بزن</string>
<string name="ring_nonstop">بدون توقف زنگ بزن</string> <string name="ring_nonstop">بدون توقف زنگ بزن</string>

@ -116,7 +116,6 @@
<string name="gtasks_GPr_header">Google tehtävät</string> <string name="gtasks_GPr_header">Google tehtävät</string>
<string name="gtasks_error_accountNotFound">Tiliä %s ei löydy kirjaudu ulos ja kirjaudu uudelleen Google Tehtävien asetuksiin.</string> <string name="gtasks_error_accountNotFound">Tiliä %s ei löydy kirjaudu ulos ja kirjaudu uudelleen Google Tehtävien asetuksiin.</string>
<string name="premium_record_audio">Tallenna muistiinpano</string> <string name="premium_record_audio">Tallenna muistiinpano</string>
<string name="premium_remove_file_confirm">Oletko varma? Ei voi peruuttaa</string>
<string name="ring_once">Soi kerran</string> <string name="ring_once">Soi kerran</string>
<string name="ring_five_times">Soi viisi kertaa</string> <string name="ring_five_times">Soi viisi kertaa</string>
<string name="ring_nonstop">Soi jatkuvasti</string> <string name="ring_nonstop">Soi jatkuvasti</string>

@ -117,7 +117,6 @@
<string name="gtasks_GLA_errorIOAuth">Erreur de communication avec les serveurs Google. Veuillez essayer plus tard.</string> <string name="gtasks_GLA_errorIOAuth">Erreur de communication avec les serveurs Google. Veuillez essayer plus tard.</string>
<string name="gtasks_error_accountNotFound">Le compte %s est introuvable veuillez vous déconnecter puis vous reconnecter depuis les préférences Google Tasks.</string> <string name="gtasks_error_accountNotFound">Le compte %s est introuvable veuillez vous déconnecter puis vous reconnecter depuis les préférences Google Tasks.</string>
<string name="premium_record_audio">Enregistrer un commentaire</string> <string name="premium_record_audio">Enregistrer un commentaire</string>
<string name="premium_remove_file_confirm">Êtes-vous sûr(e) \? Cette opération est irréversible</string>
<string name="ring_once">Sonner une fois</string> <string name="ring_once">Sonner une fois</string>
<string name="ring_five_times">Sonner cinq fois</string> <string name="ring_five_times">Sonner cinq fois</string>
<string name="ring_nonstop">Sonner en continu</string> <string name="ring_nonstop">Sonner en continu</string>

@ -121,7 +121,6 @@
<string name="gtasks_GLA_errorIOAuth">Houbo un problema ao comunicarse cos servidores de Google. Inténtao máis tarde.</string> <string name="gtasks_GLA_errorIOAuth">Houbo un problema ao comunicarse cos servidores de Google. Inténtao máis tarde.</string>
<string name="gtasks_error_accountNotFound">Non se atopou a conta %s — pecha a sesión e iníciaa de novo desde a configuración de Google Tasks.</string> <string name="gtasks_error_accountNotFound">Non se atopou a conta %s — pecha a sesión e iníciaa de novo desde a configuración de Google Tasks.</string>
<string name="premium_record_audio">Gravar unha nota</string> <string name="premium_record_audio">Gravar unha nota</string>
<string name="premium_remove_file_confirm">Está seguro/a\? Non se pode desfacer</string>
<string name="ring_once">Soar unha vez</string> <string name="ring_once">Soar unha vez</string>
<string name="ring_five_times">Soar cinco veces</string> <string name="ring_five_times">Soar cinco veces</string>
<string name="ring_nonstop">Soar sen parar</string> <string name="ring_nonstop">Soar sen parar</string>

@ -484,7 +484,6 @@
<string name="ring_nonstop">Zvoni neprekidno</string> <string name="ring_nonstop">Zvoni neprekidno</string>
<string name="ring_five_times">Zvoni pet puta</string> <string name="ring_five_times">Zvoni pet puta</string>
<string name="ring_once">Zvoni jednom</string> <string name="ring_once">Zvoni jednom</string>
<string name="premium_remove_file_confirm">Sigurno\? Ne može se poništiti</string>
<string name="premium_record_audio">Snimi bilješku</string> <string name="premium_record_audio">Snimi bilješku</string>
<string name="gtasks_error_accountNotFound">Račun %s nije pronađen odjavi se i ponovo se prijavi putem Google Tasks postavki.</string> <string name="gtasks_error_accountNotFound">Račun %s nije pronađen odjavi se i ponovo se prijavi putem Google Tasks postavki.</string>
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>

@ -123,7 +123,6 @@
<string name="gtasks_GLA_errorIOAuth">Sajnáljuk, de hiba történt a Google szervereivel való kommunikálás során. Próbáld újra később.</string> <string name="gtasks_GLA_errorIOAuth">Sajnáljuk, de hiba történt a Google szervereivel való kommunikálás során. Próbáld újra később.</string>
<string name="gtasks_error_accountNotFound">%s fiók nem található — kérlek, jelentkezz ki, és vissza a Google Tasks beállításokban.</string> <string name="gtasks_error_accountNotFound">%s fiók nem található — kérlek, jelentkezz ki, és vissza a Google Tasks beállításokban.</string>
<string name="premium_record_audio">Jegyzet rögzítése</string> <string name="premium_record_audio">Jegyzet rögzítése</string>
<string name="premium_remove_file_confirm">Biztos benne? A művelet nem visszavonható</string>
<string name="ring_once">Egy csengés</string> <string name="ring_once">Egy csengés</string>
<string name="ring_five_times">Öt csengés</string> <string name="ring_five_times">Öt csengés</string>
<string name="ring_nonstop">Folyamatos csengés</string> <string name="ring_nonstop">Folyamatos csengés</string>

@ -88,7 +88,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Akun %s tidak ditemukan—silakan keluar dan masuk kembali dari pengaturan Google Tasks.</string> <string name="gtasks_error_accountNotFound">Akun %s tidak ditemukan—silakan keluar dan masuk kembali dari pengaturan Google Tasks.</string>
<string name="premium_record_audio">Rekam catatan</string> <string name="premium_record_audio">Rekam catatan</string>
<string name="premium_remove_file_confirm">Apakah anda yakin\? Ini tidak dapat dibatalkan</string>
<string name="ring_once">Dering sekali</string> <string name="ring_once">Dering sekali</string>
<string name="ring_five_times">Dering lima kali</string> <string name="ring_five_times">Dering lima kali</string>
<string name="ring_nonstop">Dering nonstop</string> <string name="ring_nonstop">Dering nonstop</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Account %s non trovato—esci ed accedi nuovamente dalle impostazioni di Google Tasks.</string> <string name="gtasks_error_accountNotFound">Account %s non trovato—esci ed accedi nuovamente dalle impostazioni di Google Tasks.</string>
<string name="premium_record_audio">Registra una nota</string> <string name="premium_record_audio">Registra una nota</string>
<string name="premium_remove_file_confirm">Sei sicuro/a\? L\'azione non può essere annullata</string>
<string name="ring_once">Suona una volta</string> <string name="ring_once">Suona una volta</string>
<string name="ring_five_times">Suona cinque volte</string> <string name="ring_five_times">Suona cinque volte</string>
<string name="ring_nonstop">Suona ininterrottamente</string> <string name="ring_nonstop">Suona ininterrottamente</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">״משימות גוגל״</string> <string name="gtasks_GPr_header">״משימות גוגל״</string>
<string name="gtasks_error_accountNotFound">החשבון %s לא נמצא. אנא התנתק והתחבר שוב במסך הגדרות של ״משימות גוגל״.</string> <string name="gtasks_error_accountNotFound">החשבון %s לא נמצא. אנא התנתק והתחבר שוב במסך הגדרות של ״משימות גוגל״.</string>
<string name="premium_record_audio">הקלד הערה</string> <string name="premium_record_audio">הקלד הערה</string>
<string name="premium_remove_file_confirm">בטוח? לא ניתן לבטל את הפעולה</string>
<string name="ring_once">צלצל פעם אחת</string> <string name="ring_once">צלצל פעם אחת</string>
<string name="ring_five_times">צלצל חמש פעמים</string> <string name="ring_five_times">צלצל חמש פעמים</string>
<string name="ring_nonstop">צלצל ללא הפסקה</string> <string name="ring_nonstop">צלצל ללא הפסקה</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">アカウント %s が見つかりません—ログアウトして再度 Google Tasks 設定からログインしてください.</string> <string name="gtasks_error_accountNotFound">アカウント %s が見つかりません—ログアウトして再度 Google Tasks 設定からログインしてください.</string>
<string name="premium_record_audio">注釈を記録</string> <string name="premium_record_audio">注釈を記録</string>
<string name="premium_remove_file_confirm">よろしいですか? 取り消しできません</string>
<string name="ring_once">1回通知音を鳴らす</string> <string name="ring_once">1回通知音を鳴らす</string>
<string name="ring_five_times">5回通知音を鳴らす</string> <string name="ring_five_times">5回通知音を鳴らす</string>
<string name="ring_nonstop">通知音を鳴らし続ける</string> <string name="ring_nonstop">通知音を鳴らし続ける</string>

@ -124,7 +124,6 @@
<string name="gtasks_GPr_header">구글 할일 목록 (Google Tasks)</string> <string name="gtasks_GPr_header">구글 할일 목록 (Google Tasks)</string>
<string name="gtasks_error_accountNotFound">%s 계정을 찾을 수 없습니다 - 로그아웃하고 구글 할일목록 (Google Tasks) 설정에서 다시 로그인해 보세요.</string> <string name="gtasks_error_accountNotFound">%s 계정을 찾을 수 없습니다 - 로그아웃하고 구글 할일목록 (Google Tasks) 설정에서 다시 로그인해 보세요.</string>
<string name="premium_record_audio">노트 기록</string> <string name="premium_record_audio">노트 기록</string>
<string name="premium_remove_file_confirm">정말입니까? 되돌릴 수 없습니다</string>
<string name="ring_once">한번 울림</string> <string name="ring_once">한번 울림</string>
<string name="ring_five_times">다섯번 울림</string> <string name="ring_five_times">다섯번 울림</string>
<string name="ring_nonstop">계속 울림</string> <string name="ring_nonstop">계속 울림</string>

@ -130,7 +130,6 @@
<string name="gtasks_GLA_errorIOAuth">Atsiprašome, turime bėdų susisiekiant su Google serveriais. Prašome pabandyti vėliau.</string> <string name="gtasks_GLA_errorIOAuth">Atsiprašome, turime bėdų susisiekiant su Google serveriais. Prašome pabandyti vėliau.</string>
<string name="gtasks_error_accountNotFound">Paskyra %s nerasta - prašome atsijungti ir prisijungti iš Google Tasks nustatymų.</string> <string name="gtasks_error_accountNotFound">Paskyra %s nerasta - prašome atsijungti ir prisijungti iš Google Tasks nustatymų.</string>
<string name="premium_record_audio">Įrašyti pastabą</string> <string name="premium_record_audio">Įrašyti pastabą</string>
<string name="premium_remove_file_confirm">Ar tikrai? Nebegalės būti atstatyta</string>
<string name="ring_once">Suskambėti vieną kartą</string> <string name="ring_once">Suskambėti vieną kartą</string>
<string name="ring_five_times">Suskambėti penkis kartus</string> <string name="ring_five_times">Suskambėti penkis kartus</string>
<string name="ring_nonstop">Skambėti nepaliaujant</string> <string name="ring_nonstop">Skambėti nepaliaujant</string>

@ -218,7 +218,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Kontoen %s ble ikke funnet. Logg ut og inn igjen fra Google Tasks-innstillingene.</string> <string name="gtasks_error_accountNotFound">Kontoen %s ble ikke funnet. Logg ut og inn igjen fra Google Tasks-innstillingene.</string>
<string name="premium_record_audio">Spill inn en anmerkning</string> <string name="premium_record_audio">Spill inn en anmerkning</string>
<string name="premium_remove_file_confirm">Er du sikker\? Dette kan ikke reverseres</string>
<string name="ring_once">Ring én gang</string> <string name="ring_once">Ring én gang</string>
<string name="ring_five_times">Ring fem ganger</string> <string name="ring_five_times">Ring fem ganger</string>
<string name="ring_nonstop">Ring konstant</string> <string name="ring_nonstop">Ring konstant</string>

@ -125,7 +125,6 @@
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Account %s niet gevonden—probeer opnieuw in te loggen via de instellingen van Google Tasks.</string> <string name="gtasks_error_accountNotFound">Account %s niet gevonden—probeer opnieuw in te loggen via de instellingen van Google Tasks.</string>
<string name="premium_record_audio">Notitie opnemen</string> <string name="premium_record_audio">Notitie opnemen</string>
<string name="premium_remove_file_confirm">Doorgaan\? Kan niet teruggezet worden</string>
<string name="ring_once">Een keer bellen</string> <string name="ring_once">Een keer bellen</string>
<string name="ring_five_times">Vijf keer bellen</string> <string name="ring_five_times">Vijf keer bellen</string>
<string name="ring_nonstop">Blijven bellen</string> <string name="ring_nonstop">Blijven bellen</string>

@ -120,7 +120,6 @@
<string name="gtasks_GPr_header">Zadania Google</string> <string name="gtasks_GPr_header">Zadania Google</string>
<string name="gtasks_error_accountNotFound">Nie znaleziono konta %s —proszę wyloguj się i zaloguj ponownie w ustawieniach Google Zadań.</string> <string name="gtasks_error_accountNotFound">Nie znaleziono konta %s —proszę wyloguj się i zaloguj ponownie w ustawieniach Google Zadań.</string>
<string name="premium_record_audio">Nagraj notatkę</string> <string name="premium_record_audio">Nagraj notatkę</string>
<string name="premium_remove_file_confirm">Jesteś pewny\? Tych zmian nie można cofnąć</string>
<string name="ring_once">Dzwoń raz</string> <string name="ring_once">Dzwoń raz</string>
<string name="ring_five_times">Dzwoń pięć razy</string> <string name="ring_five_times">Dzwoń pięć razy</string>
<string name="ring_nonstop">Dzwoń nonstop</string> <string name="ring_nonstop">Dzwoń nonstop</string>

@ -121,7 +121,6 @@
<string name="gtasks_GPr_header">Google Tarefas</string> <string name="gtasks_GPr_header">Google Tarefas</string>
<string name="gtasks_error_accountNotFound">Conta %s não encontrada—Desconecte e conecte-se novamente pelo painel Google Tasks.</string> <string name="gtasks_error_accountNotFound">Conta %s não encontrada—Desconecte e conecte-se novamente pelo painel Google Tasks.</string>
<string name="premium_record_audio">Gravar uma nota</string> <string name="premium_record_audio">Gravar uma nota</string>
<string name="premium_remove_file_confirm">Tem certeza\? Não pode ser desfeito</string>
<string name="ring_once">Tocar uma vez</string> <string name="ring_once">Tocar uma vez</string>
<string name="ring_five_times">Tocar cinco vezes</string> <string name="ring_five_times">Tocar cinco vezes</string>
<string name="ring_nonstop">Tocar continuamente</string> <string name="ring_nonstop">Tocar continuamente</string>

@ -116,7 +116,6 @@
<string name="gtasks_GPr_header">Tarefas Google</string> <string name="gtasks_GPr_header">Tarefas Google</string>
<string name="gtasks_error_accountNotFound">Não foi encontrada a conta %s. Termine a sessão e inicie-a novamente nas definições das tarefas Google.</string> <string name="gtasks_error_accountNotFound">Não foi encontrada a conta %s. Termine a sessão e inicie-a novamente nas definições das tarefas Google.</string>
<string name="premium_record_audio">Gravar uma nota áudio</string> <string name="premium_record_audio">Gravar uma nota áudio</string>
<string name="premium_remove_file_confirm">Tem certeza\? A ação não pode ser anulada</string>
<string name="ring_once">Tocar uma vez</string> <string name="ring_once">Tocar uma vez</string>
<string name="ring_five_times">Tocar 5 vezes</string> <string name="ring_five_times">Tocar 5 vezes</string>
<string name="ring_nonstop">Tocar sem parar</string> <string name="ring_nonstop">Tocar sem parar</string>

@ -399,7 +399,6 @@
<string name="ring_nonstop">Sună non-stop</string> <string name="ring_nonstop">Sună non-stop</string>
<string name="ring_five_times">Sună de cinci ori</string> <string name="ring_five_times">Sună de cinci ori</string>
<string name="ring_once">Sună o dată</string> <string name="ring_once">Sună o dată</string>
<string name="premium_remove_file_confirm">Ești sigur\? Nu poate fi anulat</string>
<string name="premium_record_audio">Înregistrați o notă</string> <string name="premium_record_audio">Înregistrați o notă</string>
<string name="gtasks_error_accountNotFound">Contul %s nu a fost găsit - vă rugăm să vă deconectați și să vă conectați din nou din setările Google Tasks.</string> <string name="gtasks_error_accountNotFound">Contul %s nu a fost găsit - vă rugăm să vă deconectați și să vă conectați din nou din setările Google Tasks.</string>
<string name="gtasks_GPr_header">Sarcini Google</string> <string name="gtasks_GPr_header">Sarcini Google</string>

@ -125,7 +125,6 @@
<string name="gtasks_GLA_errorIOAuth">При обращении к серверам Google возникли проблемы. Пожалуйста, попробуйте позже.</string> <string name="gtasks_GLA_errorIOAuth">При обращении к серверам Google возникли проблемы. Пожалуйста, попробуйте позже.</string>
<string name="gtasks_error_accountNotFound">Учетная запись %s не найдена — пожалуйста, выйдите и войдите снова через настройки Google Tasks.</string> <string name="gtasks_error_accountNotFound">Учетная запись %s не найдена — пожалуйста, выйдите и войдите снова через настройки Google Tasks.</string>
<string name="premium_record_audio">Записать заметку</string> <string name="premium_record_audio">Записать заметку</string>
<string name="premium_remove_file_confirm">Вы уверены? Действие нельзя отменить</string>
<string name="ring_once">1 раз</string> <string name="ring_once">1 раз</string>
<string name="ring_five_times">5 раз</string> <string name="ring_five_times">5 раз</string>
<string name="ring_nonstop">Звучать безостановочно</string> <string name="ring_nonstop">Звучать безостановочно</string>

@ -363,7 +363,6 @@
<string name="SSD_sort_modified">අවසන් වරට වෙනස් කරන ලද දිනය අනුව</string> <string name="SSD_sort_modified">අවසන් වරට වෙනස් කරන ලද දිනය අනුව</string>
<string name="astrid_sort_order">Astrid අතින් වර්ග කිරීම</string> <string name="astrid_sort_order">Astrid අතින් වර්ග කිරීම</string>
<string name="TVA_add_comment">අදහස දක්වන්න…</string> <string name="TVA_add_comment">අදහස දක්වන්න…</string>
<string name="premium_remove_file_confirm">ඔබට විශ්වාසද\? අහෝසි කළ නොහැක</string>
<string name="premium_record_audio">සටහනක් පටිගත කරන්න</string> <string name="premium_record_audio">සටහනක් පටිගත කරන්න</string>
<string name="gtasks_error_accountNotFound">%s ගිණුම සොයාගත නොහැකිවිය - කරුණාකර Log out වී Google Tasks සැකසුම් වලින් නැවත ලොග් වන්න.</string> <string name="gtasks_error_accountNotFound">%s ගිණුම සොයාගත නොහැකිවිය - කරුණාකර Log out වී Google Tasks සැකසුම් වලින් නැවත ලොග් වන්න.</string>
<string name="gtasks_GPr_header">Google කාර්යයන්</string> <string name="gtasks_GPr_header">Google කාර්යයන්</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">Úlohy Google</string> <string name="gtasks_GPr_header">Úlohy Google</string>
<string name="gtasks_error_accountNotFound">Účet %s sa nenašiel—prosím, odhlás sa a znovu prihlás v nastaveniach Úlohy Google.</string> <string name="gtasks_error_accountNotFound">Účet %s sa nenašiel—prosím, odhlás sa a znovu prihlás v nastaveniach Úlohy Google.</string>
<string name="premium_record_audio">Nahrať poznámku</string> <string name="premium_record_audio">Nahrať poznámku</string>
<string name="premium_remove_file_confirm">Naozaj? Nedá sa vrátiť</string>
<string name="ring_once">Zvoniť raz</string> <string name="ring_once">Zvoniť raz</string>
<string name="ring_five_times">Zvoniť päť krát</string> <string name="ring_five_times">Zvoniť päť krát</string>
<string name="ring_nonstop">Zvoniť neustále</string> <string name="ring_nonstop">Zvoniť neustále</string>

@ -99,7 +99,6 @@
<string name="gtasks_GPr_header">Google Naloge</string> <string name="gtasks_GPr_header">Google Naloge</string>
<string name="gtasks_error_accountNotFound">Račun %s ni najden—prosimo odjavite se, nato pa ponovno prijavite skozi nastavitve Google Nalog.</string> <string name="gtasks_error_accountNotFound">Račun %s ni najden—prosimo odjavite se, nato pa ponovno prijavite skozi nastavitve Google Nalog.</string>
<string name="premium_record_audio">Posnemi opombo</string> <string name="premium_record_audio">Posnemi opombo</string>
<string name="premium_remove_file_confirm">Ste prepričani? Tega ni mogoče razveljaviti</string>
<string name="rmd_NoA_done">Končano</string> <string name="rmd_NoA_done">Končano</string>
<string name="rmd_NoA_snooze">Dremež</string> <string name="rmd_NoA_snooze">Dremež</string>
<string name="rmd_EPr_quiet_hours_start_title">Tihe ure se začnejo ob</string> <string name="rmd_EPr_quiet_hours_start_title">Tihe ure se začnejo ob</string>

@ -116,7 +116,6 @@
<string name="gtasks_GPr_header">Google Uppgifter</string> <string name="gtasks_GPr_header">Google Uppgifter</string>
<string name="gtasks_error_accountNotFound">Kontot %s hittades inte— vänligen logga ut och in igen från inställningarna för Google Tasks.</string> <string name="gtasks_error_accountNotFound">Kontot %s hittades inte— vänligen logga ut och in igen från inställningarna för Google Tasks.</string>
<string name="premium_record_audio">Spela in en anteckning</string> <string name="premium_record_audio">Spela in en anteckning</string>
<string name="premium_remove_file_confirm">Är du säker? Detta kan inte ångras</string>
<string name="ring_once">Ring en gång</string> <string name="ring_once">Ring en gång</string>
<string name="ring_five_times">Ring fem gånger</string> <string name="ring_five_times">Ring fem gånger</string>
<string name="ring_nonstop">Ring konstant</string> <string name="ring_nonstop">Ring konstant</string>

@ -111,7 +111,6 @@
<string name="snooze_all">அனைத்தையும் உறக்கநிலையில் வைக்கவும்</string> <string name="snooze_all">அனைத்தையும் உறக்கநிலையில் வைக்கவும்</string>
<string name="rmd_NoA_snooze">உறக்கநிலை</string> <string name="rmd_NoA_snooze">உறக்கநிலை</string>
<string name="rmd_NoA_done">முழுமை</string> <string name="rmd_NoA_done">முழுமை</string>
<string name="premium_remove_file_confirm">நீ சொல்வது உறுதியா\? செயல்தவிர்க்க முடியாது</string>
<string name="premium_record_audio">ஒரு குறிப்பைப் பதிவுசெய்க</string> <string name="premium_record_audio">ஒரு குறிப்பைப் பதிவுசெய்க</string>
<string name="gtasks_error_accountNotFound">கணக்கு %s காணப்படவில்லை - தயவுசெய்து வெளியேறி, Google பணிகள் அமைப்புகளிலிருந்து மீண்டும் உள்நுழைக.</string> <string name="gtasks_error_accountNotFound">கணக்கு %s காணப்படவில்லை - தயவுசெய்து வெளியேறி, Google பணிகள் அமைப்புகளிலிருந்து மீண்டும் உள்நுழைக.</string>
<string name="gtasks_GPr_header">Google பணிகள்</string> <string name="gtasks_GPr_header">Google பணிகள்</string>

@ -508,7 +508,6 @@
<string name="ring_nonstop">แหวนไม่หยุดนิ่ง</string> <string name="ring_nonstop">แหวนไม่หยุดนิ่ง</string>
<string name="ring_five_times">แหวนห้าครั้ง</string> <string name="ring_five_times">แหวนห้าครั้ง</string>
<string name="ring_once">เรียกหนึ่งครั้ง</string> <string name="ring_once">เรียกหนึ่งครั้ง</string>
<string name="premium_remove_file_confirm">เธอแน่ใจหรือ ไม่สามารถเลิกทําได้</string>
<string name="premium_record_audio">การบันทึกบันทึกย่อ</string> <string name="premium_record_audio">การบันทึกบันทึกย่อ</string>
<string name="gtasks_GPr_header">งานของ Google</string> <string name="gtasks_GPr_header">งานของ Google</string>
<string name="gtasks_GLA_authenticating">กําลังรับรองความถูกต้อง…</string> <string name="gtasks_GLA_authenticating">กําลังรับรองความถูกต้อง…</string>

@ -126,7 +126,6 @@
<string name="gtasks_GPr_header">Google Görevler</string> <string name="gtasks_GPr_header">Google Görevler</string>
<string name="gtasks_error_accountNotFound">%s hesabı bulunamadı—lütfen çıkış yapıp Google Görevler ayarlarından yeniden oturum açın.</string> <string name="gtasks_error_accountNotFound">%s hesabı bulunamadı—lütfen çıkış yapıp Google Görevler ayarlarından yeniden oturum açın.</string>
<string name="premium_record_audio">Bir not kaydet</string> <string name="premium_record_audio">Bir not kaydet</string>
<string name="premium_remove_file_confirm">Emin misiniz? Geri döndürülemez</string>
<string name="ring_once">Bir kez çal</string> <string name="ring_once">Bir kez çal</string>
<string name="ring_five_times">Beş kez çal</string> <string name="ring_five_times">Beş kez çal</string>
<string name="ring_nonstop">Durmadan çal</string> <string name="ring_nonstop">Durmadan çal</string>

@ -125,7 +125,6 @@
<string name="gtasks_GLA_errorIOAuth">Вибачте, виникли проблеми звернення до серверів Google. Будь ласка, спробуйте пізніше.</string> <string name="gtasks_GLA_errorIOAuth">Вибачте, виникли проблеми звернення до серверів Google. Будь ласка, спробуйте пізніше.</string>
<string name="gtasks_error_accountNotFound">Обліковий запис %s не знайдено — будь ласка, вийдіть і увійдіть знову через налаштування Google Tasks.</string> <string name="gtasks_error_accountNotFound">Обліковий запис %s не знайдено — будь ласка, вийдіть і увійдіть знову через налаштування Google Tasks.</string>
<string name="premium_record_audio">Записати голосову замітку</string> <string name="premium_record_audio">Записати голосову замітку</string>
<string name="premium_remove_file_confirm">Ви впевнені\? Це не можна буде скасувати</string>
<string name="ring_once">Звучати один раз</string> <string name="ring_once">Звучати один раз</string>
<string name="ring_five_times">Звучати п’ять разів</string> <string name="ring_five_times">Звучати п’ять разів</string>
<string name="ring_nonstop">Звучати безперервно</string> <string name="ring_nonstop">Звучати безперервно</string>

@ -159,7 +159,6 @@
<string name="ring_nonstop">بغیر رکے رنگ بجنا</string> <string name="ring_nonstop">بغیر رکے رنگ بجنا</string>
<string name="ring_five_times">پانچ بار رنگ بجنا</string> <string name="ring_five_times">پانچ بار رنگ بجنا</string>
<string name="ring_once">ایک بار رنگ بجنا</string> <string name="ring_once">ایک بار رنگ بجنا</string>
<string name="premium_remove_file_confirm">کیا آپ تیار ہیں؟ یہ واپس نہیں ہو گا</string>
<string name="premium_record_audio">ایک نوٹ ریکارڈ کریں</string> <string name="premium_record_audio">ایک نوٹ ریکارڈ کریں</string>
<string name="repeat_weekly">ہفتہ وار دہرانا</string> <string name="repeat_weekly">ہفتہ وار دہرانا</string>
<string name="repeat_daily">روزانہ دہرانا</string> <string name="repeat_daily">روزانہ دہرانا</string>

@ -590,7 +590,6 @@
<string name="ring_nonstop">Đổ chuông không ngừng</string> <string name="ring_nonstop">Đổ chuông không ngừng</string>
<string name="ring_five_times">Đổ chuông năm lần</string> <string name="ring_five_times">Đổ chuông năm lần</string>
<string name="ring_once">Đổ chuông một lần</string> <string name="ring_once">Đổ chuông một lần</string>
<string name="premium_remove_file_confirm">Bạn có chắc không\? Không thể hoàn tác</string>
<string name="premium_record_audio">Ghi lại một bản ghi chú</string> <string name="premium_record_audio">Ghi lại một bản ghi chú</string>
<string name="gtasks_error_accountNotFound">Không tìm thấy tài khoản %s—vui lòng đăng xuất và đăng nhập lại trong cài đặt của Google Tasks.</string> <string name="gtasks_error_accountNotFound">Không tìm thấy tài khoản %s—vui lòng đăng xuất và đăng nhập lại trong cài đặt của Google Tasks.</string>
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>

@ -114,7 +114,6 @@
<string name="gtasks_GLA_errorIOAuth">对不起,我们在与 Google 服务器通讯时遇到了问题。请稍后再尝试。</string> <string name="gtasks_GLA_errorIOAuth">对不起,我们在与 Google 服务器通讯时遇到了问题。请稍后再尝试。</string>
<string name="gtasks_error_accountNotFound">找不到帐户 %s —请先退出,再从 Google Tasks 设置中重新登录。</string> <string name="gtasks_error_accountNotFound">找不到帐户 %s —请先退出,再从 Google Tasks 设置中重新登录。</string>
<string name="premium_record_audio">录制一条便笺</string> <string name="premium_record_audio">录制一条便笺</string>
<string name="premium_remove_file_confirm">您确定吗?此操作无法恢复喔</string>
<string name="ring_once">响铃一次</string> <string name="ring_once">响铃一次</string>
<string name="ring_five_times">响铃五次</string> <string name="ring_five_times">响铃五次</string>
<string name="ring_nonstop">响个不停</string> <string name="ring_nonstop">响个不停</string>

@ -95,7 +95,6 @@
<string name="gtasks_GPr_header">Google Tasks (測試中!)</string> <string name="gtasks_GPr_header">Google Tasks (測試中!)</string>
<string name="gtasks_error_accountNotFound">找不到帳戶%s——請退出然後從Google 工作表設置中重新登錄。</string> <string name="gtasks_error_accountNotFound">找不到帳戶%s——請退出然後從Google 工作表設置中重新登錄。</string>
<string name="premium_record_audio">錄製一條便箋</string> <string name="premium_record_audio">錄製一條便箋</string>
<string name="premium_remove_file_confirm">您確定嗎?無法恢復的喔</string>
<string name="ring_once">響鈴一次</string> <string name="ring_once">響鈴一次</string>
<string name="ring_five_times">響鈴五次</string> <string name="ring_five_times">響鈴五次</string>
<string name="ring_nonstop">不斷響鈴</string> <string name="ring_nonstop">不斷響鈴</string>

@ -147,7 +147,6 @@ File %1$s contained %2$s.\n\n
<string name="gtasks_GPr_header">Google Tasks</string> <string name="gtasks_GPr_header">Google Tasks</string>
<string name="gtasks_error_accountNotFound">Account %s not found—please log out and log back in from the Google Tasks settings.</string> <string name="gtasks_error_accountNotFound">Account %s not found—please log out and log back in from the Google Tasks settings.</string>
<string name="premium_record_audio">Record a note</string> <string name="premium_record_audio">Record a note</string>
<string name="premium_remove_file_confirm">Are you sure? Cannot be undone</string>
<string name="ring_once">Ring once</string> <string name="ring_once">Ring once</string>
<string name="ring_five_times">Ring five times</string> <string name="ring_five_times">Ring five times</string>
<string name="ring_nonstop">Ring nonstop</string> <string name="ring_nonstop">Ring nonstop</string>

Loading…
Cancel
Save