Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2024-04-18 03:42:43 +02:00
commit 44c2ec6508
116 changed files with 2258 additions and 1019 deletions

View file

@ -1,4 +1,4 @@
FROM ubuntu:jammy@sha256:77906da86b60585ce12215807090eb327e7386c8fafb5402369e421f44eff17e
FROM ubuntu:jammy@sha256:1b8d8ff4777f36f19bfe73ee4df61e3a0b789caeff29caa019539ec7c9a57f95
ARG DEBIAN_FRONTEND=noninteractive
ENV ANDROID_HOME=/usr/lib/android-sdk

View file

@ -39,7 +39,7 @@ jobs:
with:
swap-size-gb: 10
- name: Initialize CodeQL
uses: github/codeql-action/init@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
languages: ${{ matrix.language }}
- name: Set up JDK 17
@ -53,4 +53,4 @@ jobs:
echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties"
./gradlew assembleDebug
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1

View file

@ -42,6 +42,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
sarif_file: results.sarif

View file

@ -77,7 +77,7 @@ configurations.configureEach {
// semantic versioning for version code
def versionMajor = 3
def versionMinor = 29
def versionMinor = 30
def versionPatch = 0
def versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@ import com.nextcloud.client.preferences.SubFolderRule;
import com.owncloud.android.AbstractIT;
import com.owncloud.android.databinding.SyncedFoldersLayoutBinding;
import com.owncloud.android.datamodel.MediaFolderType;
import com.owncloud.android.datamodel.SyncedFolder;
import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
import com.owncloud.android.ui.activity.SyncedFoldersActivity;
import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
@ -63,7 +64,8 @@ public class SyncedFoldersActivityIT extends AbstractIT {
MediaFolderType.IMAGE,
false,
SubFolderRule.YEAR_MONTH,
false);
false,
SyncedFolder.NOT_SCANNED_YET);
SyncedFolderPreferencesDialogFragment sut = SyncedFolderPreferencesDialogFragment.newInstance(item, 0);
Intent intent = new Intent(targetContext, SyncedFoldersActivity.class);

View file

@ -201,7 +201,7 @@ class BackgroundJobManagerTest {
fun job_is_unique_and_replaces_previous_job() {
verify(workManager).enqueueUniqueWork(
eq(BackgroundJobManagerImpl.JOB_CONTENT_OBSERVER),
eq(ExistingWorkPolicy.APPEND),
eq(ExistingWorkPolicy.REPLACE),
argThat(IsOneTimeWorkRequest())
)
}

View file

@ -0,0 +1,32 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android
import com.owncloud.android.datamodel.OCFile
import java.security.SecureRandom
open class EncryptionIT : AbstractIT() {
fun testFolder(): OCFile {
val rootPath = "/"
val folderPath = "/TestFolder/"
OCFile(rootPath).apply {
storageManager.saveFile(this)
}
return OCFile(folderPath).apply {
decryptedRemotePath = folderPath
isEncrypted = true
fileLength = SecureRandom().nextLong()
setFolder()
parentId = storageManager.getFileByDecryptedRemotePath(rootPath)!!.fileId
storageManager.saveFile(this)
}
}
}

View file

@ -836,7 +836,7 @@ public class EncryptionTestIT extends AbstractIT {
// Encryption
Cipher encryptorCipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv);
EncryptionUtils.encryptFile(file, encryptorCipher);
EncryptionUtils.encryptFile(user.getAccountName(), file, encryptorCipher);
String encryptorCipherAuthTag = EncryptionUtils.getAuthenticationTag(encryptorCipher);
// Decryption

View file

@ -7,13 +7,18 @@
*/
package com.owncloud.android.utils
import com.owncloud.android.AbstractIT
import com.owncloud.android.EncryptionIT
import com.owncloud.android.datamodel.ArbitraryDataProviderImpl
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedMetadata
import com.owncloud.android.lib.resources.e2ee.CsrHelper
import com.owncloud.android.operations.RefreshFolderOperation
import org.junit.Assert.assertEquals
import org.junit.Test
class EncryptionUtilsIT : AbstractIT() {
class EncryptionUtilsIT : EncryptionIT() {
@Throws(
java.security.NoSuchAlgorithmException::class,
java.io.IOException::class,
@ -30,4 +35,25 @@ class EncryptionUtilsIT : AbstractIT() {
assertEquals(key, EncryptionUtils.getPublicKey(user, e2eUser, arbitraryDataProvider))
}
@Test
@Throws(Exception::class)
fun testUpdateFileNameForEncryptedFileV1() {
val folder = testFolder()
val decryptedFilename = "image.png"
val mockEncryptedFilename = "encrypted_file_name.png"
val decryptedMetadata = DecryptedMetadata()
val filesData = DecryptedFile().apply {
encrypted = Data().apply {
filename = decryptedFilename
}
}
val files = mapOf(mockEncryptedFilename to filesData)
val metadata = DecryptedFolderMetadataFileV1(decryptedMetadata, files)
RefreshFolderOperation.updateFileNameForEncryptedFileV1(storageManager, metadata, folder)
assertEquals(folder.decryptedRemotePath.contains("null"), false)
}
}

View file

@ -10,7 +10,7 @@ package com.owncloud.android.utils
import com.google.gson.reflect.TypeToken
import com.nextcloud.client.account.MockUser
import com.nextcloud.common.User
import com.owncloud.android.AbstractIT
import com.owncloud.android.EncryptionIT
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.e2e.v1.decrypted.Data
import com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFolderMetadataFileV1
@ -21,13 +21,15 @@ import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedUser
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledrop
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFiledropUser
import com.owncloud.android.datamodel.e2e.v2.encrypted.EncryptedFolderMetadataFile
import com.owncloud.android.operations.RefreshFolderOperation
import com.owncloud.android.util.EncryptionTestIT
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertTrue
import org.junit.Assert.assertNotEquals
import org.junit.Test
class EncryptionUtilsV2IT : AbstractIT() {
@Suppress("TooManyFunctions", "LargeClass")
class EncryptionUtilsV2IT : EncryptionIT() {
private val encryptionTestUtils = EncryptionTestUtils()
private val encryptionUtilsV2 = EncryptionUtilsV2()
@ -781,6 +783,21 @@ class EncryptionUtilsV2IT : AbstractIT() {
assertTrue(encryptionUtilsV2.verifySignedMessage(base64Ans, jsonBase64, certs))
}
@Test
@Throws(Exception::class)
fun testUpdateFileNameForEncryptedFile() {
val folder = testFolder()
val metadata = EncryptionTestUtils().generateFolderMetadataV2(
client.userId,
EncryptionTestIT.publicKey
)
RefreshFolderOperation.updateFileNameForEncryptedFile(storageManager, metadata, folder)
assertEquals(folder.decryptedRemotePath.contains("null"), false)
}
/**
* DecryptedFolderMetadata -> EncryptedFolderMetadata -> JSON -> encrypt -> decrypt -> JSON ->
* EncryptedFolderMetadata -> DecryptedFolderMetadata

View file

@ -174,7 +174,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
MediaFolderType.IMAGE,
false,
SubFolderRule.YEAR_MONTH,
false
false,
SyncedFolder.NOT_SCANNED_YET
)
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
}
@ -198,7 +199,8 @@ class SyncedFolderUtilsTest : AbstractIT() {
MediaFolderType.IMAGE,
false,
SubFolderRule.YEAR_MONTH,
false
false,
SyncedFolder.NOT_SCANNED_YET
)
Assert.assertFalse(SyncedFolderUtils.isQualifyingMediaFolder(folder))
}

View file

@ -42,8 +42,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.nextcloud.client.assistant.component.AddTaskAlertDialog
import com.nextcloud.client.assistant.component.CenterText
import com.nextcloud.client.assistant.component.TaskTypesRow
import com.nextcloud.client.assistant.component.TaskView
import com.nextcloud.client.assistant.taskTypes.TaskTypesRow
import com.nextcloud.client.assistant.task.TaskView
import com.nextcloud.client.assistant.repository.AssistantMockRepository
import com.nextcloud.ui.composeActivity.ComposeActivity
import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog

View file

@ -35,3 +35,8 @@ fun Task.statusData(): Pair<Int, Int> {
}
}
}
// TODO add
fun Task.completionDateRepresentation(): String {
return completionExpectedAt ?: "TODO IMPLEMENT IT"
}

View file

@ -7,12 +7,14 @@
*/
package com.nextcloud.client.assistant.repository
import com.nextcloud.utils.extensions.getRandomString
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.assistant.model.Task
import com.owncloud.android.lib.resources.assistant.model.TaskList
import com.owncloud.android.lib.resources.assistant.model.TaskType
import com.owncloud.android.lib.resources.assistant.model.TaskTypes
@Suppress("MagicNumber")
class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : AssistantRepositoryType {
override fun getTaskTypes(): RemoteOperationResult<TaskTypes> {
return RemoteOperationResult<TaskTypes>(RemoteOperationResult.ResultCode.OK).apply {
@ -41,16 +43,8 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass
null,
"12",
"",
"Give me some text",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s," +
" when an unknown printer took a galley of type and scrambled it to make a type" +
" specimen book. It has survived not only five centuries, " +
"but also the leap into electronic typesetting, remaining essentially unchanged." +
" It was popularised in the 1960s with the release of Letraset sheets containing " +
"Lorem Ipsum passages, and more recently with desktop publishing software like Aldus" +
" PageMaker including versions of Lorem Ipsum",
"",
"Give me some long text 1",
"Lorem ipsum".getRandomString(100),
""
),
Task(
@ -60,7 +54,62 @@ class AssistantMockRepository(private val giveEmptyTasks: Boolean = false) : Ass
"12",
"",
"Give me some text 2",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem".getRandomString(100),
"",
""
),
Task(
3,
"FreePrompt",
null,
"12",
"",
"Give me some text 3",
"Lorem".getRandomString(300),
"",
""
),
Task(
4,
"FreePrompt",
null,
"12",
"",
"Give me some text 4",
"Lorem".getRandomString(300),
"",
""
),
Task(
5,
"FreePrompt",
null,
"12",
"",
"Give me some text 5",
"Lorem".getRandomString(300),
"",
""
),
Task(
6,
"FreePrompt",
null,
"12",
"",
"Give me some text 6",
"Lorem".getRandomString(300),
"",
""
),
Task(
7,
"FreePrompt",
null,
"12",
"",
"Give me some text 7",
"Lorem".getRandomString(300),
"",
""
)

View file

@ -0,0 +1,58 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.assistant.task
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.nextcloud.client.assistant.extensions.statusData
import com.owncloud.android.lib.resources.assistant.model.Task
@Composable
fun TaskStatus(task: Task, foregroundColor: Color) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
val (iconId, descriptionId) = task.statusData()
Image(
painter = painterResource(id = iconId),
modifier = Modifier.size(16.dp),
colorFilter = ColorFilter.tint(foregroundColor),
contentDescription = "status icon"
)
Spacer(modifier = Modifier.width(6.dp))
Text(text = stringResource(id = descriptionId), color = foregroundColor)
/*
Spacer(modifier = Modifier.weight(1f))
Text(text = task.completionDateRepresentation(), color = foregroundColor)
Spacer(modifier = Modifier.width(6.dp))
*/
}
}

View file

@ -1,27 +1,22 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <alper_ozturk@proton.me>
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.assistant.component
package com.nextcloud.client.assistant.task
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
@ -31,18 +26,15 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nextcloud.client.assistant.extensions.statusData
import com.nextcloud.client.assistant.taskDetail.TaskDetailBottomSheet
import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet
import com.nextcloud.utils.extensions.getRandomString
import com.owncloud.android.R
import com.owncloud.android.lib.resources.assistant.model.Task
@ -53,7 +45,7 @@ fun TaskView(
task: Task,
showDeleteTaskAlertDialog: (Long) -> Unit
) {
var expanded by remember { mutableStateOf(false) }
var showTaskDetailBottomSheet by remember { mutableStateOf(false) }
var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
Column(
@ -62,7 +54,7 @@ fun TaskView(
.clip(RoundedCornerShape(16.dp))
.background(MaterialTheme.colorScheme.primary)
.combinedClickable(onClick = {
expanded = !expanded
showTaskDetailBottomSheet = true
}, onLongClick = {
showMoreActionsBottomSheet = true
})
@ -84,10 +76,11 @@ fun TaskView(
HorizontalDivider(modifier = Modifier.padding(horizontal = 4.dp, vertical = 8.dp))
Text(
text = if (expanded) it else it.take(100) + "...",
text = it.take(100),
fontSize = 12.sp,
color = Color.White,
modifier = Modifier
.height(100.dp)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioLowBouncy,
@ -97,39 +90,7 @@ fun TaskView(
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
val (iconId, descriptionId) = task.statusData()
Image(
painter = painterResource(id = iconId),
modifier = Modifier.size(16.dp),
colorFilter = ColorFilter.tint(Color.White),
contentDescription = "status icon"
)
Spacer(modifier = Modifier.width(6.dp))
Text(text = stringResource(id = descriptionId), color = Color.White)
Spacer(modifier = Modifier.weight(1f))
if ((task.output?.length ?: 0) >= 100) {
Image(
painter = painterResource(
id = if (!expanded) R.drawable.ic_expand_more else R.drawable.ic_expand_less
),
contentDescription = "expand content icon",
colorFilter = ColorFilter.tint(Color.White)
)
}
Spacer(modifier = Modifier.width(8.dp))
}
TaskStatus(task, foregroundColor = Color.White)
if (showMoreActionsBottomSheet) {
val bottomSheetAction = listOf(
@ -147,27 +108,20 @@ fun TaskView(
dismiss = { showMoreActionsBottomSheet = false }
)
}
if (showTaskDetailBottomSheet) {
TaskDetailBottomSheet(task) {
showTaskDetailBottomSheet = false
}
}
}
}
@Suppress("MagicNumber")
@Preview
@Composable
private fun TaskViewPreview() {
val output =
"Lorem Ipsum is simply dummy text of the printing and " +
"typesetting industry. Lorem Ipsum has been the " +
"industry's standard dummy text ever since the 1500s, " +
"when an unknown printer took a galley of type and " +
"scrambled it to make a type specimen book. " +
"It has survived not only five centuries, but also " +
"the leap into electronic typesetting, remaining" +
" essentially unchanged. It wLorem Ipsum is simply dummy" +
" text of the printing and typesetting industry. " +
"Lorem Ipsum has been the industry's standard dummy " +
"text ever since the 1500s, when an unknown printer took a" +
" galley of type and scrambled it to make a type specimen book. " +
"It has survived not only five centuries, but also the leap " +
"into electronic typesetting, remaining essentially unchanged."
val output = "Lorem".getRandomString(100)
TaskView(
task = Task(

View file

@ -0,0 +1,165 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.assistant.taskDetail
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nextcloud.client.assistant.task.TaskStatus
import com.nextcloud.utils.extensions.getRandomString
import com.owncloud.android.R
import com.owncloud.android.lib.resources.assistant.model.Task
@Suppress("LongMethod")
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun TaskDetailBottomSheet(task: Task, dismiss: () -> Unit) {
var showInput by remember { mutableStateOf(true) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
modifier = Modifier.padding(top = 32.dp),
containerColor = Color.White,
onDismissRequest = {
dismiss()
},
sheetState = sheetState
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
stickyHeader {
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
) {
TextInputSelectButton(
Modifier.weight(1f),
R.string.assistant_task_detail_screen_input_button_title,
showInput,
onClick = {
showInput = true
}
)
TextInputSelectButton(
Modifier.weight(1f),
R.string.assistant_task_detail_screen_output_button_title,
!showInput,
onClick = {
showInput = false
}
)
}
}
item {
Spacer(modifier = Modifier.height(16.dp))
Column(
modifier = Modifier
.fillMaxSize()
.background(color = colorResource(id = R.color.light_grey), shape = RoundedCornerShape(8.dp))
.padding(16.dp)
) {
Text(
text = if (showInput) {
task.input ?: ""
} else {
task.output ?: ""
},
fontSize = 12.sp,
color = Color.Black,
modifier = Modifier
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioLowBouncy,
stiffness = Spring.StiffnessLow
)
)
)
}
TaskStatus(task, foregroundColor = Color.Black)
Spacer(modifier = Modifier.height(32.dp))
}
}
}
}
@Composable
private fun TextInputSelectButton(modifier: Modifier, titleId: Int, highlightCondition: Boolean, onClick: () -> Unit) {
Button(
onClick = onClick,
shape = RoundedCornerShape(8.dp),
colors = if (highlightCondition) {
ButtonDefaults.buttonColors(containerColor = Color.White)
} else {
ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.light_grey))
},
modifier = modifier
.widthIn(min = 0.dp, max = 200.dp)
.padding(horizontal = 4.dp)
) {
Text(text = stringResource(id = titleId), color = Color.Black)
}
}
@Suppress("MagicNumber")
@Preview
@Composable
private fun TaskDetailScreenPreview() {
TaskDetailBottomSheet(
task = Task(
1,
"Free Prompt",
0,
"1",
"1",
"Give me text".getRandomString(100),
"output".getRandomString(300),
"",
""
)
) {
}
}

View file

@ -5,7 +5,7 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.assistant.component
package com.nextcloud.client.assistant.taskTypes
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Row

View file

@ -58,7 +58,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 76, to = 77),
AutoMigration(from = 77, to = 78),
AutoMigration(from = 78, to = 79, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
AutoMigration(from = 79, to = 80)
AutoMigration(from = 79, to = 80),
AutoMigration(from = 80, to = 81)
],
exportSchema = true
)

View file

@ -45,6 +45,8 @@ data class SyncedFolderEntity(
val hidden: Int?,
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE)
val subFolderRule: Int?,
@ColumnInfo(name = ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN)
val excludeHidden: Int?
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN)
val excludeHidden: Int?,
@ColumnInfo(name = ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS)
val lastScanTimestampMs: Long?
)

View file

@ -264,7 +264,7 @@ internal class BackgroundJobManagerImpl(
.setConstraints(constrains)
.build()
workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.APPEND, request)
workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.REPLACE, request)
}
override fun schedulePeriodicContactsBackup(user: User) {

View file

@ -11,6 +11,7 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import com.nextcloud.client.device.PowerManagementService
import com.owncloud.android.datamodel.SyncedFolderProvider
import com.owncloud.android.lib.common.utils.Log_OC
/**
* This work is triggered when OS detects change in media folders.
@ -31,6 +32,7 @@ class ContentObserverWork(
backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class))
if (params.triggeredContentUris.size > 0) {
Log_OC.d(TAG, "File-sync Content Observer detected files change")
checkAndStartFileSyncJob()
backgroundJobManager.startMediaFoldersDetectionJob()
}

View file

@ -62,7 +62,6 @@ class FilesSyncWork(
companion object {
const val TAG = "FilesSyncJob"
const val SKIP_CUSTOM = "skipCustom"
const val OVERRIDE_POWER_SAVING = "overridePowerSaving"
const val CHANGED_FILES = "changedFiles"
const val FOREGROUND_SERVICE_ID = 414
@ -70,7 +69,7 @@ class FilesSyncWork(
@Suppress("MagicNumber")
private fun updateForegroundWorker(progressPercent: Int, useForegroundWorker: Boolean) {
if (useForegroundWorker) {
if (!useForegroundWorker) {
return
}
@ -95,6 +94,7 @@ class FilesSyncWork(
@Suppress("MagicNumber")
override fun doWork(): Result {
backgroundJobManager.logStartOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class))
Log_OC.d(TAG, "File-sync worker started")
val overridePowerSaving = inputData.getBoolean(OVERRIDE_POWER_SAVING, false)
// If we are in power save mode, better to postpone upload
@ -114,7 +114,9 @@ class FilesSyncWork(
// Get changed files from ContentObserverWork (only images and videos) or by scanning filesystem
val changedFiles = inputData.getStringArray(CHANGED_FILES)
Log_OC.d(TAG, "File-sync worker changed files from observer: " + changedFiles.contentToString())
collectChangedFiles(changedFiles)
Log_OC.d(TAG, "File-sync worker finished checking files.")
// Create all the providers we'll need
val filesystemDataProvider = FilesystemDataProvider(contentResolver)
@ -129,11 +131,7 @@ class FilesSyncWork(
(50 + (index.toDouble() / syncedFolders.size.toDouble()) * 50).toInt(),
changedFiles.isNullOrEmpty()
)
if (syncedFolder.isEnabled && (
changedFiles.isNullOrEmpty() ||
MediaFolderType.CUSTOM != syncedFolder.type
)
) {
if (syncedFolder.isEnabled) {
syncFolder(
context,
resources,
@ -145,6 +143,7 @@ class FilesSyncWork(
)
}
}
Log_OC.d(TAG, "File-sync worker finished")
val result = Result.success()
backgroundJobManager.logEndOfWorker(BackgroundJobManagerImpl.formatClassTag(this::class), result)
return result
@ -222,7 +221,6 @@ class FilesSyncWork(
needsWifi = syncedFolder.isWifiOnly
uploadAction = syncedFolder.uploadAction
}
FileUploadHelper.instance().uploadNewFiles(
user,
localPaths,

View file

@ -75,8 +75,10 @@ class FileUploadHelper {
) {
val failedUploads = uploadsStorageManager.failedUploads
if (failedUploads == null || failedUploads.isEmpty()) {
Log_OC.d(TAG, "Failed uploads are empty or null")
return
}
retryUploads(
uploadsStorageManager,
connectivityService,
@ -120,6 +122,7 @@ class FileUploadHelper {
val charging = batteryStatus.isCharging || batteryStatus.isFull
val isPowerSaving = powerManagementService.isPowerSavingEnabled
var uploadUser = Optional.empty<User>()
for (failedUpload in failedUploads) {
// 1. extract failed upload owner account and cache it between loops (expensive query)
if (!uploadUser.isPresent || !uploadUser.get().nameEquals(failedUpload.accountName)) {

View file

@ -165,16 +165,16 @@ class FileUploadWorker(
}
if (user.isPresent) {
val operation = createUploadFileOperation(upload, user.get())
val uploadFileOperation = createUploadFileOperation(upload, user.get())
currentUploadFileOperation = operation
val result = upload(operation, user.get())
currentUploadFileOperation = uploadFileOperation
val result = upload(uploadFileOperation, user.get())
currentUploadFileOperation = null
fileUploaderDelegate.sendBroadcastUploadFinished(
operation,
uploadFileOperation,
result,
operation.oldFile?.storagePath,
uploadFileOperation.oldFile?.storagePath,
context,
localBroadcastManager
)
@ -205,39 +205,39 @@ class FileUploadWorker(
}
@Suppress("TooGenericExceptionCaught", "DEPRECATION")
private fun upload(operation: UploadFileOperation, user: User): RemoteOperationResult<Any?> {
private fun upload(uploadFileOperation: UploadFileOperation, user: User): RemoteOperationResult<Any?> {
lateinit var result: RemoteOperationResult<Any?>
notificationManager.prepareForStart(
operation,
intents.startIntent(operation),
intents.notificationStartIntent(operation)
uploadFileOperation,
cancelPendingIntent = intents.startIntent(uploadFileOperation),
intents.notificationStartIntent(uploadFileOperation)
)
try {
val storageManager = operation.storageManager
val storageManager = uploadFileOperation.storageManager
val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context)
val uploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context)
result = operation.execute(uploadClient)
result = uploadFileOperation.execute(uploadClient)
val task = ThumbnailsCacheManager.ThumbnailGenerationTask(storageManager, user)
val file = File(operation.originalStoragePath)
val remoteId: String? = operation.file.remoteId
val file = File(uploadFileOperation.originalStoragePath)
val remoteId: String? = uploadFileOperation.file.remoteId
task.execute(ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, remoteId))
} catch (e: Exception) {
Log_OC.e(TAG, "Error uploading", e)
result = RemoteOperationResult<Any?>(e)
} finally {
cleanupUploadProcess(result, operation)
cleanupUploadProcess(result, uploadFileOperation)
}
return result
}
private fun cleanupUploadProcess(result: RemoteOperationResult<Any?>, operation: UploadFileOperation) {
private fun cleanupUploadProcess(result: RemoteOperationResult<Any?>, uploadFileOperation: UploadFileOperation) {
if (!isStopped || !result.isCancelled) {
uploadsStorageManager.updateDatabaseUploadResult(result, operation)
notifyUploadResult(operation, result)
uploadsStorageManager.updateDatabaseUploadResult(result, uploadFileOperation)
notifyUploadResult(uploadFileOperation, result)
notificationManager.dismissWorkerNotifications()
}
}
@ -315,10 +315,11 @@ class FileUploadWorker(
if (percent != lastPercent) {
notificationManager.run {
updateUploadProgress(fileAbsoluteName, percent, currentUploadFileOperation)
val accountName = currentUploadFileOperation?.user?.accountName
val remotePath = currentUploadFileOperation?.remotePath
val filename = currentUploadFileOperation?.fileName ?: ""
updateUploadProgress(filename, percent, currentUploadFileOperation)
if (accountName != null && remotePath != null) {
val key: String =
@ -329,7 +330,7 @@ class FileUploadWorker(
progressRate,
totalTransferredSoFar,
totalToTransfer,
fileAbsoluteName
filename
)
}

View file

@ -16,7 +16,6 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import com.owncloud.android.R
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.resources.files.FileUtils
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.ui.notifications.NotificationUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
@ -44,14 +43,18 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi
}
@Suppress("MagicNumber")
fun prepareForStart(upload: UploadFileOperation, pendingIntent: PendingIntent, startIntent: PendingIntent) {
fun prepareForStart(
uploadFileOperation: UploadFileOperation,
cancelPendingIntent: PendingIntent,
startIntent: PendingIntent
) {
notificationBuilder.run {
setContentTitle(context.getString(R.string.uploader_upload_in_progress_ticker))
setContentText(
String.format(
context.getString(R.string.uploader_upload_in_progress),
0,
upload.fileName
uploadFileOperation.fileName
)
)
setTicker(context.getString(R.string.foreground_service_upload))
@ -62,13 +65,13 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi
addAction(
R.drawable.ic_action_cancel_grey,
context.getString(R.string.common_cancel),
pendingIntent
cancelPendingIntent
)
setContentIntent(startIntent)
}
if (!upload.isInstantPicture && !upload.isInstantVideo) {
if (!uploadFileOperation.isInstantPicture && !uploadFileOperation.isInstantVideo) {
showNotification()
}
}
@ -138,11 +141,10 @@ class UploadNotificationManager(private val context: Context, viewThemeUtils: Vi
}
@Suppress("MagicNumber")
fun updateUploadProgress(filePath: String, percent: Int, currentOperation: UploadFileOperation?) {
fun updateUploadProgress(filename: String, percent: Int, currentOperation: UploadFileOperation?) {
notificationBuilder.run {
setProgress(100, percent, false)
val fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1)
val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, fileName)
val text = String.format(context.getString(R.string.uploader_upload_in_progress), percent, filename)
setContentText(text)
showNotification()

View file

@ -36,7 +36,7 @@ import java.lang.ref.WeakReference
class ComposeActivity : DrawerActivity() {
lateinit var binding: ActivityComposeBinding
private var menuItemId: Int? = null
private var menuItemId: Int = R.id.nav_all_files
companion object {
const val DESTINATION = "DESTINATION"
@ -51,13 +51,12 @@ class ComposeActivity : DrawerActivity() {
val destination = intent.getSerializableArgument(DESTINATION, ComposeDestination::class.java)
val titleId = intent.getIntExtra(TITLE, R.string.empty)
menuItemId = intent.getIntExtra(MENU_ITEM, -1)
menuItemId = intent.getIntExtra(MENU_ITEM, R.id.nav_all_files)
setupToolbar()
updateActionBarTitleAndHomeButtonByString(getString(titleId))
setupDrawer(menuItemId)
if (menuItemId != -1) {
setupDrawer(menuItemId!!)
setupToolbarShowOnlyMenuButtonAndTitle(getString(titleId)) {
openDrawer()
}
binding.composeView.setContent {
@ -72,15 +71,13 @@ class ComposeActivity : DrawerActivity() {
override fun onResume() {
super.onResume()
if (menuItemId != -1) {
setDrawerMenuItemChecked(R.id.nav_assistant)
}
setDrawerMenuItemChecked(menuItemId)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
if (isDrawerOpen) closeDrawer() else openDrawer()
toggleDrawer()
true
}
else -> super.onOptionsItemSelected(item)

View file

@ -0,0 +1,17 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.utils.extensions
fun String.getRandomString(length: Int): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
val result = (1..length)
.map { allowedChars.random() }
.joinToString("")
return this + result
}

View file

@ -124,7 +124,7 @@ import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFER
* Contains methods to build the "static" strings. These strings were before constants in different classes.
*/
public class MainApp extends MultiDexApplication implements HasAndroidInjector {
public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_23;
public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_26;
public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_16;
private static final String TAG = MainApp.class.getSimpleName();

View file

@ -56,7 +56,10 @@ import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimeType;
import com.owncloud.android.utils.MimeTypeUtil;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -71,6 +74,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import kotlin.Pair;
public class FileDataStorageManager {
private static final String TAG = FileDataStorageManager.class.getSimpleName();
@ -338,6 +342,36 @@ public class FileDataStorageManager {
return ocFile;
}
public static void clearTempEncryptedFolder(String accountName) {
File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName));
if (!tempEncryptedFolder.exists()) {
Log_OC.d(TAG,"tempEncryptedFolder not exists");
return;
}
try {
FileUtils.cleanDirectory(tempEncryptedFolder);
Log_OC.d(TAG,"tempEncryptedFolder cleared");
} catch (IOException exception) {
Log_OC.d(TAG,"Error caught at clearTempEncryptedFolder: " + exception);
}
}
public static File createTempEncryptedFolder(String accountName) {
File tempEncryptedFolder = new File(FileStorageUtils.getTemporalEncryptedFolderPath(accountName));
if (!tempEncryptedFolder.exists()) {
boolean isTempEncryptedFolderCreated = tempEncryptedFolder.mkdirs();
Log_OC.d(TAG, "tempEncryptedFolder created" + isTempEncryptedFolderCreated);
} else {
Log_OC.d(TAG, "tempEncryptedFolder already exists");
}
return tempEncryptedFolder;
}
public void saveNewFile(OCFile newFile) {
String remoteParentPath = new File(newFile.getRemotePath()).getParent();
remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ?
@ -1704,6 +1738,7 @@ public class FileDataStorageManager {
}
}
@SuppressFBWarnings("PSC")
public void saveConflict(OCFile ocFile, String etagInConflict) {
ContentValues cv = new ContentValues();
if (!ocFile.isDown()) {
@ -2297,6 +2332,59 @@ public class FileDataStorageManager {
}
}
public String getFolderName(String path) {
return "/" + path.split("/")[1] + "/";
}
public String retrieveRemotePathConsideringEncryption(OCFile file) {
if (file == null) {
throw new NullPointerException("file cannot be null");
}
String remotePath = file.getRemotePath();
if (file.isEncrypted()) {
remotePath = getEncryptedRemotePath(file.getRemotePath());
}
return remotePath;
}
public String getEncryptedRemotePath(String decryptedRemotePath) {
String folderName = getFolderName(decryptedRemotePath);
if (folderName == null) {
throw new NullPointerException("folderName cannot be null");
}
OCFile folder = getFileByDecryptedRemotePath(folderName);
List<OCFile> files = getAllFilesRecursivelyInsideFolder(folder);
List<Pair<String, String>> decryptedFileNamesAndEncryptedRemotePaths = getDecryptedFileNamesAndEncryptedRemotePaths(files);
String decryptedFileName = decryptedRemotePath.substring( decryptedRemotePath.lastIndexOf('/') + 1);
for (Pair<String, String> item : decryptedFileNamesAndEncryptedRemotePaths) {
if (item.getFirst().equals(decryptedFileName)) {
return item.getSecond();
}
}
return null;
}
@SuppressFBWarnings("OCP")
private List<Pair<String, String>> getDecryptedFileNamesAndEncryptedRemotePaths(List<OCFile> fileList) {
List<Pair<String, String>> result = new ArrayList<>();
for (OCFile file : fileList) {
if (file.isEncrypted()) {
Pair<String, String> fileNameAndEncryptedRemotePath = new Pair<>(file.getDecryptedFileName(), file.getRemotePath());
result.add(fileNameAndEncryptedRemotePath);
}
}
return result;
}
public void removeLocalFiles(User user, FileDataStorageManager storageManager) {
File tempDir = new File(FileStorageUtils.getTemporalPath(user.getAccountName()));
File saveDir = new File(FileStorageUtils.getSavePath(user.getAccountName()));

View file

@ -107,6 +107,7 @@ public class FilesystemDataProvider {
public void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean isFolder, SyncedFolder syncedFolder) {
// takes multiple milliseconds to query data from database (around 75% of execution time) (6ms)
FileSystemDataSet data = getFilesystemDataSet(localPath, syncedFolder);
int isFolderValue = 0;
@ -145,7 +146,7 @@ public class FilesystemDataProvider {
}
}
// updating data takes multiple milliseconds (around 25% of exec time) (2 ms)
int result = contentResolver.update(
ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM,
cv,

View file

@ -21,6 +21,7 @@ import java.io.Serializable;
public class SyncedFolder implements Serializable, Cloneable {
public static final long UNPERSISTED_ID = Long.MIN_VALUE;
public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1;
public static final long NOT_SCANNED_YET = -1;
private static final long serialVersionUID = -793476118299906429L;
@ -41,6 +42,7 @@ public class SyncedFolder implements Serializable, Cloneable {
private boolean hidden;
private SubFolderRule subfolderRule;
private boolean excludeHidden;
private long lastScanTimestampMs;
/**
* constructor for new, to be persisted entity.
@ -75,7 +77,8 @@ public class SyncedFolder implements Serializable, Cloneable {
MediaFolderType type,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden) {
boolean excludeHidden,
long lastScanTimestampMs) {
this(UNPERSISTED_ID,
localPath,
remotePath,
@ -91,7 +94,8 @@ public class SyncedFolder implements Serializable, Cloneable {
type,
hidden,
subFolderRule,
excludeHidden);
excludeHidden,
lastScanTimestampMs);
}
/**
@ -114,7 +118,8 @@ public class SyncedFolder implements Serializable, Cloneable {
MediaFolderType type,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden) {
boolean excludeHidden,
long lastScanTimestampMs) {
this.id = id;
this.localPath = localPath;
this.remotePath = remotePath;
@ -130,6 +135,7 @@ public class SyncedFolder implements Serializable, Cloneable {
this.hidden = hidden;
this.subfolderRule = subFolderRule;
this.excludeHidden = excludeHidden;
this.lastScanTimestampMs = lastScanTimestampMs;
}
/**
@ -271,4 +277,8 @@ public class SyncedFolder implements Serializable, Cloneable {
public boolean containsFile(String filePath){
return filePath.contains(localPath);
}
public long getLastScanTimestampMs() { return lastScanTimestampMs; }
public void setLastScanTimestampMs(long lastScanTimestampMs) { this.lastScanTimestampMs = lastScanTimestampMs; }
}

View file

@ -60,7 +60,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
MediaFolderType type,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden) {
boolean excludeHidden,
long lastScanTimestampMs) {
super(id,
localPath,
remotePath,
@ -76,7 +77,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
type,
hidden,
subFolderRule,
excludeHidden);
excludeHidden,
lastScanTimestampMs);
this.filePaths = filePaths;
this.folderName = folderName;
this.numberOfFiles = numberOfFiles;
@ -98,7 +100,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
MediaFolderType type,
boolean hidden,
SubFolderRule subFolderRule,
boolean excludeHidden) {
boolean excludeHidden,
long lastScanTimestampMs) {
super(id,
localPath,
remotePath,
@ -114,7 +117,8 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
type,
hidden,
subFolderRule,
excludeHidden);
excludeHidden,
lastScanTimestampMs);
this.folderName = folderName;
}

View file

@ -360,7 +360,9 @@ public class SyncedFolderProvider extends Observable {
SubFolderRule subFolderRule = SubFolderRule.values()[cursor.getInt(
cursor.getColumnIndexOrThrow(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE))];
boolean excludeHidden = cursor.getInt(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN)) == 1;
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN)) == 1;
long lastScanTimestampMs = cursor.getLong(cursor.getColumnIndexOrThrow(
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS));
syncedFolder = new SyncedFolder(id,
@ -378,7 +380,8 @@ public class SyncedFolderProvider extends Observable {
type,
hidden,
subFolderRule,
excludeHidden);
excludeHidden,
lastScanTimestampMs);
}
return syncedFolder;
}
@ -407,8 +410,8 @@ public class SyncedFolderProvider extends Observable {
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().id);
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden());
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_RULE, syncedFolder.getSubfolderRule().ordinal());
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_EXCLUDE_HIDDEN, syncedFolder.isExcludeHidden());
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXCLUDE_HIDDEN, syncedFolder.isExcludeHidden());
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS, syncedFolder.getLastScanTimestampMs());
return cv;
}

View file

@ -25,7 +25,7 @@ import java.util.List;
*/
public class ProviderMeta {
public static final String DB_NAME = "filelist";
public static final int DB_VERSION = 80;
public static final int DB_VERSION = 81;
private ProviderMeta() {
// No instance
@ -293,7 +293,8 @@ public class ProviderMeta {
public static final String SYNCED_FOLDER_NAME_COLLISION_POLICY = "name_collision_policy";
public static final String SYNCED_FOLDER_HIDDEN = "hidden";
public static final String SYNCED_FOLDER_SUBFOLDER_RULE = "sub_folder_rule";
public static final String SYNCED_EXCLUDE_HIDDEN = "exclude_hidden";
public static final String SYNCED_FOLDER_EXCLUDE_HIDDEN = "exclude_hidden";
public static final String SYNCED_FOLDER_LAST_SCAN_TIMESTAMP_MS = "last_scan_timestamp_ms";
// Columns of external links table
public static final String EXTERNAL_LINKS_ICON_URL = "icon_url";

View file

@ -58,15 +58,14 @@ import java.util.Vector;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
/**
* Remote operation performing the synchronization of the list of files contained in a folder identified with its
* remote path.
* Fetches the list and properties of the files contained in the given folder, including their properties, and updates
* the local database with them.
* Does NOT enter in the child folders to synchronize their contents also.
* Remote operation performing the synchronization of the list of files contained in a folder identified with its remote
* path. Fetches the list and properties of the files contained in the given folder, including their properties, and
* updates the local database with them. Does NOT enter in the child folders to synchronize their contents also.
*/
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public class RefreshFolderOperation extends RemoteOperation {
@ -74,37 +73,53 @@ public class RefreshFolderOperation extends RemoteOperation {
private static final String TAG = RefreshFolderOperation.class.getSimpleName();
public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED =
RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED =
RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
/** Time stamp for the synchronization process in progress */
/**
* Time stamp for the synchronization process in progress
*/
private long mCurrentSyncTime;
/** Remote folder to synchronize */
/**
* Remote folder to synchronize
*/
private OCFile mLocalFolder;
/** Access to the local database */
/**
* Access to the local database
*/
private FileDataStorageManager mStorageManager;
/** Account where the file to synchronize belongs */
/**
* Account where the file to synchronize belongs
*/
private User user;
/** Android context; necessary to send requests to the download service */
/**
* Android context; necessary to send requests to the download service
*/
private Context mContext;
/** Files and folders contained in the synchronized folder after a successful operation */
/**
* Files and folders contained in the synchronized folder after a successful operation
*/
private List<OCFile> mChildren;
/** Counter of conflicts found between local and remote files */
/**
* Counter of conflicts found between local and remote files
*/
private int mConflictsFound;
/** Counter of failed operations in synchronization of kept-in-sync files */
/**
* Counter of failed operations in synchronization of kept-in-sync files
*/
private int mFailsInKeptInSyncFound;
/**
* Map of remote and local paths to files that where locally stored in a location
* out of the ownCloud folder and couldn't be copied automatically into it
* Map of remote and local paths to files that where locally stored in a location out of the ownCloud folder and
* couldn't be copied automatically into it
**/
private Map<String, String> mForgottenLocalFiles;
@ -113,10 +128,14 @@ public class RefreshFolderOperation extends RemoteOperation {
*/
private boolean mSyncFullAccount;
/** 'True' means that the remote folder changed and should be fetched */
/**
* 'True' means that the remote folder changed and should be fetched
*/
private boolean mRemoteFolderChanged;
/** 'True' means that Etag will be ignored */
/**
* 'True' means that Etag will be ignored
*/
private boolean mIgnoreETag;
/**
@ -131,16 +150,14 @@ public class RefreshFolderOperation extends RemoteOperation {
/**
* Creates a new instance of {@link RefreshFolderOperation}.
*
* @param folder Folder to synchronize.
* @param currentSyncTime Time stamp for the synchronization process in progress.
* @param syncFullAccount 'True' means that this operation is part of a full account
* synchronization.
* @param ignoreETag 'True' means that the content of the remote folder should
* be fetched and updated even though the 'eTag' did not
* change.
* @param dataStorageManager Interface with the local database.
* @param user ownCloud account where the folder is located.
* @param context Application context.
* @param folder Folder to synchronize.
* @param currentSyncTime Time stamp for the synchronization process in progress.
* @param syncFullAccount 'True' means that this operation is part of a full account synchronization.
* @param ignoreETag 'True' means that the content of the remote folder should be fetched and updated even
* though the 'eTag' did not change.
* @param dataStorageManager Interface with the local database.
* @param user ownCloud account where the folder is located.
* @param context Application context.
*/
public RefreshFolderOperation(OCFile folder,
long currentSyncTime,
@ -196,8 +213,8 @@ public class RefreshFolderOperation extends RemoteOperation {
}
/**
* Returns the list of files and folders contained in the synchronized folder,
* if called after synchronization is complete.
* Returns the list of files and folders contained in the synchronized folder, if called after synchronization is
* complete.
*
* @return List of files and folders contained in the synchronized folder.
*/
@ -207,7 +224,7 @@ public class RefreshFolderOperation extends RemoteOperation {
/**
* Performs the synchronization.
*
* <p>
* {@inheritDoc}
*/
@Override
@ -246,7 +263,7 @@ public class RefreshFolderOperation extends RemoteOperation {
if (!mSyncFullAccount && mRemoteFolderChanged) {
sendLocalBroadcast(
EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result
);
);
}
if (result.isSuccess() && !mSyncFullAccount && !mOnlyFileMetadata) {
@ -256,7 +273,7 @@ public class RefreshFolderOperation extends RemoteOperation {
if (!mSyncFullAccount) {
sendLocalBroadcast(
EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result
);
);
}
return result;
@ -371,7 +388,7 @@ public class RefreshFolderOperation extends RemoteOperation {
result = new RemoteOperationResult(ResultCode.OK);
Log_OC.i(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
(mRemoteFolderChanged ? "changed" : "not changed"));
(mRemoteFolderChanged ? "changed" : "not changed"));
} else {
// check failed
@ -380,10 +397,10 @@ public class RefreshFolderOperation extends RemoteOperation {
}
if (result.isException()) {
Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
result.getLogMessage(), result.getException());
result.getLogMessage(), result.getException());
} else {
Log_OC.e(TAG, "Checked " + user.getAccountName() + remotePath + " : " +
result.getLogMessage());
result.getLogMessage());
}
}
@ -425,9 +442,9 @@ public class RefreshFolderOperation extends RemoteOperation {
/**
* Synchronizes the data retrieved from the server about the contents of the target folder
* with the current data in the local database.
*
* Synchronizes the data retrieved from the server about the contents of the target folder with the current data in
* the local database.
* <p>
* Grants that mChildren is updated with fresh data after execution.
*
* @param folderAndFiles Remote folder and children files in Folder
@ -553,6 +570,7 @@ public class RefreshFolderOperation extends RemoteOperation {
updatedFiles.add(updatedFile);
}
// save updated contents in local database
// update file name for encrypted files
if (e2EVersion == E2EVersion.V1_2) {
@ -584,6 +602,37 @@ public class RefreshFolderOperation extends RemoteOperation {
return metadata;
}
@SuppressFBWarnings("CE")
private static void setMimeTypeAndDecryptedRemotePath(OCFile updatedFile, FileDataStorageManager storageManager, String decryptedFileName, String mimetype) {
OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
if (parentFile == null) {
throw new NullPointerException("parentFile cannot be null");
}
String decryptedRemotePath;
if (decryptedFileName != null) {
decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
} else {
decryptedRemotePath = parentFile.getRemotePath() + updatedFile.getFileName();
}
if (updatedFile.isFolder()) {
decryptedRemotePath += "/";
}
updatedFile.setDecryptedRemotePath(decryptedRemotePath);
if (mimetype == null || mimetype.isEmpty()) {
if (updatedFile.isFolder()) {
updatedFile.setMimeType(MimeType.DIRECTORY);
} else {
updatedFile.setMimeType("application/octet-stream");
}
} else {
updatedFile.setMimeType(mimetype);
}
}
public static void updateFileNameForEncryptedFileV1(FileDataStorageManager storageManager,
@NonNull DecryptedFolderMetadataFileV1 metadata,
OCFile updatedFile) {
@ -597,28 +646,16 @@ public class RefreshFolderOperation extends RemoteOperation {
} else {
com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile decryptedFile =
metadata.getFiles().get(updatedFile.getFileName());
if (decryptedFile == null) {
throw new NullPointerException("decryptedFile cannot be null");
}
decryptedFileName = decryptedFile.getEncrypted().getFilename();
mimetype = decryptedFile.getEncrypted().getMimetype();
}
OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
if (updatedFile.isFolder()) {
decryptedRemotePath += "/";
}
updatedFile.setDecryptedRemotePath(decryptedRemotePath);
if (mimetype == null || mimetype.isEmpty()) {
if (updatedFile.isFolder()) {
updatedFile.setMimeType(MimeType.DIRECTORY);
} else {
updatedFile.setMimeType("application/octet-stream");
}
} else {
updatedFile.setMimeType(mimetype);
}
setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype);
} catch (NullPointerException e) {
Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!");
}
@ -636,28 +673,16 @@ public class RefreshFolderOperation extends RemoteOperation {
mimetype = MimeType.DIRECTORY;
} else {
DecryptedFile decryptedFile = metadata.getMetadata().getFiles().get(updatedFile.getFileName());
if (decryptedFile == null) {
throw new NullPointerException("decryptedFile cannot be null");
}
decryptedFileName = decryptedFile.getFilename();
mimetype = decryptedFile.getMimetype();
}
OCFile parentFile = storageManager.getFileById(updatedFile.getParentId());
String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName;
if (updatedFile.isFolder()) {
decryptedRemotePath += "/";
}
updatedFile.setDecryptedRemotePath(decryptedRemotePath);
if (mimetype == null || mimetype.isEmpty()) {
if (updatedFile.isFolder()) {
updatedFile.setMimeType(MimeType.DIRECTORY);
} else {
updatedFile.setMimeType("application/octet-stream");
}
} else {
updatedFile.setMimeType(mimetype);
}
setMimeTypeAndDecryptedRemotePath(updatedFile, storageManager, decryptedFileName, mimetype);
} catch (NullPointerException e) {
Log_OC.e(TAG, "DecryptedMetadata for file " + updatedFile.getFileId() + " not found!");
}
@ -668,8 +693,8 @@ public class RefreshFolderOperation extends RemoteOperation {
updatedFile.setFileId(localFile.getFileId());
updatedFile.setLastSyncDateForData(localFile.getLastSyncDateForData());
updatedFile.setModificationTimestampAtLastSyncForData(
localFile.getModificationTimestampAtLastSyncForData()
);
localFile.getModificationTimestampAtLastSyncForData()
);
if (localFile.isEncrypted()) {
if (mLocalFolder.getStoragePath() == null) {
updatedFile.setStoragePath(FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mLocalFolder) +
@ -685,7 +710,7 @@ public class RefreshFolderOperation extends RemoteOperation {
// eTag will not be updated unless file CONTENTS are synchronized
if (!updatedFile.isFolder() && localFile.isDown() &&
!updatedFile.getEtag().equals(localFile.getEtag())) {
!updatedFile.getEtag().equals(localFile.getEtag())) {
updatedFile.setEtagInConflict(updatedFile.getEtag());
}
@ -695,8 +720,8 @@ public class RefreshFolderOperation extends RemoteOperation {
updatedFile.setFileLength(remoteFile.getFileLength());
updatedFile.setMountType(remoteFile.getMountType());
} else if (remoteFolderChanged && MimeTypeUtil.isImage(remoteFile) &&
remoteFile.getModificationTimestamp() !=
localFile.getModificationTimestamp()) {
remoteFile.getModificationTimestamp() !=
localFile.getModificationTimestamp()) {
updatedFile.setUpdateThumbnailNeeded(true);
Log_OC.d(TAG, "Image " + remoteFile.getFileName() + " updated on the server");
}
@ -713,6 +738,7 @@ public class RefreshFolderOperation extends RemoteOperation {
}
@NonNull
@SuppressFBWarnings("OCP")
public static Map<String, OCFile> prefillLocalFilesMap(Object metadata, List<OCFile> localFiles) {
Map<String, OCFile> localFilesMap = Maps.newHashMapWithExpectedSize(localFiles.size());
@ -731,13 +757,12 @@ public class RefreshFolderOperation extends RemoteOperation {
}
/**
* Performs a list of synchronization operations, determining if a download or upload is needed
* or if exists conflict due to changes both in local and remote contents of the each file.
* Performs a list of synchronization operations, determining if a download or upload is needed or if exists
* conflict due to changes both in local and remote contents of the each file.
* <p>
* If download or upload is needed, request the operation to the corresponding service and goes on.
*
* If download or upload is needed, request the operation to the corresponding service and goes
* on.
*
* @param filesToSyncContents Synchronization operations to execute.
* @param filesToSyncContents Synchronization operations to execute.
*/
private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents) {
RemoteOperationResult contentsResult;
@ -750,10 +775,10 @@ public class RefreshFolderOperation extends RemoteOperation {
mFailsInKeptInSyncFound++;
if (contentsResult.getException() != null) {
Log_OC.e(TAG, "Error while synchronizing favourites : "
+ contentsResult.getLogMessage(), contentsResult.getException());
+ contentsResult.getLogMessage(), contentsResult.getException());
} else {
Log_OC.e(TAG, "Error while synchronizing favourites : "
+ contentsResult.getLogMessage());
+ contentsResult.getLogMessage());
}
}
} // won't let these fails break the synchronization process
@ -763,9 +788,9 @@ public class RefreshFolderOperation extends RemoteOperation {
/**
* Syncs the Share resources for the files contained in the folder refreshed (children, not deeper descendants).
*
* @param client Handler of a session with an OC server.
* @return The result of the remote operation retrieving the Share resources in the folder refreshed by
* the operation.
* @param client Handler of a session with an OC server.
* @return The result of the remote operation retrieving the Share resources in the folder refreshed by the
* operation.
*/
private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
RemoteOperationResult result;
@ -793,12 +818,10 @@ public class RefreshFolderOperation extends RemoteOperation {
}
/**
* Sends a message to any application component interested in the progress
* of the synchronization.
* Sends a message to any application component interested in the progress of the synchronization.
*
* @param event broadcast event (Intent Action)
* @param dirRemotePath Remote path of a folder that was just synchronized
* (with or without success)
* @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
* @param result remote operation result
*/
private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) {

View file

@ -442,6 +442,7 @@ public class UploadFileOperation extends SyncOperation {
File temporalFile = null;
File originalFile = new File(mOriginalStoragePath);
File expectedFile = null;
File encryptedTempFile = null;
FileLock fileLock = null;
long size;
@ -552,7 +553,7 @@ public class UploadFileOperation extends SyncOperation {
byte[] iv = EncryptionUtils.randomBytes(EncryptionUtils.ivLength);
Cipher cipher = EncryptionUtils.getCipher(Cipher.ENCRYPT_MODE, key, iv);
File file = new File(mFile.getStoragePath());
EncryptedFile encryptedFile = EncryptionUtils.encryptFile(file, cipher);
EncryptedFile encryptedFile = EncryptionUtils.encryptFile(user.getAccountName(), file, cipher);
// new random file name, check if it exists in metadata
String encryptedFileName = EncryptionUtils.generateUid();
@ -567,9 +568,7 @@ public class UploadFileOperation extends SyncOperation {
}
}
File encryptedTempFile = encryptedFile.getEncryptedFile();
/***** E2E *****/
encryptedTempFile = encryptedFile.getEncryptedFile();
FileChannel channel = null;
try {
@ -712,8 +711,6 @@ public class UploadFileOperation extends SyncOperation {
user,
getStorageManager());
}
encryptedTempFile.delete();
}
} catch (FileNotFoundException e) {
Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore");
@ -755,6 +752,13 @@ public class UploadFileOperation extends SyncOperation {
if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) {
result = unlockFolderResult;
}
if (encryptedTempFile != null) {
boolean isTempEncryptedFileDeleted = encryptedTempFile.delete();
Log_OC.e(TAG, "isTempEncryptedFileDeleted: " + isTempEncryptedFileDeleted);
} else {
Log_OC.e(TAG, "Encrypted temp file cannot be found");
}
}
if (result.isSuccess()) {

View file

@ -21,6 +21,7 @@ import com.nextcloud.client.jobs.upload.UploadNotificationManager
import com.nextcloud.model.HTTPStatusCodes
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.R
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.datamodel.UploadsStorageManager
import com.owncloud.android.db.OCUpload
@ -42,6 +43,10 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
@Inject
var uploadsStorageManager: UploadsStorageManager? = null
@JvmField
@Inject
var fileStorageManager: FileDataStorageManager? = null
private var conflictUploadId: Long = 0
private var existingFile: OCFile? = null
private var newFile: OCFile? = null
@ -159,8 +164,8 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
return
}
if (existingFile == null) {
// fetch info of existing file from server
val operation = ReadFileRemoteOperation(newFile!!.remotePath)
val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(newFile) ?: return
val operation = ReadFileRemoteOperation(remotePath)
@Suppress("TooGenericExceptionCaught")
Thread {
@ -169,7 +174,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
if (result.isSuccess) {
existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile)
existingFile?.lastSyncDateForProperties = System.currentTimeMillis()
startDialog()
startDialog(remotePath)
} else {
Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode)
showErrorAndFinish(result.httpCode)
@ -180,11 +185,12 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
}
}.start()
} else {
startDialog()
val remotePath = fileStorageManager?.retrieveRemotePathConsideringEncryption(existingFile) ?: return
startDialog(remotePath)
}
}
private fun startDialog() {
private fun startDialog(remotePath: String) {
val userOptional = user
if (!userOptional.isPresent) {
Log_OC.e(TAG, "User not present")
@ -197,7 +203,7 @@ class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener
if (prev != null) {
fragmentTransaction.remove(prev)
}
if (existingFile != null && storageManager.fileExists(newFile?.remotePath)) {
if (existingFile != null && storageManager.fileExists(remotePath)) {
val dialog = ConflictsResolveDialog.newInstance(
existingFile,
newFile,

View file

@ -286,10 +286,10 @@ public abstract class DrawerActivity extends ToolbarActivity
public void updateHeader() {
int primaryColor = themeColorUtils.unchangedPrimaryColor(getAccount(), this);
boolean isClientBranded = getResources().getBoolean(R.bool.is_branded_client);
if (getAccount() != null &&
getCapabilities().getServerBackground() != null &&
!getResources().getBoolean(R.bool.is_branded_client)) {
getCapabilities().getServerBackground() != null && !isClientBranded) {
OCCapability capability = getCapabilities();
String logo = capability.getServerLogo();
@ -339,47 +339,57 @@ public abstract class DrawerActivity extends ToolbarActivity
}
// hide ecosystem apps according to user preference or in branded client
LinearLayout ecosystemApps = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps);
if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) {
ecosystemApps.setVisibility(View.GONE);
LinearLayout banner = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps);
boolean shouldHideTopBanner = isClientBranded || !preferences.isShowEcosystemApps();
if (shouldHideTopBanner) {
hideTopBanner(banner);
} else {
LinearLayout notesView = ecosystemApps.findViewById(R.id.drawer_ecosystem_notes);
LinearLayout talkView = ecosystemApps.findViewById(R.id.drawer_ecosystem_talk);
LinearLayout moreView = ecosystemApps.findViewById(R.id.drawer_ecosystem_more);
LinearLayout assistantView = ecosystemApps.findViewById(R.id.drawer_ecosystem_assistant);
notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes"));
talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2"));
moreView.setOnClickListener(v -> openAppStore("Nextcloud", true));
assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1));
if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) {
assistantView.setVisibility(View.VISIBLE);
} else {
assistantView.setVisibility(View.GONE);
}
List<LinearLayout> views = Arrays.asList(notesView, talkView, moreView, assistantView);
int iconColor;
if (Hct.fromInt(primaryColor).getTone() < 80.0) {
iconColor = Color.WHITE;
} else {
iconColor = getColor(R.color.grey_800_transparent);
}
for (LinearLayout view : views) {
ImageView imageView = (ImageView) view.getChildAt(0);
imageView.setImageTintList(ColorStateList.valueOf(iconColor));
GradientDrawable background = (GradientDrawable) imageView.getBackground();
background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor);
TextView textView = (TextView) view.getChildAt(1);
textView.setTextColor(iconColor);
}
ecosystemApps.setVisibility(View.VISIBLE);
showTopBanner(banner, primaryColor);
}
}
private void hideTopBanner(LinearLayout banner) {
banner.setVisibility(View.GONE);
}
private void showTopBanner(LinearLayout banner, int primaryColor) {
LinearLayout notesView = banner.findViewById(R.id.drawer_ecosystem_notes);
LinearLayout talkView = banner.findViewById(R.id.drawer_ecosystem_talk);
LinearLayout moreView = banner.findViewById(R.id.drawer_ecosystem_more);
LinearLayout assistantView = banner.findViewById(R.id.drawer_ecosystem_assistant);
notesView.setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes"));
talkView.setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2"));
moreView.setOnClickListener(v -> openAppStore("Nextcloud", true));
assistantView.setOnClickListener(v -> startComposeActivity(ComposeDestination.AssistantScreen, R.string.assistant_screen_top_bar_title, -1));
if (getCapabilities() != null && getCapabilities().getAssistant().isTrue()) {
assistantView.setVisibility(View.VISIBLE);
} else {
assistantView.setVisibility(View.GONE);
}
List<LinearLayout> views = Arrays.asList(notesView, talkView, moreView, assistantView);
int iconColor;
if (Hct.fromInt(primaryColor).getTone() < 80.0) {
iconColor = Color.WHITE;
} else {
iconColor = getColor(R.color.grey_800_transparent);
}
for (LinearLayout view : views) {
ImageView imageView = (ImageView) view.getChildAt(0);
imageView.setImageTintList(ColorStateList.valueOf(iconColor));
GradientDrawable background = (GradientDrawable) imageView.getBackground();
background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor);
TextView textView = (TextView) view.getChildAt(1);
textView.setTextColor(iconColor);
}
banner.setVisibility(View.VISIBLE);
}
/**
* Open specified app and, if not installed redirect to corresponding download.
*
@ -463,9 +473,7 @@ public abstract class DrawerActivity extends ToolbarActivity
DrawerMenuUtil.filterGroupfoldersMenuItem(menu, capability);
DrawerMenuUtil.filterAssistantMenuItem(menu, capability, getResources());
DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community,
!getResources().getBoolean(R.bool.participate_enabled));
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled));
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled));
DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout));
}
@ -494,10 +502,17 @@ public abstract class DrawerActivity extends ToolbarActivity
MainApp.showOnlyPersonalFiles(itemId == R.id.nav_personal_files);
Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (this instanceof ComposeActivity) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
intent.setAction(FileDisplayActivity.ALL_FILES);
intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
startActivity(intent);
}
closeDrawer();
} else if (itemId == R.id.nav_favorites) {
handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH),
menuItem.getItemId());
@ -520,7 +535,8 @@ public abstract class DrawerActivity extends ToolbarActivity
startActivity(CommunityActivity.class);
} else if (itemId == R.id.nav_logout) {
mCheckedMenuItem = -1;
menuItem.setChecked(false);
MenuItem isNewMenuItemChecked = menuItem.setChecked(false);
Log_OC.d(TAG,"onNavigationItemClicked nav_logout setChecked " + isNewMenuItemChecked);
final Optional<User> optionalUser = getUser();
if (optionalUser.isPresent()) {
UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager());
@ -627,6 +643,11 @@ public abstract class DrawerActivity extends ToolbarActivity
private void launchActivityForSearch(SearchEvent searchEvent, int menuItemId) {
Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (this instanceof ComposeActivity) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
intent.setAction(Intent.ACTION_SEARCH);
intent.putExtra(OCFileListFragment.SEARCH_EVENT, searchEvent);
intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
@ -673,6 +694,14 @@ public abstract class DrawerActivity extends ToolbarActivity
return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
}
public void toggleDrawer() {
if (isDrawerOpen()) {
closeDrawer();
} else {
openDrawer();
}
}
/**
* closes the drawer.
*/
@ -1115,6 +1144,11 @@ public abstract class DrawerActivity extends ToolbarActivity
MainApp.showOnlyPersonalFiles(onlyPersonalFiles);
Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class);
fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (this instanceof ComposeActivity) {
fileDisplayActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
fileDisplayActivity.setAction(FileDisplayActivity.ALL_FILES);
startActivity(fileDisplayActivity);
}
@ -1122,7 +1156,8 @@ public abstract class DrawerActivity extends ToolbarActivity
@Override
public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
if (callContext instanceof MenuItem menuItem) {
menuItem.setIcon(avatarDrawable);
MenuItem newIcon = menuItem.setIcon(avatarDrawable);
Log_OC.d(TAG,"avatarGenerated new icon: " + newIcon);
} else if (callContext instanceof ImageView imageView) {
imageView.setImageDrawable(avatarDrawable);
} else if (callContext instanceof MaterialButton materialButton) {

View file

@ -54,7 +54,6 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper;
import com.nextcloud.client.jobs.download.FileDownloadWorker;
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.jobs.upload.FileUploadWorker;
import com.nextcloud.client.jobs.upload.UploadNotificationManager;
import com.nextcloud.client.media.PlayerServiceConnection;
import com.nextcloud.client.network.ClientFactory;
import com.nextcloud.client.network.ConnectivityService;
@ -878,18 +877,24 @@ public class FileDisplayActivity extends FileActivity
requestUploadOfFilesFromFileSystem(basePath, filePaths, resultCode);
}
private String[] getRemotePaths(String directory, String[] filePaths, String localBasePath) {
String[] remotePaths = new String[filePaths.length];
for (int j = 0; j < remotePaths.length; j++) {
String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath);
remotePaths[j] = directory + relativePath;
}
return remotePaths;
}
private void requestUploadOfFilesFromFileSystem(String localBasePath, String[] filePaths, int resultCode) {
if (localBasePath != null && filePaths != null) {
if (!localBasePath.endsWith("/")) {
localBasePath = localBasePath + "/";
}
String[] remotePaths = new String[filePaths.length];
String remotePathBase = getCurrentDir().getRemotePath();
for (int j = 0; j < remotePaths.length; j++) {
String relativePath = StringUtils.removePrefix(filePaths[j], localBasePath);
remotePaths[j] = remotePathBase + relativePath;
}
String[] decryptedRemotePaths = getRemotePaths(remotePathBase, filePaths, localBasePath);
int behaviour = switch (resultCode) {
case UploadFilesActivity.RESULT_OK_AND_MOVE -> FileUploadWorker.LOCAL_BEHAVIOUR_MOVE;
@ -899,7 +904,7 @@ public class FileDisplayActivity extends FileActivity
FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new),
filePaths,
remotePaths,
decryptedRemotePaths,
behaviour,
true,
UploadFileOperation.CREATED_BY_USER,

View file

@ -387,7 +387,8 @@ class SyncedFoldersActivity :
syncedFolder.type,
syncedFolder.isHidden,
syncedFolder.subfolderRule,
syncedFolder.isExcludeHidden
syncedFolder.isExcludeHidden,
syncedFolder.lastScanTimestampMs
)
}
@ -418,7 +419,8 @@ class SyncedFoldersActivity :
mediaFolder.type,
syncedFolder.isHidden,
syncedFolder.subfolderRule,
syncedFolder.isExcludeHidden
syncedFolder.isExcludeHidden,
syncedFolder.lastScanTimestampMs
)
}
@ -448,7 +450,8 @@ class SyncedFoldersActivity :
mediaFolder.type,
false,
SubFolderRule.YEAR_MONTH,
false
false,
SyncedFolder.NOT_SCANNED_YET
)
}
@ -541,7 +544,8 @@ class SyncedFoldersActivity :
MediaFolderType.CUSTOM,
false,
SubFolderRule.YEAR_MONTH,
false
false,
SyncedFolder.NOT_SCANNED_YET
)
onSyncFolderSettingsClick(0, emptyCustomFolder)
} else {
@ -658,7 +662,8 @@ class SyncedFoldersActivity :
syncedFolder.type,
syncedFolder.isHidden,
syncedFolder.subFolderRule,
syncedFolder.isExcludeHidden
syncedFolder.isExcludeHidden,
SyncedFolder.NOT_SCANNED_YET
)
saveOrUpdateSyncedFolder(newCustomFolder)
adapter.addSyncFolderItem(newCustomFolder)

View file

@ -104,6 +104,22 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton);
}
public void setupToolbarShowOnlyMenuButtonAndTitle(String title, View.OnClickListener toggleDrawer) {
setupToolbar(false, false);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
LinearLayout toolbar = findViewById(R.id.toolbar_linear_layout);
MaterialButton menuButton = findViewById(R.id.toolbar_menu_button);
MaterialTextView titleTextView = findViewById(R.id.toolbar_title);
titleTextView.setText(title);
toolbar.setVisibility(View.VISIBLE);
menuButton.setOnClickListener(toggleDrawer);
}
public void setupToolbar() {
setupToolbar(false, false);
}
@ -278,7 +294,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable
public void clearToolbarSubtitle() {
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
if (actionBar != null) {
actionBar.setSubtitle(null);
}
}

View file

@ -141,9 +141,11 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
if (itemId == R.id.action_upload_list_failed_clear) {
uploadsStorageManager.clearFailedButNotDelayedUploads();
clearTempEncryptedFolder();
loadUploadItemsFromDb();
} else {
} else if (itemId == R.id.action_upload_list_failed_retry) {
// FIXME For e2e resume is not working
new Thread(() -> {
FileUploadHelper.Companion.instance().retryFailedUploads(
uploadsStorageManager,
@ -152,10 +154,11 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
powerManagementService);
parentActivity.runOnUiThread(this::loadUploadItemsFromDb);
}).start();
}
return true;
});
failedPopup.show();
}
@ -169,6 +172,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
if (itemId == R.id.action_upload_list_cancelled_clear) {
uploadsStorageManager.clearCancelledUploadsForCurrentAccount();
loadUploadItemsFromDb();
clearTempEncryptedFolder();
} else if (itemId == R.id.action_upload_list_cancelled_resume) {
retryCancelledUploads();
}
@ -179,6 +183,12 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
popup.show();
}
private void clearTempEncryptedFolder() {
Optional<User> user = parentActivity.getUser();
user.ifPresent(value -> FileDataStorageManager.clearTempEncryptedFolder(value.getAccountName()));
}
// FIXME For e2e resume is not working
private void retryCancelledUploads() {
new Thread(() -> {
boolean showNotExistMessage = FileUploadHelper.Companion.instance().retryCancelledUploads(

View file

@ -27,7 +27,7 @@ public final class DrawerMenuUtil {
User user,
Resources resources) {
if (user.isAnonymous()) {
filterMenuItems(menu, R.id.nav_gallery, R.id.nav_favorites);
removeMenuItem(menu, R.id.nav_gallery, R.id.nav_favorites);
}
if (!resources.getBoolean(R.bool.recently_modified_enabled)) {
@ -38,26 +38,29 @@ public final class DrawerMenuUtil {
public static void filterTrashbinMenuItem(Menu menu, @Nullable OCCapability capability) {
if (capability != null && capability.getFilesUndelete().isFalse() ||
capability != null && capability.getFilesUndelete().isUnknown()) {
filterMenuItems(menu, R.id.nav_trashbin);
removeMenuItem(menu, R.id.nav_trashbin);
}
}
public static void filterActivityMenuItem(Menu menu, @Nullable OCCapability capability) {
if (capability != null && capability.getActivity().isFalse()) {
filterMenuItems(menu, R.id.nav_activity);
removeMenuItem(menu, R.id.nav_activity);
}
}
public static void filterAssistantMenuItem(Menu menu, @Nullable OCCapability capability, Resources resources) {
boolean showCondition = capability != null && capability.getAssistant().isTrue() && !resources.getBoolean(R.bool.is_branded_client);
if (!showCondition) {
filterMenuItems(menu, R.id.nav_assistant);
if (resources.getBoolean(R.bool.is_branded_client)) {
if (capability != null && capability.getAssistant().isFalse()) {
removeMenuItem(menu, R.id.nav_assistant);
}
} else {
removeMenuItem(menu, R.id.nav_assistant);
}
}
public static void filterGroupfoldersMenuItem(Menu menu, @Nullable OCCapability capability) {
if (capability != null && !capability.getGroupfolders().isTrue()) {
filterMenuItems(menu, R.id.nav_groupfolders);
removeMenuItem(menu, R.id.nav_groupfolders);
}
}
@ -74,7 +77,7 @@ public final class DrawerMenuUtil {
}
}
private static void filterMenuItems(Menu menu, int... menuIds) {
private static void removeMenuItem(Menu menu, int... menuIds) {
if (menuIds != null) {
for (int menuId : menuIds) {
menu.removeItem(menuId);

View file

@ -547,12 +547,12 @@ public final class EncryptionUtils {
return Base64.decode(string, Base64.NO_WRAP);
}
public static EncryptedFile encryptFile(File file, Cipher cipher) throws InvalidParameterSpecException {
// FIXME this won't work on low or write-protected storage
File encryptedFile = new File(file.getAbsolutePath() + ".enc.jpg");
encryptFileWithGivenCipher(file, encryptedFile, cipher);
public static EncryptedFile encryptFile(String accountName, File file, Cipher cipher) throws InvalidParameterSpecException, IOException {
File tempEncryptedFolder = FileDataStorageManager.createTempEncryptedFolder(accountName);
File tempEncryptedFile = File.createTempFile(file.getName(), null, tempEncryptedFolder);
encryptFileWithGivenCipher(file, tempEncryptedFile, cipher);
String authenticationTagString = getAuthenticationTag(cipher);
return new EncryptedFile(encryptedFile, authenticationTagString);
return new EncryptedFile(tempEncryptedFile, authenticationTagString);
}
public static String getAuthenticationTag(Cipher cipher) throws InvalidParameterSpecException {
@ -569,7 +569,7 @@ public final class EncryptionUtils {
}
public static void encryptFileWithGivenCipher(File inputFile, File encryptedFile, Cipher cipher) {
try( FileInputStream inputStream = new FileInputStream(inputFile);
try (FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream fileOutputStream = new FileOutputStream(encryptedFile);
CipherOutputStream outputStream = new CipherOutputStream(fileOutputStream, cipher)) {
byte[] buffer = new byte[4096];

View file

@ -106,6 +106,17 @@ public final class FileStorageUtils {
// that can be in the accountName since 0.1.190B
}
public static String getTemporalEncryptedFolderPath(String accountName) {
return MainApp
.getAppContext()
.getFilesDir()
.getAbsolutePath()
+ File.separator
+ accountName
+ File.separator
+ "temp_encrypted_folder";
}
/**
* Get absolute path to tmp folder inside app folder for given accountName.
*/

View file

@ -20,7 +20,6 @@ import com.nextcloud.client.device.BatteryStatus;
import com.nextcloud.client.device.PowerManagementService;
import com.nextcloud.client.jobs.BackgroundJobManager;
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.jobs.upload.FileUploadWorker;
import com.nextcloud.client.network.ConnectivityService;
import com.owncloud.android.MainApp;
import com.owncloud.android.datamodel.FilesystemDataProvider;
@ -55,6 +54,59 @@ public final class FilesSyncHelper {
// utility class -> private constructor
}
private static void insertCustomFolderIntoDB(Path path,
SyncedFolder syncedFolder,
FilesystemDataProvider filesystemDataProvider,
long lastCheck,
long thisCheck) {
final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs();
try {
FileUtil.walkFileTree(path, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
File file = path.toFile();
if (syncedFolder.isExcludeHidden() && file.isHidden()) {
// exclude hidden file or folder
return FileVisitResult.CONTINUE;
}
if (attrs.lastModifiedTime().toMillis() < lastCheck) {
// skip files that were already checked
return FileVisitResult.CONTINUE;
}
if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
// storeOrUpdateFileValue takes a few ms
// -> Rest of this file check takes not even 1 ms.
filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
attrs.lastModifiedTime().toMillis(),
file.isDirectory(), syncedFolder);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) {
return null;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
return FileVisitResult.CONTINUE;
}
});
syncedFolder.setLastScanTimestampMs(thisCheck);
} catch (IOException e) {
Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e);
}
}
private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) {
final Context context = MainApp.getAppContext();
final ContentResolver contentResolver = context.getContentResolver();
@ -63,55 +115,33 @@ public final class FilesSyncHelper {
if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) {
MediaFolderType mediaType = syncedFolder.getType();
final long lastCheckTimestampMs = syncedFolder.getLastScanTimestampMs();
final long thisCheckTimestampMs = System.currentTimeMillis();
Log_OC.d(TAG,"File-sync start check folder "+syncedFolder.getLocalPath());
long startTime = System.nanoTime();
if (mediaType == MediaFolderType.IMAGE) {
FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI
, syncedFolder);
FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI,
syncedFolder,
lastCheckTimestampMs, thisCheckTimestampMs);
FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
syncedFolder);
syncedFolder,
lastCheckTimestampMs, thisCheckTimestampMs);
} else if (mediaType == MediaFolderType.VIDEO) {
FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI,
syncedFolder);
syncedFolder,
lastCheckTimestampMs, thisCheckTimestampMs);
FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
syncedFolder);
syncedFolder,
lastCheckTimestampMs, thisCheckTimestampMs);
} else {
try {
FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver);
Path path = Paths.get(syncedFolder.getLocalPath());
FileUtil.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
File file = path.toFile();
if (syncedFolder.isExcludeHidden() && file.isHidden()) {
// exclude hidden file or folder
return FileVisitResult.CONTINUE;
}
if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) {
filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(),
attrs.lastModifiedTime().toMillis(),
file.isDirectory(), syncedFolder);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
if (syncedFolder.isExcludeHidden() && dir.compareTo(Paths.get(syncedFolder.getLocalPath())) != 0 && dir.toFile().isHidden()) {
return null;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e);
}
FilesSyncHelper.insertCustomFolderIntoDB(path, syncedFolder, filesystemDataProvider, lastCheckTimestampMs, thisCheckTimestampMs);
}
Log_OC.d(TAG,"File-sync finished full check for custom folder "+syncedFolder.getLocalPath()+" within "+(System.nanoTime() - startTime)+ "ns");
}
}
@ -160,7 +190,8 @@ public final class FilesSyncHelper {
return filePath;
}
private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) {
private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder,
long lastCheckTimestampMs, long thisCheckTimestampMs) {
final Context context = MainApp.getAppContext();
final ContentResolver contentResolver = context.getContentResolver();
@ -192,13 +223,22 @@ public final class FilesSyncHelper {
while (cursor.moveToNext()) {
contentPath = cursor.getString(column_index_data);
isFolder = new File(contentPath).isDirectory();
if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) {
if (syncedFolder.getLastScanTimestampMs() != SyncedFolder.NOT_SCANNED_YET &&
cursor.getLong(column_index_date_modified) < (lastCheckTimestampMs / 1000)) {
continue;
}
if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000) {
// storeOrUpdateFileValue takes a few ms
// -> Rest of this file check takes not even 1 ms.
filesystemDataProvider.storeOrUpdateFileValue(contentPath,
cursor.getLong(column_index_date_modified), isFolder,
syncedFolder);
}
}
cursor.close();
syncedFolder.setLastScanTimestampMs(thisCheckTimestampMs);
}
}

View file

@ -1,15 +0,0 @@
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
~ SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FF000000"
android:pathData="m296,615 l-56,-56 240,-240 240,240 -56,56 -184,-184 -184,184Z" />
</vector>

View file

@ -114,6 +114,42 @@
</RelativeLayout>
<LinearLayout
android:id="@+id/toolbar_linear_layout"
android:visibility="gone"
tools:visibility="visible"
android:orientation="horizontal"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:id="@+id/toolbar_menu_button"
style="@style/Widget.AppTheme.Button.IconButton"
android:layout_width="48dp"
android:layout_height="48dp"
app:cornerRadius="@dimen/button_corner_radius"
app:icon="@drawable/ic_menu"
app:iconTint="@color/black" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/toolbar_title"
android:gravity="center"
android:textColor="@color/black"
android:lines="1"
android:textSize="16sp"
android:layout_width="wrap_content"
android:layout_height="48dp"/>
</LinearLayout>
<!-- home/search toolbar -->
<com.google.android.material.card.MaterialCardView
android:id="@+id/home_toolbar"

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">فشل إنشاء الحساب</string>
<string name="account_icon">أيقونة الحساب </string>
<string name="account_not_found">الحساب غير موجود </string>
<string name="action_clear_failed_uploads">مسح الملفات التي فشل رفعها</string>
<string name="action_edit">تعديل</string>
<string name="action_empty_notifications">إزالة جميع التنبيهات</string>
<string name="action_empty_trashbin">تفريغ سلة المهملات</string>
@ -57,6 +56,7 @@
<string name="assistant_screen_task_types_error_state_message">تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت.</string>
<string name="assistant_screen_top_bar_title">المُساعِد</string>
<string name="assistant_screen_unknown_task_status_text">غير معروف</string>
<string name="assistant_task_detail_screen_output_button_title">المُخرَجَات</string>
<string name="associated_account_not_found">الحساب المرتبط غير موجود!</string>
<string name="auth_access_failed">فشل الوصول لـ: %1$s</string>
<string name="auth_account_does_not_exist">هذا الحساب لم تتم إضافته في هذا الجهاز بعد</string>
@ -171,7 +171,6 @@
<string name="confirmation_remove_folder_alert">هل توَدُّ حقاً حذف %1$s وما يحتويه؟</string>
<string name="confirmation_remove_folders_alert">هل توَدُّ حقاً حذف العناصر المختارة وما يحتوّه؟</string>
<string name="confirmation_remove_local">محلياً فقط</string>
<string name="conflict_dialog_error">خطأ في إنشاء حوار التعارض!</string>
<string name="conflict_file_headline">ملف متضارب %1$s </string>
<string name="conflict_local_file">ملف محلي</string>
<string name="conflict_message_description">إذا قمت باختيار كلا الاصدارين, الملف المحلي سيحتوي على رقم ملحق باسم الملف.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Account creation failed</string>
<string name="account_icon">Account icon</string>
<string name="account_not_found">Account not found!</string>
<string name="action_clear_failed_uploads">Clear failed uploads</string>
<string name="action_edit">Edit</string>
<string name="action_empty_notifications">Clear all notifications</string>
<string name="action_empty_trashbin">Empty trash bin</string>
@ -57,6 +56,8 @@
<string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string>
<string name="assistant_screen_top_bar_title">Assistant</string>
<string name="assistant_screen_unknown_task_status_text">Unknown</string>
<string name="assistant_task_detail_screen_input_button_title">Input</string>
<string name="assistant_task_detail_screen_output_button_title">Output</string>
<string name="associated_account_not_found">Associated account not found!</string>
<string name="auth_access_failed">Access failed: %1$s</string>
<string name="auth_account_does_not_exist">The account is not added on this device yet</string>
@ -171,7 +172,6 @@
<string name="confirmation_remove_folder_alert">Do you really want to delete %1$s and its content?</string>
<string name="confirmation_remove_folders_alert">Do you really want to delete the selected items and their contents?</string>
<string name="confirmation_remove_local">Local only</string>
<string name="conflict_dialog_error">Error creating conflict dialogue!</string>
<string name="conflict_file_headline">Conflicting file %1$s</string>
<string name="conflict_local_file">Local file</string>
<string name="conflict_message_description">If you select both versions, the local file will have a number appended to its name.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Създаването на профила се провали</string>
<string name="account_icon">Профил</string>
<string name="account_not_found">Няма намерен профил!</string>
<string name="action_clear_failed_uploads">Изчисти провалените качвания</string>
<string name="action_edit">Променяне</string>
<string name="action_empty_notifications">Премахнете известията</string>
<string name="action_empty_trashbin">Изпразване на кошчето</string>
@ -153,7 +152,6 @@
<string name="confirmation_remove_folder_alert">Наистина ли желаете %1$s и съдържанието ѝ да бъдат изтрито?</string>
<string name="confirmation_remove_folders_alert">Наистина ли желаете избраните елементи и съдържанието им да бъдат премахнати?</string>
<string name="confirmation_remove_local">Само локално</string>
<string name="conflict_dialog_error">Грешка при създаване на диалог за конфликт!</string>
<string name="conflict_file_headline">Несъвместим файл %1$s</string>
<string name="conflict_local_file">Локален файл</string>
<string name="conflict_message_description">Ако изберете и двете версии, ще бъде добавен номер към името на локалния файл.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">C\'hwitet eo bet ar c\'houiñ eus anr c\'hont</string>
<string name="account_icon">Ikonenn kont</string>
<string name="account_not_found">N\'o ket bet kavet ar c\'hont</string>
<string name="action_clear_failed_uploads">Lemmel ar pellgasoù chwitet</string>
<string name="action_edit">Cheñch</string>
<string name="action_empty_notifications">Lemmel toud ar c\'hemenadennoù</string>
<string name="action_send_share">Kas/Rannan</string>
@ -136,7 +135,6 @@
<string name="confirmation_remove_folder_alert">Sur oc\'h lemel %1$s ha pep tra a zo barzh ?</string>
<string name="confirmation_remove_folders_alert">Sur oc\'h lemel an traoù choazet ha pep tra a zo barzh ?</string>
<string name="confirmation_remove_local">E-berzh nemetken</string>
<string name="conflict_dialog_error">Ur fazi en deus krouet divizoù diemglev !</string>
<string name="conflict_message_description">Ma choazit an daou stumm, ar restr diabarzh en do un niver ouzhpennet war e anv.</string>
<string name="contactlist_item_icon">Skeudenn implijer evit ar roll darempredoù</string>
<string name="contactlist_no_permission">Oaetre ebet roet, netra a zo bet emporzhiet.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Ha fallat la creació del compte</string>
<string name="account_icon">Icona del compte</string>
<string name="account_not_found">No s\'ha trobat el compte!</string>
<string name="action_clear_failed_uploads">Esborra les pujades amb errors</string>
<string name="action_edit">Edita</string>
<string name="action_empty_notifications">Neteja totes les notificacions</string>
<string name="action_empty_trashbin">Buida la paperera</string>
@ -152,7 +151,6 @@
<string name="confirmation_remove_folder_alert">Esteu segur que voleu suprimir %1$s i els seus continguts?</string>
<string name="confirmation_remove_folders_alert">Esteu segur que voleu suprimir els elements seleccionats i el seu contingut?</string>
<string name="confirmation_remove_local">Només local</string>
<string name="conflict_dialog_error">Error creant diàleg de conflicte!</string>
<string name="conflict_file_headline">Fitxer en conflicte %1$s</string>
<string name="conflict_local_file">Fitxer local</string>
<string name="conflict_message_description">Si seleccioneu ambdues versions, s\'afegirà un numero al nom del fitxer local.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Vytvoření účtu se nezdařilo</string>
<string name="account_icon">Ikona účtu</string>
<string name="account_not_found">Účet nenalezen!</string>
<string name="action_clear_failed_uploads">Vyčistit nezdařená nahrávání</string>
<string name="action_edit">Upravit</string>
<string name="action_empty_notifications">Vyčistit všechna upozornění</string>
<string name="action_empty_trashbin">Vyprázdnit koš</string>
@ -43,6 +42,7 @@
<string name="assistant_screen_successful_task_text">Dokončeno</string>
<string name="assistant_screen_task_delete_success_message">Úloha úspěšně smazána</string>
<string name="assistant_screen_unknown_task_status_text">Neznámé</string>
<string name="assistant_task_detail_screen_output_button_title">Výstup</string>
<string name="associated_account_not_found">Související účet nenalezen!</string>
<string name="auth_access_failed">Přístup se nezdařil: %1$s</string>
<string name="auth_account_does_not_exist">Účet zatím není na tomto zařízení přidán</string>
@ -157,7 +157,6 @@
<string name="confirmation_remove_folder_alert">Opravdu chcete %1$s a jeho obsah odstranit?</string>
<string name="confirmation_remove_folders_alert">Opravdu chcete vybrané položky a jejich obsah odstranit?</string>
<string name="confirmation_remove_local">Pouze místní</string>
<string name="conflict_dialog_error">Chyba při vytváření dialogu ke konfliktu!</string>
<string name="conflict_file_headline">Kolidující soubor %1$s</string>
<string name="conflict_local_file">Místní soubor</string>
<string name="conflict_message_description">Pokud zvolíte obě verze, k názvu místního souboru bude připojeno číslo.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Fejl ved oprettelse af konto</string>
<string name="account_icon">Kontoikon</string>
<string name="account_not_found">Konto blev ikke fundet.</string>
<string name="action_clear_failed_uploads">Fjern fejlede uploads</string>
<string name="action_edit">Redigér</string>
<string name="action_empty_notifications">Ryd alle notifikationer</string>
<string name="action_empty_trashbin">Tøm papirkurv</string>
@ -165,7 +164,6 @@
<string name="confirmation_remove_folder_alert">Er du sikker på at du vil slette %1$s med indhold?</string>
<string name="confirmation_remove_folders_alert">Er du sikker på at du vil slette de valgte artikler med indhold?</string>
<string name="confirmation_remove_local">Kun lokal</string>
<string name="conflict_dialog_error">Fejl ved skabelse af konfliktdialog</string>
<string name="conflict_file_headline">Fil i konflikt %1$s</string>
<string name="conflict_local_file">Lokal fil</string>
<string name="conflict_message_description">Hvis du vælger begge versioner, vil den lokale fil få tilføjet et nummer til sit navn.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Kontoerstellung fehlgeschlagen</string>
<string name="account_icon">Konto-Icon</string>
<string name="account_not_found">Kein Konto gefunden</string>
<string name="action_clear_failed_uploads">Fehlgeschlagene Uploads entfernen</string>
<string name="action_edit">Bearbeiten</string>
<string name="action_empty_notifications">Alle Benachrichtigungen löschen</string>
<string name="action_empty_trashbin">Papierkorb leeren</string>
@ -57,6 +56,8 @@
<string name="assistant_screen_task_types_error_state_message">Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung.</string>
<string name="assistant_screen_top_bar_title">Assistent</string>
<string name="assistant_screen_unknown_task_status_text">Unbekannt</string>
<string name="assistant_task_detail_screen_input_button_title">Eingabe</string>
<string name="assistant_task_detail_screen_output_button_title">Ausgabe</string>
<string name="associated_account_not_found">Verknüpftes Konto nicht gefunden!</string>
<string name="auth_access_failed">Zugriffsfehler: %1$s</string>
<string name="auth_account_does_not_exist">Das Konto ist bislang auf dem Gerät nicht vorhanden</string>
@ -171,7 +172,6 @@
<string name="confirmation_remove_folder_alert">Wollen Sie %1$s und deren Inhalte wirklich löschen?</string>
<string name="confirmation_remove_folders_alert">Möchten Sie die ausgewählten Elemente und deren inhalt wirklich löschen?</string>
<string name="confirmation_remove_local">Nur lokal</string>
<string name="conflict_dialog_error">Fehler beim Erstellen des Konfliktdialoges</string>
<string name="conflict_file_headline">Konflikt-Datei %1$s</string>
<string name="conflict_local_file">Lokale Datei</string>
<string name="conflict_message_description">Falls beide Versionen gewählt werden, wird bei der lokalen Datei eine Zahl am Ende des Dateinamens hinzugefügt.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Αποτυχία δημιουργίας λογαριασμού</string>
<string name="account_icon">Εικονίδιο λογαριασμού</string>
<string name="account_not_found">Δεν βρέθηκε λογαριασμός!</string>
<string name="action_clear_failed_uploads">Εκκαθάριση αποτυχημένων μεταφορτώσεων</string>
<string name="action_edit">Επεξεργασία</string>
<string name="action_empty_notifications">Εκκαθάριση ειδοποιήσεων</string>
<string name="action_empty_trashbin">Άδειασμα απορριμάτων</string>
@ -151,7 +150,6 @@
<string name="confirmation_remove_folder_alert">Θέλετε σίγουρα να διαγράψετε το %1$s και τα περιεχόμενά του;</string>
<string name="confirmation_remove_folders_alert">Θέλετε να διαγράψετε τα επιλεγμένα αντικείμενα και τα περιεχόμενά τους;</string>
<string name="confirmation_remove_local">Μόνο τοπικά</string>
<string name="conflict_dialog_error">Σφάλμα δημιουργίας αναφοράς διένεξης!</string>
<string name="conflict_file_headline">Αρχείο σε αντίφαση %1$s</string>
<string name="conflict_local_file">Τοπικό αρχείο</string>
<string name="conflict_message_description">Εάν επιλέξετε και τις δύο εκδόσεις, στο όνομα του τοπικού αρχείου θα προστεθεί ένας αριθμός.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Kreado de konto malsukcesis</string>
<string name="account_icon">Konta piktogramo</string>
<string name="account_not_found">Konto ne trovita!</string>
<string name="action_clear_failed_uploads">Vakigi malsukcesajn alŝutojn</string>
<string name="action_edit">Modifi</string>
<string name="action_empty_notifications">Forviŝi ĉiujn sciigojn</string>
<string name="action_empty_trashbin">Malpleni rubujon</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Falló la creación de la carpeta</string>
<string name="account_icon">Icono de cuenta</string>
<string name="account_not_found">Cuenta no encontrada!</string>
<string name="action_clear_failed_uploads">Borrar subidas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Borrar todas las notificaciones</string>
<string name="action_empty_trashbin">Vaciar la papelera</string>
@ -150,7 +149,6 @@
<string name="confirmation_remove_folder_alert">¿Realmente quieres eliminar %1$s y su contenido?</string>
<string name="confirmation_remove_folders_alert">¿Realmente desea eliminar los elementos seleccionados y sus contenidos?</string>
<string name="confirmation_remove_local">Sólo local</string>
<string name="conflict_dialog_error">¡Error al crear el diálogo de conflicto!</string>
<string name="conflict_file_headline">Archivo en conflicto %1$s</string>
<string name="conflict_local_file">Archivo local</string>
<string name="conflict_message_description">Si selecciona ambas versiones, se le agregará un número al nombre del archivo copiado.</string>

View file

@ -5,7 +5,6 @@
<string name="about_version">versión %1$s</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_send_share">Enviar/Compartir</string>
<string name="action_switch_grid_view">Vista de cuadrícula</string>

View file

@ -6,7 +6,6 @@
<string name="account_creation_failed">Fallo en la creación de la cuenta</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Borrar todas las notificaciones</string>
<string name="action_empty_trashbin">Vacíar la papelera</string>

View file

@ -5,7 +5,6 @@
<string name="about_version">versión %1$s</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_send_share">Enviar/Compartir</string>
<string name="action_switch_grid_view">Vista de cuadrícula</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Error al crear la cuenta</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Borrar todas las notificaciones</string>
<string name="action_send_share">Enviar/Compartir</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Creación de cuenta fallida</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Borrar todas las notificaciones</string>
<string name="action_empty_trashbin">Eliminar papelera de reciclaje</string>
@ -153,7 +152,6 @@
<string name="confirmation_remove_folder_alert">¿Realmente quieres eliminar %1$s y sus contenidos? </string>
<string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
<string name="confirmation_remove_local">Sólo local</string>
<string name="conflict_dialog_error">¡Error al crear el diálogo de conflicto!</string>
<string name="conflict_file_headline">Archivo en conflicto %1$s</string>
<string name="conflict_local_file">Archivo local</string>
<string name="conflict_message_description">Si selecciona ambas versiones, el archivo local tendrá un número agregado a su nombre.</string>

View file

@ -5,7 +5,6 @@
<string name="about_version">versión %1$s</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_send_share">Enviar/Compartir</string>
<string name="action_switch_grid_view">Vista de cuadrícula</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Falló la creación de la cuenta</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Limpiar todas las notificaciones</string>
<string name="action_empty_trashbin">Vaciar la papelera de reciclaje</string>
@ -171,7 +170,6 @@
<string name="confirmation_remove_folder_alert">¿Realmente quieres eliminar %1$s y sus contenidos? </string>
<string name="confirmation_remove_folders_alert">¿Reamente deseas eliminar los elementos seleccionados y sus contenidos?</string>
<string name="confirmation_remove_local">Sólo local</string>
<string name="conflict_dialog_error">¡Se presentó un error al crear el diálogo de conflictos!</string>
<string name="conflict_file_headline">Archivo conflictivo %1$s</string>
<string name="conflict_local_file">Archivo local</string>
<string name="conflict_message_description">Si seleccionas ambas versiones, el archivo local tendrá un número al final del nombre.</string>

View file

@ -5,7 +5,6 @@
<string name="about_version">versión %1$s</string>
<string name="account_icon">Ícono de la cuenta</string>
<string name="account_not_found">¡No se encontró la cuenta!</string>
<string name="action_clear_failed_uploads">Borrar cargas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_send_share">Enviar/Compartir</string>
<string name="action_switch_grid_view">Vista de cuadrícula</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Ha fallado la creación de la cuenta</string>
<string name="account_icon">Icono de la cuenta</string>
<string name="account_not_found">¡Cuenta no encontrada!</string>
<string name="action_clear_failed_uploads">Borrar subidas fallidas</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Limpiar todas las notificaciones</string>
<string name="action_empty_trashbin">Vaciar papelera</string>
@ -47,6 +46,8 @@
<string name="assistant_screen_task_delete_success_message">¡La tarea fue eliminada exitósamente!</string>
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Eliminar tarea</string>
<string name="assistant_screen_unknown_task_status_text">Desconocido</string>
<string name="assistant_task_detail_screen_input_button_title">Entrada</string>
<string name="assistant_task_detail_screen_output_button_title">Salida</string>
<string name="associated_account_not_found">¡Cuenta asociada no encontrada!</string>
<string name="auth_access_failed">Acceso fallido: %1$s</string>
<string name="auth_account_does_not_exist">La cuenta no se ha añadido aún en este dispositivo</string>
@ -160,7 +161,6 @@
<string name="confirmation_remove_folder_alert">¿Realmente deseas eliminar %1$s y todo su contenido?</string>
<string name="confirmation_remove_folders_alert">¿Estás seguro de que quieres eliminar los elementos seleccionados y sus contenidos?</string>
<string name="confirmation_remove_local">Solo local</string>
<string name="conflict_dialog_error">¡Error al crear el diálogo de conflicto!</string>
<string name="conflict_file_headline">Conflicto en archivo %1$s</string>
<string name="conflict_local_file">Archivo local</string>
<string name="conflict_message_description">Si seleccionas ambas versiones, el archivo local tendrá un número añadido a su nombre.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Konto loomine ebaõnnestus</string>
<string name="account_icon">Konto ikoon</string>
<string name="account_not_found">Kontot ei leitud!</string>
<string name="action_clear_failed_uploads">Puhasta ebaõnnestunud üleslaadimised</string>
<string name="action_edit">Redigeeri</string>
<string name="action_empty_notifications">Kustuta kõik teavitused</string>
<string name="action_empty_trashbin">Tühjenda prügikast</string>
@ -136,7 +135,6 @@
<string name="confirmation_remove_folder_alert">Oled sa kindel, et soovid %1$s ja selle sisu kustutada?</string>
<string name="confirmation_remove_folders_alert">Oled sa kindel, et soovid valitud objektid ja nende sisud kustutada?</string>
<string name="confirmation_remove_local">Ainult kohalik</string>
<string name="conflict_dialog_error">Konfliktidialoogi loomine ebaõnnestus!</string>
<string name="conflict_local_file">Kohalik fail</string>
<string name="conflict_message_description">Kui valid mõlemad versioonid, siis lisatakse kohaliku faili nimele number.</string>
<string name="conflict_server_file">Serveri fail</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Kontuaren sorrerak huts egin du</string>
<string name="account_icon">Kontuaren ikonoa</string>
<string name="account_not_found">Ez da kontua aurkitu!</string>
<string name="action_clear_failed_uploads">Garbitu huts egin duten igoerak</string>
<string name="action_edit">Aldatu</string>
<string name="action_empty_notifications">Garbitu jakinarazpen guztiak</string>
<string name="action_empty_trashbin">Hustu zakarrontzia</string>
@ -47,6 +46,8 @@
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Ezabatu zeregina</string>
<string name="assistant_screen_top_bar_title">Morroia</string>
<string name="assistant_screen_unknown_task_status_text">Ezezaguna</string>
<string name="assistant_task_detail_screen_input_button_title">Sarrera</string>
<string name="assistant_task_detail_screen_output_button_title">Irteera</string>
<string name="associated_account_not_found">Ez da aurkitu lotutako konturik</string>
<string name="auth_access_failed">Huts egin du atzitzean: %1$s</string>
<string name="auth_account_does_not_exist">Kontua ez da gailu honetan gehitu oraindik</string>
@ -161,7 +162,6 @@
<string name="confirmation_remove_folder_alert">Ziur zaude %1$s eta bere edukia ezabatu nahi duzula?</string>
<string name="confirmation_remove_folders_alert">Ziur zaude hautatutako elementuak eta beren edukiak ezabatu nahi dituzula?</string>
<string name="confirmation_remove_local">Lokala bakarrik</string>
<string name="conflict_dialog_error">Errorea gatazkaren elkarrizketa-koadroa sortzean!</string>
<string name="conflict_file_headline">%1$sfitxategi gatazkatsua</string>
<string name="conflict_local_file">Fitxategi lokala</string>
<string name="conflict_message_description">Bi bertsioak hautatzen badituzu, fitxategi lokalaren izenari zenbaki bat gehituko zaio.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">ایجاد حساب ناموفق بود</string>
<string name="account_icon">آیکون حساب</string>
<string name="account_not_found">حساب پیدا نشد!</string>
<string name="action_clear_failed_uploads">پاک کردن آپلودهای ناموفق</string>
<string name="action_edit">ویرایش</string>
<string name="action_empty_notifications">پاک کردن تمام اعلانها</string>
<string name="action_empty_trashbin">خالی کردن سطل زباله</string>
@ -158,7 +157,6 @@
<string name="confirmation_remove_folder_alert">آیا واقعا می خواهید %1$s و محتویات آن را حذف کنید؟</string>
<string name="confirmation_remove_folders_alert">آیا واقعاً می‌خواهید موارد انتخاب شده و محتوای آنها حذف شود؟</string>
<string name="confirmation_remove_local">فقط محلی</string>
<string name="conflict_dialog_error">هنگام ساختن دیالوگ مغایرت‌ها خطایی رخ داده است!</string>
<string name="conflict_file_headline">فایل متناقض %1$s</string>
<string name="conflict_local_file">پروندهٔ محلّی</string>
<string name="conflict_message_description">اگر هردو نسخه را انتخاب کنید، یک شماره به نام فایل محلی اضافه خواهد شد.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Tilin luonti epäonnistui</string>
<string name="account_icon">Tilikuvake</string>
<string name="account_not_found">Tiliä ei löydy!</string>
<string name="action_clear_failed_uploads">Tyhjennä epäonnistuneet lähetykset</string>
<string name="action_edit">Muokkaa</string>
<string name="action_empty_notifications">Hylkää kaikki ilmoitukset</string>
<string name="action_empty_trashbin">Tyhjennä roskakori</string>
@ -158,7 +157,6 @@
<string name="confirmation_remove_folder_alert">Haluatko varmasti poistaa kohteen %1$s ja sen sisällön?</string>
<string name="confirmation_remove_folders_alert">Haluatko varmasti poistaa valitut kohteet ja niiden sisällön?</string>
<string name="confirmation_remove_local">Vain paikallisen</string>
<string name="conflict_dialog_error">Virhe konfliktitietojen näyttämisessä!</string>
<string name="conflict_file_headline">Ristiriitainen kohde %1$s</string>
<string name="conflict_local_file">Paikallinen tiedosto</string>
<string name="conflict_message_description">Jos valitset molemmat versiot, paikallisen tiedoston nimeen lisätään numero.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Échec de création du compte</string>
<string name="account_icon">Icône du compte</string>
<string name="account_not_found">Compte introuvable !</string>
<string name="action_clear_failed_uploads">Effacer les téléversements échoués</string>
<string name="action_edit">Modifier</string>
<string name="action_empty_notifications">Effacer toutes les notifications</string>
<string name="action_empty_trashbin">Vider la corbeille</string>
@ -57,6 +56,8 @@
<string name="assistant_screen_task_types_error_state_message">Impossible de récupérer les types des tâches, veuillez vérifier votre connexion Internet.</string>
<string name="assistant_screen_top_bar_title">Assistant</string>
<string name="assistant_screen_unknown_task_status_text">Inconnu</string>
<string name="assistant_task_detail_screen_input_button_title">Entrée</string>
<string name="assistant_task_detail_screen_output_button_title">Sortie</string>
<string name="associated_account_not_found">Compte associé introuvable !</string>
<string name="auth_access_failed">L\'accès a échoué: %1$s</string>
<string name="auth_account_does_not_exist">Le compte n\'est pas encore ajouté sur cet appareil</string>
@ -171,7 +172,6 @@
<string name="confirmation_remove_folder_alert">Voulez-vous vraiment supprimer %1$s et ses contenus ?</string>
<string name="confirmation_remove_folders_alert">Souhaitez-vous vraiment supprimer les éléments sélectionnés ainsi que leurs contenus ?</string>
<string name="confirmation_remove_local">Local seulement</string>
<string name="conflict_dialog_error">Une erreur qui crée un dialogue conflictuel !</string>
<string name="conflict_file_headline">Fichier %1$s en conflit</string>
<string name="conflict_local_file">fichier local</string>
<string name="conflict_message_description">Si vous sélectionnez les deux versions, le fichier local aura un numéro ajouté à son nom.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Dhfhàillig cruthachadh a chunntais</string>
<string name="account_icon">Ìomhaigheag a chunntais</string>
<string name="account_not_found">Cha deach an cunntas a lorg!</string>
<string name="action_clear_failed_uploads">Falamhaich na luchdaidhean suas a dhfhàillig</string>
<string name="action_edit">Deasaich</string>
<string name="action_empty_notifications">Falamhaich a h-uile brath</string>
<string name="action_empty_trashbin">Falamhaich an sgudal</string>
@ -138,7 +137,6 @@
<string name="confirmation_remove_folder_alert">A bheil thu cinnteach gu bheil thu airson %1$s s a shusbaint a sguabadh às?</string>
<string name="confirmation_remove_folders_alert">A bheil thu cinnteach gu bheil thu airson na nithean a thagh thu s an susbaint a sguabadh às?</string>
<string name="confirmation_remove_local">Ionadail a-mhàin</string>
<string name="conflict_dialog_error">Mearachd le cruthachadh còmhradh na còmhstri!</string>
<string name="conflict_file_headline">Faidhle %1$s ann an còmhstri</string>
<string name="conflict_message_description">Ma thaghas tu an dà thionndadh, thèid àireamh a chur ri ainm an fhaidhle ionadail.</string>
<string name="contactlist_item_icon">Ìomhaigheag a chleachdaiche air liosta an luchd-aithne</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Produciuse un fallo ao crear a conta</string>
<string name="account_icon">Icona da conta</string>
<string name="account_not_found">Non se atopou a conta!</string>
<string name="action_clear_failed_uploads">Limpar os envíos fallados</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Limpar todas as notificacións</string>
<string name="action_empty_trashbin">Baleirar o lixo</string>
@ -43,6 +42,7 @@
<string name="assistant_screen_successful_task_text">Completado</string>
<string name="assistant_screen_task_delete_success_message">A tarefa foi eliminada satisfactoriamente</string>
<string name="assistant_screen_unknown_task_status_text">Descoñecido</string>
<string name="assistant_task_detail_screen_input_button_title">Entrada</string>
<string name="associated_account_not_found">Non se atopou unha conta asociada!</string>
<string name="auth_access_failed">Acceso fallado: %1$s</string>
<string name="auth_account_does_not_exist">A conta aínda non existe no dispositivo</string>
@ -155,7 +155,6 @@
<string name="confirmation_remove_folder_alert">Confirma que quere eliminar %1$s e todo o seu contido?</string>
<string name="confirmation_remove_folders_alert">Confirma que quere eliminar os elementos seleccionados e o seu contido?</string>
<string name="confirmation_remove_local">Só local</string>
<string name="conflict_dialog_error">Produciuse un erro ao crear o diálogo de conflitos!</string>
<string name="conflict_file_headline">Ficheiro en conflito %1$s</string>
<string name="conflict_local_file">Ficheiro local</string>
<string name="conflict_message_description">Se selecciona ambas versións, o ficheiro local terá un número engadido ao nome.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Kreiranje korisničkog računa neuspjelo</string>
<string name="account_icon">Ikona korisničkog računa</string>
<string name="account_not_found">Korisnički račun nije pronađen!</string>
<string name="action_clear_failed_uploads">Izbriši neuspješne otpreme</string>
<string name="action_edit">Uredi</string>
<string name="action_empty_notifications">Izbriši sve obavijesti</string>
<string name="action_empty_trashbin">Isprazni kantu za smeće</string>
@ -145,7 +144,6 @@
<string name="confirmation_remove_folder_alert">Želite li zaista izbrisati %1$s i pripadajući sadržaj?</string>
<string name="confirmation_remove_folders_alert">Želite li zaista izbrisati odabrane stavke i pripadajući sadržaj?</string>
<string name="confirmation_remove_local">Samo lokalno</string>
<string name="conflict_dialog_error">Pogreška pri stvaranju dijaloškog okvira o nepodudaranju!</string>
<string name="conflict_file_headline">Nepodudarna datoteka %1$s</string>
<string name="conflict_local_file">Lokalna datoteka</string>
<string name="conflict_message_description">Ako odaberete obje inačice, lokalna će datoteka uz naziv imati i broj.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">A fiók létrehozása sikertelen</string>
<string name="account_icon">Fiók ikon</string>
<string name="account_not_found">A fiók nem található!</string>
<string name="action_clear_failed_uploads">Sikertelen feltöltések eltávolítása</string>
<string name="action_edit">Szerkesztés</string>
<string name="action_empty_notifications">Összes értesítés törlése</string>
<string name="action_empty_trashbin">Kuka ürítése</string>
@ -155,7 +154,6 @@
<string name="confirmation_remove_folder_alert">Biztos, hogy törli ezt: %1$s és a tartalmát?</string>
<string name="confirmation_remove_folders_alert">Biztos, hogy törli a kiválasztott elemeket és tartalmukat?</string>
<string name="confirmation_remove_local">Csak a helyi példány</string>
<string name="conflict_dialog_error">Hiba az ütközési párbeszédablak létrehozásakor!</string>
<string name="conflict_file_headline">Ütköző fájl: %1$s</string>
<string name="conflict_local_file">Helyi fájl</string>
<string name="conflict_message_description">Amennyiben mindkét verziót kiválasztja, a helyi fájl nevéhez egy szám lesz hozzáfűzve.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Pembuatan akun gagal</string>
<string name="account_icon">Ikon akun</string>
<string name="account_not_found">Akun tidak ditemukan!</string>
<string name="action_clear_failed_uploads">Bersihkan unggahan gagal.</string>
<string name="action_edit">Sunting</string>
<string name="action_empty_notifications">Hapus semua notifikasi</string>
<string name="action_empty_trashbin">Kosongkan tempat sampah</string>
@ -147,7 +146,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini
<string name="confirmation_remove_folder_alert">Apakah anda yakin ingin menghapus %1$s beserta isinya?</string>
<string name="confirmation_remove_folders_alert">Apa anda yakin ingin menghapus item yang terpilih beserta isinya?</string>
<string name="confirmation_remove_local">Lokal saja</string>
<string name="conflict_dialog_error">Galat saat membuat dialog konflik!</string>
<string name="conflict_file_headline">File konflik %1$s</string>
<string name="conflict_local_file">File lokal</string>
<string name="conflict_message_description">Jika Anda memilih kedua versi, nama dari berkas lokal akan ditambahi angka.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Gerð notandaaðgangs mistókst</string>
<string name="account_icon">Táknmynd fyrir notandaaðgang</string>
<string name="account_not_found">Notandaaðgangur fannst ekki!</string>
<string name="action_clear_failed_uploads">Hreinsa lista yfir innsendingar sem mistókust</string>
<string name="action_edit">Breyta</string>
<string name="action_empty_notifications">Hreinsa allar tilkynningar</string>
<string name="action_empty_trashbin">Tæma ruslið</string>
@ -150,7 +149,6 @@
<string name="confirmation_remove_folder_alert">Ertu viss um að þú viljir eyða %1$s og innihaldi þess?</string>
<string name="confirmation_remove_folders_alert">Ertu viss um að þú viljir eyða völdum atriðum og innihaldi þeirra?</string>
<string name="confirmation_remove_local">Einungis staðvært</string>
<string name="conflict_dialog_error">Villa við að búa til árekstraglugga!</string>
<string name="conflict_local_file">Skrá á tölvunni</string>
<string name="conflict_message_description">Ef þú velur báðar útgáfur, þá mun verða bætt tölustaf aftan við heiti afrituðu skrárinnar.</string>
<string name="conflict_server_file">Skrá á netþjóni</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Creazione dell\'account non riuscita</string>
<string name="account_icon">Icona dell\'account</string>
<string name="account_not_found">Account non trovato</string>
<string name="action_clear_failed_uploads">Cancella caricamenti non riusciti</string>
<string name="action_edit">Modifica</string>
<string name="action_empty_notifications">Cancella tutte le notifiche</string>
<string name="action_empty_trashbin">Svuota cestino</string>
@ -153,7 +152,6 @@
<string name="confirmation_remove_folder_alert">Vuoi davvero rimuovere %1$s e il relativo contenuto?</string>
<string name="confirmation_remove_folders_alert">Vuoi davvero eliminare gli elementi selezionati e il loro contenuto?</string>
<string name="confirmation_remove_local">Solo localmente</string>
<string name="conflict_dialog_error">Errore di creazione della finestra di conflitto!</string>
<string name="conflict_file_headline">File %1$s in conflitto</string>
<string name="conflict_local_file">File locale</string>
<string name="conflict_message_description">Se selezioni entrambe le versioni, il file locale ha un numero aggiunto al suo nome.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">יצירת החשבון נכשלה</string>
<string name="account_icon">סמל חשבון</string>
<string name="account_not_found">החשבון לא נמצא!</string>
<string name="action_clear_failed_uploads">פינוי העלאות שנכשלו</string>
<string name="action_edit">עריכה</string>
<string name="action_empty_notifications">פינוי כל ההתראות</string>
<string name="action_empty_trashbin">פינוי סל האשפה</string>
@ -144,7 +143,6 @@
<string name="confirmation_remove_folder_alert">למחוק את %1$s על תוכנו?</string>
<string name="confirmation_remove_folders_alert">למחוק את הפריטים הנבחרים ואת תוכנם?</string>
<string name="confirmation_remove_local">מקומי בלבד</string>
<string name="conflict_dialog_error">שגיאה ביצירת תיבת דו־שיח סתירה!</string>
<string name="conflict_local_file">קובץ מקומי</string>
<string name="conflict_message_description">בבחירה של שתי הגרסאות, יתווסף מספר בסוף שמו של הקובץ המקומי.</string>
<string name="conflict_server_file">קובץ בשרת</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">アカウントの作成に失敗しました</string>
<string name="account_icon">アカウントアイコン</string>
<string name="account_not_found">アカウントが見つかりません!</string>
<string name="action_clear_failed_uploads">失敗したアップロードのクリア</string>
<string name="action_edit">編集</string>
<string name="action_empty_notifications">全ての通知を削除</string>
<string name="action_empty_trashbin">ゴミ箱を空にする</string>
@ -155,7 +154,6 @@
<string name="confirmation_remove_folder_alert">本当に %1$s を中身も一緒に削除しますか?</string>
<string name="confirmation_remove_folders_alert">本当に選択したアイテムとその内容を削除しますか?</string>
<string name="confirmation_remove_local">ローカルのみ</string>
<string name="conflict_dialog_error">コンフリクトダイアログの作成に失敗しました</string>
<string name="conflict_file_headline">%1$sはすでに存在します</string>
<string name="conflict_local_file">ローカルファイル</string>
<string name="conflict_message_description">両方のバージョンを選択した場合、ローカルファイルはファイル名に数字が追加されます。</string>

View file

@ -1,506 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about_android">%1$s Android აპლიკაცია</string>
<string name="about_title">ჩვენს შესახებ</string>
<string name="about_version">ვერსია %1$s</string>
<string name="account_icon">ანგარიშის პიქტოგრამა</string>
<string name="account_not_found">ანგარიში ვერ იქნა ნაპოვნი!</string>
<string name="action_clear_failed_uploads">წარუმატებლად დასრულებული ატვირთების გასუფთავება</string>
<string name="action_edit">ცვლილება</string>
<string name="action_send_share">გაგზავნა/გაზიარება</string>
<string name="action_switch_grid_view">ბადისებური ხედი</string>
<string name="action_switch_list_view">ჩამონათვლისებური ხედი</string>
<string name="actionbar_mkdir">ახალი დირექტორია</string>
<string name="actionbar_open_with">გახსნა</string>
<string name="actionbar_search">ძიება</string>
<string name="actionbar_see_details">დეტალები</string>
<string name="actionbar_send_file">გაგზავნა</string>
<string name="actionbar_settings">პარამეტრები</string>
<string name="actionbar_sort">განლაგება</string>
<string name="active_user">აქტიური მომხმარებელი</string>
<string name="activities_no_results_headline">აქტოვობა ჯერ არაა</string>
<string name="activity_chooser_send_file_title">გაგზავნა</string>
<string name="activity_chooser_title">გააგზავნეთ ბმული…</string>
<string name="activity_icon">აქტივობა</string>
<string name="allow_resharing">ხელახალი გაზიარების დაშვება</string>
<string name="assistant_screen_all_task_type">ყველა</string>
<string name="auth_account_does_not_exist">ანგარიში ამ მოწყობილობაზე ჯერ არაა დამატებული</string>
<string name="auth_account_not_new">მოწყობილობაზე ანგარიში ამ მომხმარებლით და სერვერით უკვე არსებობს</string>
<string name="auth_account_not_the_same">შეყვანილი მომხმარებელი არ ემთხვევა ამ ანგარიშის მომხმარებელს</string>
<string name="auth_bad_oc_version_title">სერვერის ამოუცნობი ვერსია</string>
<string name="auth_connection_established">კავშირი დამყარდა</string>
<string name="auth_host_url">სერვერის მისამართი https://...</string>
<string name="auth_incorrect_address_title">სერვერისთვის არასწორი მისამსამართის ფორმატი</string>
<string name="auth_incorrect_path_title">სერვერი ვერ მოიძებნა</string>
<string name="auth_no_net_conn_title">ქსელთან კავშირი არ არის</string>
<string name="auth_nossl_plain_ok_title">დაცული კავშირი არაა ხელმისაწვდომი.</string>
<string name="auth_not_configured_title">არასწორი სერვერის კონფიგურაცია</string>
<string name="auth_oauth_error">წარუმატებელი ავტორიზაცია</string>
<string name="auth_oauth_error_access_denied">წვდომა ავტორიზაციის სერვერის მიერ აიკრძალა</string>
<string name="auth_redirect_non_secure_connection_title">დაცული კავშირი გადამისამართდა დაუცველი მარშრუტით.</string>
<string name="auth_secure_connection">დაცული კავშირი დამყარდა</string>
<string name="auth_ssl_general_error_title">SSL ინიციალიზაცია ვერ მოხერხდა</string>
<string name="auth_ssl_unverified_server_title">SSL სერვერის იდენტობა ვერ დამოწმდა</string>
<string name="auth_testing_connection">კავშირის შემოწმება</string>
<string name="auth_timeout_title">სერვერმა პასუხი დააგვიანია</string>
<string name="auth_unauthorized">არასწორი მომხმარებელი ან პაროლი</string>
<string name="auth_unknown_error_title">წარმოიშვა უცნობი შეცდომა!</string>
<string name="auth_unknown_host_title">ჰოსტი ვერ მოიძებნა</string>
<string name="auth_unsupported_multiaccount">%1$s არ უჭერს მხარს მრავალი ანგარიშის არსებობას</string>
<string name="auth_wrong_connection_title">კავშირი ვერ დამყარდა</string>
<string name="auto_upload_on_wifi">ატვირთვა მხოლოდ შეუზღუდავ Wi-Fi-ზე</string>
<string name="auto_upload_path">/ავტომატური_ატვირთვა</string>
<string name="autoupload_create_new_custom_folder">შექმენით ახალი სპეციფიური დირექტორია</string>
<string name="autoupload_custom_folder">დააყენეთ სპეციფიური დირექტორია</string>
<string name="avatar">ავატარი</string>
<string name="battery_optimization_close">დახურვა</string>
<string name="battery_optimization_disable">გამორთვა</string>
<string name="calendar">კალენდარი</string>
<string name="certificate_load_problem">სერტიპიკატის ჩატვირთვასთან პრობლემაა.</string>
<string name="checkbox">ჩექბოქსი</string>
<string name="clipboard_label">ტექსტი %1$s-იდან დაკოპირდა</string>
<string name="clipboard_no_text_to_copy">ბუფერში გადასატანად ტექსტი მიღებული არაა</string>
<string name="clipboard_unexpected_error">ბუფერში გადატანისას წარმოშვა მოულოდნელი შეცდომა</string>
<string name="common_back">უკან</string>
<string name="common_cancel">გაუქმება</string>
<string name="common_cancel_sync">სინქრონიზაციის შეჩერება</string>
<string name="common_choose_account">აირჩიეთ ანგარიში</string>
<string name="common_confirm">დადასტურება</string>
<string name="common_copy">კოპირება</string>
<string name="common_delete">გაუქმება</string>
<string name="common_error">შეცდომა</string>
<string name="common_error_out_memory">არასაკმარისი მეხსიერება</string>
<string name="common_error_unknown">უცნობი შეცდომა</string>
<string name="common_loading">იტვირთება…</string>
<string name="common_next">შემდეგი</string>
<string name="common_no">არა</string>
<string name="common_ok">კარგი</string>
<string name="common_pending">მოლოდინშია</string>
<string name="common_remove">გაუქმება</string>
<string name="common_rename">გადარქმევა</string>
<string name="common_save">შენახვა</string>
<string name="common_send">გაგზავნა</string>
<string name="common_share">გაზიარება</string>
<string name="common_skip">გამოტოვება</string>
<string name="common_yes">კი</string>
<string name="community_beta_headline">დევ. ვერსიის შემოწმება</string>
<string name="community_beta_text">იმყოფება სისხლდენის ზღვარზე და მოიცავს ყველა დამდეგ ფუნქციას. შეიძლება გამოჩნდეს შეცდომები, ასეთი შემთხვევისას გთხოვთ გვამცნობოთ რეპორტით.</string>
<string name="community_contribute_forum_forum">ფურუმი</string>
<string name="community_contribute_forum_text">დაეხმარეთ სხვებს</string>
<string name="community_contribute_headline">აქტიურად შეიტანეთ წვლილი</string>
<string name="community_contribute_translate_text">აპლიკაცია</string>
<string name="community_contribute_translate_translate">გადათარგმნეთ</string>
<string name="community_dev_direct_download">დეველოპმენტ რელიზის პირდაპირი ჩამოტვირთვა</string>
<string name="community_dev_fdroid">მიიღეთ დეველოპმენტ რელიზი F-Droid აპლიკაციიდან</string>
<string name="community_rc_fdroid">მიიღეთ რელიზ კანდიდატი F-Droid აპლიკაციიდან</string>
<string name="community_rc_play_store">მიიღეთ რელიზ კანდიდატი Google Play-დან</string>
<string name="community_release_candidate_headline">რელიზის კანდიდატი</string>
<string name="community_release_candidate_text">ეს რელიზის კანდიდატი (რკ) დამდეგი რელიზის კადრია და მოსალოდნელია მისი სტაბილურობა. ამის დამოწმებაში დაგვეხმარება ინდივიდუალური მოწყობილობის შემოწმება. შემოწმებისთვის დარეგისტრირდით Play-ზე ან გადახედეთ \"ვერსიის\" სექციას F-Droid-ში.</string>
<string name="community_testing_bug_text">იპოვეთ შეცდომა? უცნაურობა?</string>
<string name="community_testing_headline">დაგვეხმარეთ შემოწმებით</string>
<string name="community_testing_report_text">დაამატეთ მოხსენიება GitHub-ზე</string>
<string name="confirmation_remove_file_alert">ნამდვილად გსურთ %1$s-ის გაუქმება?</string>
<string name="confirmation_remove_files_alert">დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერების გაუქმება?</string>
<string name="confirmation_remove_folder_alert">ნამდვილად გსურთ %1$s-ის და შემცველობის გაუქმება?</string>
<string name="confirmation_remove_folders_alert">დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერებისა და მათი შემცველობის გაუქმება?</string>
<string name="confirmation_remove_local">მხოლოდ ლოკალური</string>
<string name="contactlist_item_icon">მომხმარებლის პიქტოგრამა კონტაქტების სიისთვის</string>
<string name="contactlist_no_permission">უფლებები არაა მოცემული, არაფერი არ იქნა იმპორტირებული.</string>
<string name="contacts">კონტაქტები</string>
<string name="contacts_backup_button">ახალი ბექაფი</string>
<string name="contacts_preferences_backup_scheduled">ბექაფი დაინიშნა და მალე დაიწყება</string>
<string name="contacts_preferences_import_scheduled">იმპორტი დაინიშნა და მალე დაიწყება</string>
<string name="contacts_preferences_no_file_found">ფაილი ვერ იქნა ნაპოვნი</string>
<string name="contacts_preferences_something_strange_happened">თქვენი ბოლო ბექაფი ვერ იქნა ნაპოვნი!</string>
<string name="copied_to_clipboard">კოპირებულია კლიპბორდში</string>
<string name="copy_file_error">ამ ფაილისა თუ დირექტორიის კოპირებისას წარმოიშვა შეცდომა</string>
<string name="copy_file_invalid_into_descendent">დირექტორიის კოპირება მასში არსებულ დირექტორიაში შეუძლებელია</string>
<string name="copy_file_invalid_overwrite">ფაილი დანიშნულ დირექტორიაში უკვე არსებობს</string>
<string name="copy_link">ბმულის კოპირება</string>
<string name="copy_move_to_encrypted_folder_not_supported">ამჟამად კოპირება/გადატანა დაშიფრულ დირექტორიაში არაა მხარდაჭერილი.</string>
<string name="create">შექმნა</string>
<string name="create_dir_fail_msg">დირექტორია ვერ შეიქმნა</string>
<string name="delete_account">ანგარიშის გაუქმება</string>
<string name="deselect_all">ყველას წაშლა</string>
<string name="dev_version_new_version_available">ხელმისაწვდომია ახალი ვერსია</string>
<string name="dev_version_no_new_version_available">ახალი ვერსია არაა ხელმისაწვდომი.</string>
<string name="dialog_close">დახურვა</string>
<string name="digest_algorithm_not_available">თქვენს ტელეფონს ამ ალგორითმის დაიჯესტის მხარდაჭერა არ აქვს. </string>
<string name="disable_new_media_folder_detection_notifications">გამორთვა</string>
<string name="dismiss">დათხოვნა</string>
<string name="done">დასრულებულია</string>
<string name="downloader_download_failed_content">ვერ ჩამოიტვირთა %1$s</string>
<string name="downloader_download_failed_credentials_error">ჩამოტვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია</string>
<string name="downloader_download_failed_ticker">ჩამოტვირთვა ვერ განხორციელდა</string>
<string name="downloader_download_file_not_found">ფაილი სერვერზე ხელმისაწვდომი აღარაა</string>
<string name="downloader_download_in_progress_content">%1$d%% ჩამოტვირთვა %2$s</string>
<string name="downloader_download_succeeded_content">%1$s ჩამოტვირთლია</string>
<string name="downloader_download_succeeded_ticker">ჩამოტვირთულია</string>
<string name="downloader_not_downloaded_yet">ჯერ ვერ ჩამოიტვირთა</string>
<string name="drawer_header_background">მხაზველი დასათაურების ფონური სურათი</string>
<string name="drawer_item_activities">აქტივობები</string>
<string name="drawer_item_all_files">ყველა ფაილი</string>
<string name="drawer_item_favorites">რჩეულები</string>
<string name="drawer_item_home">სახლი</string>
<string name="drawer_item_notifications">შეტყობინებები</string>
<string name="drawer_item_on_device">მოწყობილობაზე</string>
<string name="drawer_item_recently_modified">ახლად შეცვლილი</string>
<string name="drawer_item_shared">გაზიარებული</string>
<string name="drawer_item_trashbin">გაუქმებული ფაილები</string>
<string name="drawer_item_uploads_list">ატვირთვები</string>
<string name="drawer_logout">გასვლა</string>
<string name="drawer_quota">გამოყენებულია %1$s სულ %2$s-იდან </string>
<string name="drawer_synced_folders">ავტო-ატვირთვა</string>
<string name="ecosystem_apps_display_more">უფრო მეტი</string>
<string name="ecosystem_apps_display_notes">ჩანაწერები</string>
<string name="ecosystem_apps_display_talk">საუბარი</string>
<string name="encrypted">დააყენეთ როგორც დაშიფრული</string>
<string name="end_to_end_encryption_confirm_button">შიფრაციის დაყენება</string>
<string name="end_to_end_encryption_dialog_close">დახურვა</string>
<string name="end_to_end_encryption_enter_password">პირადი გასაღების დეშიფრაციისთვის გთხოვთ შეიყვანოთ პაროლი.</string>
<string name="end_to_end_encryption_folder_not_empty">ეს დირექტორია არაა ცარიელი</string>
<string name="end_to_end_encryption_keywords_description">12 სიტყვა ერთად წარმოქმნის ძალიან ძლიერ პაროლს, იძლევა თქვენი დაშიფრული ფაილების ჩვენებისა და მოხმარების უფლებას. გთხოვთ ჩაიწეროთ და შეინახოთ დაცულ ადგილას.</string>
<string name="end_to_end_encryption_passphrase_title">შენიშნეთ თქვენი 12 სიტყვიანი შიფრაციის პაროლი</string>
<string name="end_to_end_encryption_storing_keys">გასაღებების შენახვა</string>
<string name="end_to_end_encryption_title">შიფრაციის დაყენება</string>
<string name="end_to_end_encryption_unsuccessful">გასაღებების შენახვა ვერ მოხერხდა, გთხოვთ სცადოთ ახლიდან.</string>
<string name="end_to_end_encryption_wrong_password">შეცდომა დეშიფრაციისას. არასწორი პაროლი?</string>
<string name="error__upload__local_file_not_copied">%1$s ვერ კოპირდება %2$s ლოკალურ დირექტორიაში</string>
<string name="error_cant_bind_to_operations_service">კრიტიკული შეცდომა: ოპერაციების განხორციელება შეუძლებელია</string>
<string name="etm_accounts">ანგარიშები</string>
<string name="etm_background_job_created">შექმნილია</string>
<string name="etm_background_job_user">მომხმარებელი</string>
<string name="etm_background_jobs">ფონური საქმეები</string>
<string name="etm_transfer_type_download">ჩამოტვირთვა</string>
<string name="etm_transfer_type_upload">ატვირთვა</string>
<string name="favorite">რჩეულებში დამატება</string>
<string name="favorite_icon">რჩეული</string>
<string name="file_delete">წაშლა</string>
<string name="file_icon">ფაილი</string>
<string name="file_list_empty">ატვირთეთ რამე ან გაუწიეთ თქვნს მოწყობილობას სინქრონიზაცია.</string>
<string name="file_list_empty_favorite_headline">რჩეულებში ჯერ არაფერი დამატებულია</string>
<string name="file_list_empty_headline">აქ ფაილები არაა</string>
<string name="file_list_empty_headline_search">ამ დირექტორიაში შედეგები არაა</string>
<string name="file_list_empty_headline_server_search">შედეგები არაა</string>
<string name="file_list_empty_moving">აქ არაფერია. შეგიძლიათ დაამატოთ დირექტორია.</string>
<string name="file_list_empty_recently_modified">გასული 7 დღის განმავლობაში შეცვლილი ფაილ(ებ)ი ვერ იქნა ნაპოვნი</string>
<string name="file_list_empty_search">იქნებ სხვა დირექტორიაშია?</string>
<string name="file_list_empty_shared_headline">ჯერ არაფერი გაზიარებულა</string>
<string name="file_list_folder">დირექტორია</string>
<string name="file_list_loading">იტვირთება…</string>
<string name="file_list_no_app_for_file_type">ამ ფაილთან სამუშაოდ არც ერთი აპლიკაცია არაა დაყანებული.</string>
<string name="file_list_seconds_ago">წამის წინ</string>
<string name="file_migration_directory_already_exists">მონაცემების დირექტორია უკვე არსებობს. აირჩიეთ ერთ-ერთი შემდეგიდან:</string>
<string name="file_migration_failed_dir_already_exists">Nextcloud დირექტორია უკვე არსებობს</string>
<string name="file_migration_failed_not_enough_space">საჭიროა მეტი სივრცე</string>
<string name="file_migration_failed_not_readable">წყარო-ფაილის წაკითხვა ვერ მოხერხდა</string>
<string name="file_migration_failed_not_writable">სამიზნე ფაილში ჩაწერა ვერ მოხერხდა</string>
<string name="file_migration_failed_while_coping">მიგრაციისას წარმოიქმნა შეცდომა</string>
<string name="file_migration_failed_while_updating_index">ინდექსის განახლება ვერ მოხერხდა</string>
<string name="file_migration_ok_finished">დასრულებულია</string>
<string name="file_migration_override_data_folder">ჩანაცვლება</string>
<string name="file_migration_source_not_readable_title">წყარო-დირექტორია ვერ იკითხება!</string>
<string name="file_migration_use_data_folder">მოხმარება</string>
<string name="file_not_found">ფაილი ვერ იქნა ნაპოვნი</string>
<string name="file_not_synced">ფაილის სინქ ვერ მოხერხდა. ნაჩვენებია ბოლო ხელმისაწვდომი ვარიანტი.</string>
<string name="file_rename">სახელის შეცვლა</string>
<string name="filedetails_details">დეტალები</string>
<string name="filedetails_download">ჩამოტვირთვა</string>
<string name="filedetails_export">ექსპორტი</string>
<string name="filedetails_renamed_in_upload_msg">ატვირთვისას ფაილს სახელი შეეცვალა %1$s-ზე</string>
<string name="filedetails_sync_file">სინქ</string>
<string name="filedisplay_no_file_selected">ფაილი არაა არჩეული</string>
<string name="filename_empty">ფაილის სახელი ვერ იქნება ცარიელი</string>
<string name="filename_forbidden_characters">აკრძალული ნიშნები: / \\ &lt; &gt; : \" | ? *</string>
<string name="filename_forbidden_charaters_from_server">ფაილის სახელი შეიცავს მინ. 1 არასწორ ნიშანს</string>
<string name="filename_hint">ფაილის სახელი</string>
<string name="folder_confirm_create">შექმნა</string>
<string name="folder_list_empty_headline">აქ დირექტორიები არაა</string>
<string name="folder_picker_choose_button_text">აირჩიეთ</string>
<string name="folder_picker_move_button_text">გადატანა</string>
<string name="forbidden_permissions">თქვენ არ გაქვთ უფლება, %s</string>
<string name="forbidden_permissions_copy">რომ დააკოპიროთ ეს ფაილი</string>
<string name="forbidden_permissions_create">შექმნათ ეს ფაილი</string>
<string name="forbidden_permissions_delete">გააუქმოთ ეს ფაილი</string>
<string name="forbidden_permissions_move">რომ გადაიტანოთ ეს ფაილი</string>
<string name="forbidden_permissions_rename">შეუცვალოთ ამ ფაილს სახელი</string>
<string name="foreground_service_upload">ფაილების ატვირთვა...</string>
<string name="foreign_files_fail">გარკვეული ფაილების გადატანა ვერ მოხერხდა</string>
<string name="foreign_files_local_text">ლოკალური: %1$s</string>
<string name="foreign_files_move">ყველას გადატანა</string>
<string name="foreign_files_remote_text">დისტანციური: %1$s</string>
<string name="foreign_files_success">ყველა ფაილი გადატანილია</string>
<string name="forward">წინ</string>
<string name="fourHours">4 საათი</string>
<string name="hint_name">სახელი</string>
<string name="hint_password">პაროლი</string>
<string name="instant_upload_on_charging">ატვირთვა მხოლოდ დატენვისას</string>
<string name="instant_upload_path">/მისიერი_ატვირთვა</string>
<string name="invisible">უჩინარი</string>
<string name="link">ბმული</string>
<string name="link_share_allow_upload_and_editing">ატვირთვისა და ცვლილების უფლებების მინიჭება</string>
<string name="link_share_file_drop">ფაილის ჩაგდება (მხოლოდ ატვირთვა)</string>
<string name="list_layout">ჩამოწერილი მაკეტი</string>
<string name="local_file_list_empty">ამ დირექტორიაში ფაილები არაა.</string>
<string name="local_file_not_found_message">ლოკალურ ფაილ-სისტემაში ფაილი ვერ მოიძებნა</string>
<string name="local_folder_list_empty">მომდევნო დირექტორიები არაა.</string>
<string name="log_send_mail_subject">%1$s Android აპლიკაციის ლოგები.</string>
<string name="login">ლოგინი</string>
<string name="logs_menu_refresh">განახლება</string>
<string name="logs_status_loading">იტვირთება…</string>
<string name="logs_title">ლოგები</string>
<string name="manage_space_clear_data">მონაცემების გასუფთავება</string>
<string name="manage_space_description"> %1$s-ის მონაცემებიდან პარამეტრები, მონაცემთა ბაზა და სერტიფიკატები სამუდამოდ წაიშლება.\n\nგადმოწერილი ფაილები დარჩება ხელუხლებელი.\n\nეს პროცესი გასტანს გარკვეულ დროს.</string>
<string name="manage_space_title">მოცულობის მენეჯმენტი</string>
<string name="media_err_io">მედია ფაილის წაკითხვა ვერ მოხერხდა</string>
<string name="media_err_malformed">მედია ფაილს გააჩნია არასწორი კოდირება</string>
<string name="media_err_timeout">ფაილის დაკვრის მცდელობის დრო ამოიწურა</string>
<string name="media_err_unknown">ჩაშენებული მედია დამკვრელი ვერ უკრავს მედია ფაილს</string>
<string name="media_err_unsupported">მედია კოდეკი მხარდაუჭერელია</string>
<string name="media_forward_description">სწრაფი გადახვევის ღილაკი</string>
<string name="media_notif_ticker">%1$s მუსიკის დამკვრელი</string>
<string name="media_play_pause_description">დაკვრის ან პაუზის ღილაკი</string>
<string name="media_rewind_description">გადახვევის ღილაკი</string>
<string name="media_state_playing">%1$s (იკვრება)</string>
<string name="menu_item_sort_by_date_newest_first">ჯერ ახალი</string>
<string name="menu_item_sort_by_date_oldest_first">ჯერ ძველი</string>
<string name="menu_item_sort_by_name_a_z">ა - ჰ</string>
<string name="menu_item_sort_by_name_z_a">ჰ - ა</string>
<string name="menu_item_sort_by_size_biggest_first">ჯერ დიდი</string>
<string name="menu_item_sort_by_size_smallest_first">ჯერ პატარა</string>
<string name="more">მეტი</string>
<string name="move_file_error">ამ ფაილისა თუ დირექტორიის გადატანისას წარმოიშვა შეცდომა</string>
<string name="move_file_invalid_into_descendent">დირექტორიის გადატანა მასში არსებულ დირექტორიაში შეუძლებელია</string>
<string name="move_file_invalid_overwrite">ფაილი დანიშნულ დირექტორიაში უკვე არსებობს</string>
<string name="network_error_socket_exception">სერვერთან დაკავშირებისას წარმოიქმნა შეცდომა</string>
<string name="new_media_folder_photos">ფოტო</string>
<string name="notification_channel_download_description">აჩვენებს გადმოწერის პროგრესს</string>
<string name="notification_channel_download_name_short">ჩამოტვირთვები</string>
<string name="notification_channel_file_sync_description">აჩვენებს ფაილების სინქ. პროგრესს და შედეგებს</string>
<string name="notification_channel_file_sync_name">ფაილების სინქ.</string>
<string name="notification_channel_media_description">მუსიკის დამკვრელის პროგრესი</string>
<string name="notification_channel_media_name">მედია დამკვრელი</string>
<string name="notification_channel_upload_description">აჩვენებს ატვირთვის პროგრესს</string>
<string name="notification_channel_upload_name_short">ატვირთვები</string>
<string name="notifications_no_results_headline">შეტყობინებები არაა</string>
<string name="notifications_no_results_message">გთხოვთ დაბრუნდეთ მოგვინაებით.</string>
<string name="oneHour">1 საათი</string>
<string name="pass_code_configure_your_pass_code">შეიყვანეთ თქვენი პასკოდი</string>
<string name="pass_code_configure_your_pass_code_explanation">პასკოდი მოთხოვნილ იქნება აპლიკაციის ყოველი გაშვებისას</string>
<string name="pass_code_enter_pass_code">გთხოვთ შეიყვანოთ თქვენი პასკოდი</string>
<string name="pass_code_mismatch">პასკოდები ერთმანეთს არ ემთხვევა</string>
<string name="pass_code_reenter_your_pass_code">გთხოვთ თქვენი პასკოდი შეიყვანოთ ახლიდან</string>
<string name="pass_code_remove_your_pass_code">თქვენი პასკოდის გაუქმება</string>
<string name="pass_code_removed">პასკოდი გაუქმდა</string>
<string name="pass_code_stored">პასკოდი შენახულია</string>
<string name="pass_code_wrong">არასწორი პასკოდი</string>
<string name="permission_storage_access">ფაილების ატვირთვისა და გადმოწერისათვის საჭიროა დამატებითი უფლებები.</string>
<string name="picture_set_as_no_app">სურათის დასაყენებლად ვერც ერთი აპლიკაცია ვერ იქნა ნაპოვნი</string>
<string name="placeholder_fileSize">389 კბ</string>
<string name="placeholder_filename">ადგილი.txt</string>
<string name="placeholder_media_time">12:23:45</string>
<string name="placeholder_sentence">ეს არის ადგილი</string>
<string name="placeholder_timestamp">18/05/2012 12:23</string>
<string name="pref_behaviour_entries_delete_file">გაუქმებულია</string>
<string name="pref_behaviour_entries_keep_file">დარჩა ორიგინალ დირექტორიაში</string>
<string name="pref_behaviour_entries_move">გადატანილია აპლიკაციის დირექტორიაში</string>
<string name="prefs_add_account">ანგარიშის დამატება</string>
<string name="prefs_calendar_contacts_no_store_error">არც Google Play-ა დაყენებული, არც F-Droid-ი</string>
<string name="prefs_category_about">ჩვენს შესახებ</string>
<string name="prefs_category_details">დეტალები</string>
<string name="prefs_category_dev">დევ.</string>
<string name="prefs_category_general">ზოგადი</string>
<string name="prefs_category_more">მეტი</string>
<string name="prefs_daily_contact_backup_summary">კონტაქტების ყოველდღიური ბექაფი</string>
<string name="prefs_gpl_v2">GNU ზოგადი ღია ლიცენზია, ვერსია 2</string>
<string name="prefs_help">დახმარება</string>
<string name="prefs_imprint">ბეჭედი</string>
<string name="prefs_instant_upload_path_use_subfolders_title">ქვე-დირექტორიების მოხმარება</string>
<string name="prefs_license">ლიცენზია</string>
<string name="prefs_lock_none">არც ერთი</string>
<string name="prefs_manage_accounts">ანგარიშების მართვა</string>
<string name="prefs_show_hidden_files">დამალული ფაილების ჩვენება</string>
<string name="prefs_sourcecode">მიიღეთ კოდი</string>
<string name="prefs_synced_folders_local_path_title">ლოკალური დირექტორია</string>
<string name="prefs_synced_folders_remote_path_title">დისტანციური დირექტორია</string>
<string name="prefs_theme_title">ვიზუალური თემა</string>
<string name="prefs_value_theme_dark">მუქი</string>
<string name="prefs_value_theme_light">ღია</string>
<string name="preview_image_description">სურათის წინასწარი ჩვენება</string>
<string name="preview_image_error_no_local_file">წინასწარი ჩვენებისთვის ლოკალური ფაილი არაა</string>
<string name="preview_image_error_unknown_format">სურათის ჩვენება შეუძლებელია</string>
<string name="preview_sorry">ბოდიში</string>
<string name="privacy">კონფიდენციალურობა</string>
<string name="push_notifications_not_implemented">Google Play სერვისების მესაკუთრეობრივი დამოკიდებულებების გამო, ფუშ შეტყობინებები გათიშულია.</string>
<string name="push_notifications_old_login">ვადაგასული აუტენტიფიკაციის გამო ფუშ შეტყობინებები არაა. გთხოვთ გაითვალისწინოთ თქვენი ანგარიშის თავიდან დამატება.</string>
<string name="push_notifications_temp_error">ფუშ შეტყობინებები ამჟამად ხელმიუწვდომელია.</string>
<string name="recommend_subject">სცდეთ %1$s თქვენს მოწყობილობაზე!</string>
<string name="recommend_text">გიწვევთ გამოიყენოთ %1$s თქვენს მოწყობილობაზე.\nჩამოტვირთეთ აქედან: %2$s</string>
<string name="recommend_urls">%1$s ან %2$s</string>
<string name="reload">გადატვირთვა</string>
<string name="remove_fail_msg">გაუქმება ვერ მოხერხდა</string>
<string name="remove_push_notification">გაუქმება</string>
<string name="remove_success_msg">გაუქმდა</string>
<string name="rename_dialog_title">შეიყვანეთ ახალი სახელი</string>
<string name="rename_local_fail_msg">ლოკალური ასლის სახელის გადარქმევა ვერ მოხერხდა, სცადეთ სხვა სახელი</string>
<string name="rename_server_fail_msg">გადარქმევა შეუძლებელია, სახელი უკვე დაკავებულია</string>
<string name="reshare_not_allowed">ხელახალი გაზიარება არაა დაშვებული</string>
<string name="resharing_is_not_allowed">ხელახალი გაზიარება არაა ნებადართული</string>
<string name="resized_image_not_possible_download">ზომა-შეცვლილი სურათი არაა ხელმისაწვდომი. გადმოვწეროთ მთლიანი სურათი?</string>
<string name="select_all">ყველას მონიშვნა</string>
<string name="send">გაგზავნა</string>
<string name="set_as">დააყენეთ როგორც</string>
<string name="set_picture_as">გამოიყენეთ სურათი როგორც</string>
<string name="share">გაზიარება</string>
<string name="share_dialog_title">გაზიარება</string>
<string name="share_file">%1$s-ის გაზიარება</string>
<string name="share_group_clarification">%1$s (ჯგუფი)</string>
<string name="share_link">ბმულის გაზიარება</string>
<string name="share_link_empty_password">უნდა შეიყვანოთ პაროლი</string>
<string name="share_link_forbidden_permissions">გააზიაროთ ეს ფაილი</string>
<string name="share_link_password_title">შეიყვანეთ პაროლი</string>
<string name="share_no_expiration_date_label">მიუთითეთ ვადის გასვლის დრო</string>
<string name="share_no_password_title">პაროლის დაყენება</string>
<string name="share_permission_can_edit">შეუძლია შეცვალოს</string>
<string name="share_remote_clarification">%1$s (დისტანციური)</string>
<string name="share_settings">პარამეტრები</string>
<string name="share_via_link_section_title">ბმულის გაზიარება</string>
<string name="shared_icon_shared">გაზიარებული</string>
<string name="sort_by">დაასორტირე</string>
<string name="ssl_validator_btn_details_hide">დამალვა</string>
<string name="ssl_validator_btn_details_see">დეტალები</string>
<string name="ssl_validator_header">სერვერის იდენტურობა ვერ დამოწმდა</string>
<string name="ssl_validator_label_C">ქვეყანა:</string>
<string name="ssl_validator_label_CN">ზოგადი სახელი:</string>
<string name="ssl_validator_label_L">ადგილმდებარეობა:</string>
<string name="ssl_validator_label_O">ორგანიზაცია:</string>
<string name="ssl_validator_label_OU">ორგანიზაციის განყოფილება:</string>
<string name="ssl_validator_label_ST">შტატი:</string>
<string name="ssl_validator_label_certificate_fingerprint">თითის ანაბეჭდი:</string>
<string name="ssl_validator_label_issuer">გაცემულია მიერ:</string>
<string name="ssl_validator_label_signature">ხელმოწერა:</string>
<string name="ssl_validator_label_signature_algorithm">ალგორითმი:</string>
<string name="ssl_validator_label_subject">გაცემულია:</string>
<string name="ssl_validator_label_validity">მოქმედების ვადა:</string>
<string name="ssl_validator_label_validity_from">ვისგან:</string>
<string name="ssl_validator_label_validity_to">ვის:</string>
<string name="ssl_validator_no_info_about_error">- ამ შეცდომაზე ინფორმაცია არაა</string>
<string name="ssl_validator_not_saved">სერტიფიკატის შენახვა ვერ მოხერხდა</string>
<string name="ssl_validator_null_cert">სერტიფიკატის ჩვენება შეუძლებელია.</string>
<string name="ssl_validator_question">გსურთ მაინც ენდოთ ამ სერტიფიკატს?</string>
<string name="ssl_validator_reason_cert_expired">- სერვერის სერტიფიკატი ვადაგასულია</string>
<string name="ssl_validator_reason_cert_not_trusted">- სერვერის სერტიფიკატი არ არის სანდო</string>
<string name="ssl_validator_reason_cert_not_yet_valid">- სერვერის სერტიფიკატის თარიღები მომავალშია</string>
<string name="ssl_validator_reason_hostname_not_verified">- URL არ ემთხვევა სერტიფიკატში არსებულ ჰოსტნეიმს</string>
<string name="storage_description_default">საწყისი</string>
<string name="storage_downloads">ჩამოტვირთვები</string>
<string name="subject_shared_with_you">გაგიზიარდათ \"%1$s\"</string>
<string name="subject_user_shared_with_you">%1$s-სგან გაგიზიარდათ \"%2$s\"</string>
<string name="sync_conflicts_in_favourites_ticker">წარმოიშვა კონფლიქტი </string>
<string name="sync_current_folder_was_removed">დირექტორია %1$s აღარ არსებობს</string>
<string name="sync_fail_content">%1$s სინქ ვერ მოხერხდა</string>
<string name="sync_fail_content_unauthorized">%1$s რესურსის არასწორი პაროლი</string>
<string name="sync_fail_in_favourites_ticker">Kept-in-sync ფაილების შეცდომა</string>
<string name="sync_fail_ticker">სინქრონიზაცია დასრუდლა წარუმატებლად</string>
<string name="sync_fail_ticker_unauthorized">სინქ ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია</string>
<string name="sync_file_nothing_to_do_msg">ფაილების შემცველობა სინქრონიზირებულია</string>
<string name="sync_foreign_files_forgotten_explanation">როდესაც ერთი ფაილი სინქრონიზირებულია რამოდენიმე ანგარიშთან, 1.3.16 ვერსიაში მონაცემების დაკარგვისგან თავის ასარიდებლად, ამ მოწყობილობით ატვირთული ფაილ(ებ)ი კოპირდება %1$s დირექტორიაში.\n\nამ ცვლილების გამო, აპლიკაციის ძველი ვერსიით ატვირთული ყველა ფაილი დაკოპირდა %2$s დირექტორიაში. თუმცა, შეცდომის გამო სინქრონიზაციისას მოხდა ამ ოპერაციის შეჩერება. შეგიძლიათ ფაილ(ებ)ი დატოვოთ ისე როგორც არის და წაშალოთ ბმული %3$s-სთან, ან გადაიტანოთ ფაილ(ებ)ი %1$s დირექტორიაში და შეინარჩუნოთ ბმული %4$s-ისთვის.\n\nქვემოთ ჩამოწერილია ლოკალური და %5$s-ში არსებული დისტანციური ფაილ(ებ)ი.</string>
<string name="sync_foreign_files_forgotten_ticker">რამოდენიმე ლოკალური ფაილი გამორჩენილია</string>
<string name="sync_in_progress">ამოღებულია ფაილის უახლესი ვერსია.</string>
<string name="sync_status_button">სინქ. სტატუსის ღილაკი</string>
<string name="sync_string_files">ფაილები</string>
<string name="synced_folder_settings_button">პარამეტრების ღილაკი</string>
<string name="synced_folders_configure_folders">დირექტორიების კონფიგურაცია</string>
<string name="synced_folders_new_info">მყისიერი ატვირთვა სრულიად გამოსწორდა. ავტო-ატვირთვას რეკონფიგურაცია გაუწიეთ მთავარი მენიუდან.\n\nისიამოვნეთ ახალი და გაუმჯობესებული ავტო-ატვირთვით.</string>
<string name="synced_folders_preferences_folder_path">%1$s-ისთვის</string>
<string name="synced_folders_type">სახეობა</string>
<string name="tags">ტეგები</string>
<string name="test_server_button">სერვერთან კავშირის შემოწმება</string>
<string name="thirtyMinutes">30 წუთი</string>
<string name="thisWeek">ამ კვირაში</string>
<string name="thumbnail">მინიატურა</string>
<string name="today">დღეს</string>
<string name="trashbin_activity_title">გაუქმებული ფაილები</string>
<string name="trashbin_empty_headline">წაშლილი ფაილები ვერ მოიძებნა</string>
<string name="trashbin_file_remove">სამუდამოდ წაშლა</string>
<string name="unset_encrypted">შიფრაციის გაუქმება</string>
<string name="unshare_link_forbidden_permissions">შეწყვიტოთ ამ ფაილის გაზიარება</string>
<string name="untrusted_domain">წვდომა არასანდო დომენით. დამატებითი ინფორმაციისთვის გთხოვთ იხილოთ დოკუმენტაცია.</string>
<string name="update_link_forbidden_permissions">განაახლოთ ეს გაზიარება</string>
<string name="upload_action_failed_clear">წარუმატებლად დასრულებული ატვირთების გასუფთავება</string>
<string name="upload_action_failed_retry">წარუმატებლად დასრულებული ატვირთების ხელახლა გაშვება</string>
<string name="upload_file_dialog_filename">ფაილის სახელი</string>
<string name="upload_file_dialog_filetype">ფაილის სახეობა</string>
<string name="upload_file_dialog_filetype_googlemap_shortcut">Google Maps იარლიყის ფაილი(%s)</string>
<string name="upload_file_dialog_filetype_internet_shortcut">ინტერნეტ იარლიყის ფაილი(%s)</string>
<string name="upload_file_dialog_filetype_snippet_text">ტექსტ ფაილის ჩამონაჭერი(.txt)</string>
<string name="upload_file_dialog_title">ატვირთვისთვის შეიყვანეთ ფაილის სახელი და სახეობა</string>
<string name="upload_files">ფაილების ატვირთვა</string>
<string name="upload_item_action_button">ელემენტის ატვირთვის ქმედების ღილაკი</string>
<string name="upload_list_delete">წაშლა</string>
<string name="upload_list_empty_headline">არც ერთი ატვირთვა არაა ხელმისაწვდომი.</string>
<string name="upload_list_empty_text_auto_upload">ატვირთეთ რაიმე ან გაააქტიურეთ ავტო-ატვირთვა.</string>
<string name="upload_query_move_foreign_files">არასაკმარისი ადგილი აფერხებს არჩეული ფაილების კოპირებას %1$s დირექტორიაში. გსურთ სანაცვლოდ მათი გადატანა?</string>
<string name="upload_unknown_error">უცნობი შეცდომა</string>
<string name="uploader_btn_alternative_text">აირჩიეთ</string>
<string name="uploader_btn_upload_text">ატვირთვა</string>
<string name="uploader_error_message_no_file_to_upload">მიღებული მონაცემები არ მოიცავდნენ სწორ ფაილს.</string>
<string name="uploader_error_message_read_permission_not_granted">%1$s მომხმარებელს არ აქვს უფლება წაიკითხოს ფაილი.</string>
<string name="uploader_error_message_source_file_not_copied">ფაილის ასლი დროებით დირექტორიაში ვერ შეიქმნა. სცადეთ მისი ახლიდან გაგზავნა.</string>
<string name="uploader_error_message_source_file_not_found">ატვირთვისთვის არჩეული ფაილი ვერ იქნა ნაპოვნი. გთხოვთ დაამოწმოთ ფაილის არსებობა.</string>
<string name="uploader_error_title_no_file_to_upload">ფაილი ატვირთვისთვის არაა</string>
<string name="uploader_info_dirname">დირექტორიის სახელი</string>
<string name="uploader_top_message">აირჩიეთ ატვირთვის დირექტორია</string>
<string name="uploader_upload_failed_content_single">%1$s ვერ აიტვირთა</string>
<string name="uploader_upload_failed_credentials_error">ატვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია</string>
<string name="uploader_upload_failed_ticker">ატვირთვა ვერ განხორციელდა</string>
<string name="uploader_upload_files_behaviour">ატვირთვის ოპციონი:</string>
<string name="uploader_upload_files_behaviour_only_upload">ფაილის საწყის დირექტორიაში დატოვება</string>
<string name="uploader_upload_files_behaviour_upload_and_delete_from_source">ფაილის საწყისი დირექტორიიდან გაუქმება</string>
<string name="uploader_upload_forbidden_permissions">ატვირთოთ ამ დირექტორიაში</string>
<string name="uploader_upload_in_progress_content">%1$d%% ატვირთვა %2$s</string>
<string name="uploader_upload_in_progress_ticker">იტვირთება...</string>
<string name="uploader_upload_succeeded_content_single">ატვირთულია %1$s</string>
<string name="uploader_wrn_no_account_quit_btn_text">გამოსვლა</string>
<string name="uploader_wrn_no_account_setup_btn_text">დაყენება</string>
<string name="uploader_wrn_no_account_text">ანგარიშები %1$s თქვენს მოწყობილობაზე არაა. გთხოვთ დააყენოთ ანგარიში.</string>
<string name="uploader_wrn_no_account_title">ანგარიში ვერ მოიძებნა</string>
<string name="uploads_view_group_current_uploads">მიმდინარე</string>
<string name="uploads_view_group_failed_uploads">ვერ მოხერხებული/მიმდინარე რესტარტი</string>
<string name="uploads_view_group_finished_uploads">ატვირთულია</string>
<string name="uploads_view_group_manually_cancelled_uploads">უარყოფილია</string>
<string name="uploads_view_later_waiting_to_upload">ველოდებით ატვირთვას</string>
<string name="uploads_view_title">ატვირთვები</string>
<string name="uploads_view_upload_status_cancelled">უარყოფილია</string>
<string name="uploads_view_upload_status_conflict">კონფლიქტი</string>
<string name="uploads_view_upload_status_failed_connection_error">კავშირის პრობლემა</string>
<string name="uploads_view_upload_status_failed_credentials_error">შეცდომა უფლებამოსილებებში</string>
<string name="uploads_view_upload_status_failed_file_error">ფაილის შეცდომა</string>
<string name="uploads_view_upload_status_failed_folder_error">დირექტორიის შეცდომა</string>
<string name="uploads_view_upload_status_failed_localfile_error">ლოკალური ფაილი ვერ იქნა ნაპოვნი</string>
<string name="uploads_view_upload_status_failed_permission_error">შეცდომა უფლებებში</string>
<string name="uploads_view_upload_status_failed_ssl_certificate_not_trusted">სერვერის არასანდო სერტიფიკატი</string>
<string name="uploads_view_upload_status_service_interrupted">აპლიკაცია შეჩერდა</string>
<string name="uploads_view_upload_status_succeeded">დასრულებულია</string>
<string name="uploads_view_upload_status_unknown_fail">უცნობი შეცდომა</string>
<string name="uploads_view_upload_status_waiting_exit_power_save_mode">ველოდებით ეკონომიური რეჟიმიდან გამოსვლას</string>
<string name="uploads_view_upload_status_waiting_for_charging">გადახდის მოლოდინშია</string>
<string name="user_icon">მომხმარებელი</string>
<string name="user_info_address">მისამართი</string>
<string name="user_info_email">ელ-ფოსტა</string>
<string name="user_info_phone">ტელეფონის ნომერი</string>
<string name="user_info_twitter">Twitter-ი</string>
<string name="user_info_website">ვებ-საიტი</string>
<string name="user_information_retrieval_error">მოხმარებლის ინფორმაციის მიღებისას წარმოიშვა შეცდომა</string>
<string name="userinfo_no_info_headline">პირადი ინფორმაციის კომპლექტი არაა</string>
<string name="userinfo_no_info_text">დაამატეთ სახელი, ფოტო და საკონტაქტო დეტალები თქვენს პროფილის გვერდზე.</string>
<string name="username">მომხმარებლის სახელი</string>
<string name="version_dev_download">ჩამოტვირთვა</string>
<string name="wait_checking_credentials">მოწმდება შენახული უფლებამოსილებანი</string>
<string name="wait_for_tmp_copy_from_private_storage">ფაილის კოპირება პირადი საცავიდან</string>
<string name="webview_version_check_alert_dialog_positive_button_title">განახლება</string>
<string name="what_s_new_image">რა არის ახალი სურათი</string>
<string name="whats_new_skip">გამოტოვება</string>
<string name="whats_new_title">ახალი %1$s-ში</string>
<string name="write_email">ელ-წერილის გაგზავნა</string>
</resources>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Account creation failed</string>
<string name="account_icon">Account icon</string>
<string name="account_not_found">Account not found!</string>
<string name="action_clear_failed_uploads">Clear failed uploads</string>
<string name="action_edit">Edit</string>
<string name="action_empty_notifications">Clear all notifications</string>
<string name="action_empty_trashbin">Empty trash bin</string>
@ -152,7 +151,6 @@
<string name="confirmation_remove_folder_alert">Do you really want to delete %1$s and the contents thereof?</string>
<string name="confirmation_remove_folders_alert">Do you really want to delete the selected items and their contents?</string>
<string name="confirmation_remove_local">Local only</string>
<string name="conflict_dialog_error">Error creating conflict dialog!</string>
<string name="conflict_file_headline">Conflicting file %1$s</string>
<string name="conflict_local_file">Local file</string>
<string name="conflict_message_description">If you select both versions, the local file will have a number appended to its name.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">계정 생성에 실패했습니다.</string>
<string name="account_icon">계정 아이콘</string>
<string name="account_not_found">계정을 찾을 수 없습니다!</string>
<string name="action_clear_failed_uploads">실패한 업로드 삭제</string>
<string name="action_edit">편집</string>
<string name="action_empty_notifications">모든 알림 지우기</string>
<string name="action_empty_trashbin">휴지통 비우기</string>
@ -154,7 +153,6 @@
<string name="confirmation_remove_folder_alert">%1$s 및 포함된 모든 내용을 삭제하시겠습니까?</string>
<string name="confirmation_remove_folders_alert">선택한 항목과 포함된 내용을 삭제하시겠습니까?</string>
<string name="confirmation_remove_local">로컬만</string>
<string name="conflict_dialog_error">다이얼로그의 충돌로 인한 오류</string>
<string name="conflict_file_headline">충돌하는 파일 %1$s</string>
<string name="conflict_local_file">로컬 파일</string>
<string name="conflict_message_description">두 버전을 모두 선택하면 기존 파일 이름에 번호가 추가됩니다.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">ການສ້າງບັນຊີບໍ່ສຳເລັດ</string>
<string name="account_icon">ໄອຄອນບັນຊີ</string>
<string name="account_not_found">ບໍ່ພົບບັນຊີ!</string>
<string name="action_clear_failed_uploads">ການອັບໂຫຼດບໍ່ສຳເລັດ</string>
<string name="action_edit">ແກ້ໄຂ</string>
<string name="action_empty_notifications">ລ້າງການແຈ້ງເຕືອນທັງຫມົດ</string>
<string name="action_empty_trashbin">ລ້າງຖັງຂີ້ເຫຍື່ອ</string>
@ -145,7 +144,6 @@
<string name="confirmation_remove_folder_alert">ທ່ານຕ້ອງການ ລຶບ%1$s ແລະ ເນື້ອຫາບໍ?</string>
<string name="confirmation_remove_folders_alert">ທ່ານຕ້ອງການລຶບລາຍການທີ່ເລືອກ ແລະ ເນື້ອຫາແທ້ບໍ?</string>
<string name="confirmation_remove_local">ຊ່ອງເກັບຢ່າງດຽວ</string>
<string name="conflict_dialog_error">ຄວາມຜິດພາດໃນການສ້າງການສົນທະນາຜິດພາດ!</string>
<string name="conflict_file_headline">ຟາຍຜິດພາດ%1$s</string>
<string name="conflict_message_description">ຖ້າທ່ານເລືອກເອົາທັງສອງເວີຊັ້ນ, ບ່ອນເກັບຟາຍຈະມີຈໍານວນສະສົມ</string>
<string name="contactlist_item_icon">ລາຍການໄອຄອນຜູ້ຕິດຕໍ່</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Paskyros sukūrimas nepavyko</string>
<string name="account_icon">Paskyros piktograma</string>
<string name="account_not_found">Paskyra nerasta!</string>
<string name="action_clear_failed_uploads">Išvalyti nepavykusius įkėlimus</string>
<string name="action_edit">Taisyti</string>
<string name="action_empty_notifications">Išvalyti visus pranešimus</string>
<string name="action_empty_trashbin">Išvalyti šiukšlinę</string>
@ -147,7 +146,6 @@
<string name="confirmation_remove_folder_alert">Ar tikrai norite ištrinti %1$s ir jo turinį?</string>
<string name="confirmation_remove_folders_alert">Ar tikrai norite ištrinti pažymėtus elementus ir jų turinį?</string>
<string name="confirmation_remove_local">Tik vietiniai</string>
<string name="conflict_dialog_error">Klaida kuriant konflikto dialogą!</string>
<string name="conflict_file_headline">Nesuderinamas failas%1$s</string>
<string name="conflict_local_file">Vietinis failas</string>
<string name="conflict_message_description">Jei pasirinksite abi versijas, vietinis failas prie pavadinimo turės numerį.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Konta izveidošana neizdevās</string>
<string name="account_icon">Konta ikona</string>
<string name="account_not_found">Konts nav atrasts!</string>
<string name="action_clear_failed_uploads">Notīrīt neizdevušās augšupielādes</string>
<string name="action_edit">Rediģēt</string>
<string name="action_empty_notifications">Notīrīt visus paziņojumus</string>
<string name="action_empty_trashbin">Izdzēst miskastes saturu</string>
@ -123,7 +122,6 @@
<string name="confirmation_remove_folder_alert">Vai tiešām vēlaties izdzēst %1$s un tā saturu?</string>
<string name="confirmation_remove_folders_alert">Vai tiešām vēlies dzēst izvēlētos objektus un to saturu?</string>
<string name="confirmation_remove_local">Tikai lokālos</string>
<string name="conflict_dialog_error">Kļūda veidojot konfliktu dialogu!</string>
<string name="contactlist_item_icon">Lietotāja ikona kontaktpersonu sarakstam</string>
<string name="contactlist_no_permission">Nav dota atļauja, importēšana neizdevās</string>
<string name="contacts">Kontakti</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Неуспешно креирање на сметка</string>
<string name="account_icon">Икона за сметката</string>
<string name="account_not_found">Сметката не е пројандена!</string>
<string name="action_clear_failed_uploads">Исчисти ги неуспешните прикачувања</string>
<string name="action_edit">Уреди</string>
<string name="action_empty_notifications">Исчисти ги сите известувања</string>
<string name="action_empty_trashbin">Испразни ја корпата со отпадоци</string>
@ -144,7 +143,6 @@
<string name="confirmation_remove_folder_alert">али си сигурен дека сакаш да ја избришеш %1$s и содржината во истата?</string>
<string name="confirmation_remove_folders_alert">Дали си сигурен дека сакаш да ја избришеш означената ставкаи содржината во неа?</string>
<string name="confirmation_remove_local">Само локално</string>
<string name="conflict_dialog_error">Грешка при креирање конфликтен извештај!</string>
<string name="conflict_file_headline">Датотеки со конфликт %1$s</string>
<string name="conflict_local_file">Локална датотека</string>
<string name="conflict_message_description">Ако ги одберете и двете верзии, локалната датотека ќе има број додаден на нејзиното име.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Oppretting av konto mislyktes</string>
<string name="account_icon">Kontoikon</string>
<string name="account_not_found">Konto ble ikke funnet!</string>
<string name="action_clear_failed_uploads">Fjern feilede opplastinger</string>
<string name="action_edit">Rediger</string>
<string name="action_empty_notifications">Slett alle varsel</string>
<string name="action_empty_trashbin">Tøm papirkurv</string>
@ -57,6 +56,8 @@
<string name="assistant_screen_task_types_error_state_message">Kan ikke hente oppgavetyper, vennligst sjekk internettforbindelsen din.</string>
<string name="assistant_screen_top_bar_title">Assistent</string>
<string name="assistant_screen_unknown_task_status_text">Ukjent</string>
<string name="assistant_task_detail_screen_input_button_title">Inndata</string>
<string name="assistant_task_detail_screen_output_button_title">Utdata</string>
<string name="associated_account_not_found">Tilknyttet bruker ikke funnet!</string>
<string name="auth_access_failed">Tilgang mislyktes: %1$s</string>
<string name="auth_account_does_not_exist">Kontoen er ikke lagt til på denne enheten enda</string>
@ -171,7 +172,6 @@
<string name="confirmation_remove_folder_alert">Ønsker du virkelig å slette %1$s og dets innhold?</string>
<string name="confirmation_remove_folders_alert">Vil du virkelig fjerne de valgte elementene og dets innhold?</string>
<string name="confirmation_remove_local">Kun lokalt</string>
<string name="conflict_dialog_error">Feil ved oppretting av konfliktdialog!</string>
<string name="conflict_file_headline">Konflikt med%1$s</string>
<string name="conflict_local_file">Lokal fil</string>
<string name="conflict_message_description">Hvis du velger begge versjonene vil den lokale filen få et tall lagt til på slutten av navnet.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Account aanmaken mislukt</string>
<string name="account_icon">Accountpictogram</string>
<string name="account_not_found">Account niet gevonden!</string>
<string name="action_clear_failed_uploads">Wis mislukte uploads</string>
<string name="action_edit">Bewerken</string>
<string name="action_empty_notifications">Handel alle meldingen af</string>
<string name="action_empty_trashbin">Prullenbak legen</string>
@ -167,7 +166,6 @@
<string name="confirmation_remove_folder_alert">Wil je %1$s en de inhoud ervan werkelijk verwijderen?</string>
<string name="confirmation_remove_folders_alert">Wil je de geselecteerde objecten en hun inhoud echt verwijderen?</string>
<string name="confirmation_remove_local">Alleen lokaal</string>
<string name="conflict_dialog_error">Fout bij het maken van conflictdialoog!</string>
<string name="conflict_file_headline">Conflicterend bestand%1$s</string>
<string name="conflict_local_file">Lokaal bestand</string>
<string name="conflict_message_description">Als je beide versies selecteert, zal het lokale bestand een nummer aan de naam toegevoegd krijgen.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Błąd tworzenia konta</string>
<string name="account_icon">Ikona konta</string>
<string name="account_not_found">Nie znaleziono konta!</string>
<string name="action_clear_failed_uploads">Wyczyść nieudane wysyłanie</string>
<string name="action_edit">Edytuj</string>
<string name="action_empty_notifications">Wyczyść wszystkie powiadomienia</string>
<string name="action_empty_trashbin">Opróżnij kosz</string>
@ -153,7 +152,6 @@
<string name="confirmation_remove_folder_alert">Czy na pewno chcesz usunąć %1$s wraz z zawartością?</string>
<string name="confirmation_remove_folders_alert">Czy na pewno chcesz usunąć wybrane pozycje i ich zawartość?</string>
<string name="confirmation_remove_local">Tylko lokalnie</string>
<string name="conflict_dialog_error">Błąd podczas tworzenia dialogu konfliktu!</string>
<string name="conflict_file_headline">Plik powodujący konflikt %1$s</string>
<string name="conflict_local_file">Plik lokalny</string>
<string name="conflict_message_description">Jeśli wybierzesz obie wersje, to do nazwy pliku lokalnego zostanie dodany numer.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Falha ao criar conta</string>
<string name="account_icon">Ícone da conta</string>
<string name="account_not_found">Conta não encontrada!</string>
<string name="action_clear_failed_uploads">Apagar envios com falha</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Apagar todas as notificações</string>
<string name="action_empty_trashbin">Esvaziar lixeira </string>
@ -57,6 +56,8 @@
<string name="assistant_screen_task_types_error_state_message">Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet.</string>
<string name="assistant_screen_top_bar_title">Assistente</string>
<string name="assistant_screen_unknown_task_status_text">Desconhecido</string>
<string name="assistant_task_detail_screen_input_button_title">Entrada</string>
<string name="assistant_task_detail_screen_output_button_title">Saída</string>
<string name="associated_account_not_found">Conta associada não encontrada!</string>
<string name="auth_access_failed">O acesso falhou: %1$s</string>
<string name="auth_account_does_not_exist">A conta ainda não está adicionada neste dispositivo</string>
@ -171,7 +172,6 @@
<string name="confirmation_remove_folder_alert">Quer realmente excluir %1$s e seu conteúdo?</string>
<string name="confirmation_remove_folders_alert">Quer realmente excluir os itens selecionados e seus conteúdos?</string>
<string name="confirmation_remove_local">Somente local</string>
<string name="conflict_dialog_error">Erro ao criar diálogo de conflito!</string>
<string name="conflict_file_headline">Arquivo conflitante %1$s</string>
<string name="conflict_local_file">Arquivo local</string>
<string name="conflict_message_description">Se você selecionar as duas versões, o arquivo local terá um número anexado ao seu nome.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Criação de conta falhou</string>
<string name="account_icon">Ícone de conta</string>
<string name="account_not_found">Conta não encontrada!</string>
<string name="action_clear_failed_uploads">Limpar envios falhados</string>
<string name="action_edit">Editar</string>
<string name="action_empty_notifications">Limpar todas as notifcações</string>
<string name="action_empty_trashbin">Esvaziar a reciclagem</string>
@ -150,7 +149,6 @@
<string name="confirmation_remove_folder_alert">Deseja realmente apagar %1$s e o seu conteúdo?</string>
<string name="confirmation_remove_folders_alert">Quer realmente apagar os itens seleccionados e os seus conteúdos?</string>
<string name="confirmation_remove_local">Apenas localmente</string>
<string name="conflict_dialog_error">Erro ao criar a janela de conflito!</string>
<string name="conflict_file_headline">Ficheiro em conflito %1$s</string>
<string name="conflict_local_file">Ficheiro local</string>
<string name="conflict_message_description">Se selecionou ambas as versões, o ficheiro local terá um número acrescentado ao seu nome.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Eroare la crearea contului</string>
<string name="account_icon">Iconița contului</string>
<string name="account_not_found">Contul nu a fost găsit!</string>
<string name="action_clear_failed_uploads">Elimină încărcările eșuate</string>
<string name="action_edit">Editare</string>
<string name="action_empty_notifications">Elimină toate notificările</string>
<string name="action_empty_trashbin">Golește coșul de gunoi</string>
@ -150,7 +149,6 @@
<string name="confirmation_remove_folder_alert">Sigur vreți să eliminați %1$s și conținutul său?</string>
<string name="confirmation_remove_folders_alert">Doriți să ștergeți elementele selectate și conținutul lor?</string>
<string name="confirmation_remove_local">Doar local</string>
<string name="conflict_dialog_error">Eroare la crearea dialogului pentru conflict!</string>
<string name="conflict_file_headline">Conflict cu fișierul %1$s</string>
<string name="conflict_local_file">Fișier local</string>
<string name="conflict_message_description">Dacă selectezi ambele variante, atunci fișierul local va avea un număr adăugat la numele său.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Ошибка создания учетной записи</string>
<string name="account_icon">Значок учётной записи</string>
<string name="account_not_found">Учётная запись не найдена!</string>
<string name="action_clear_failed_uploads">Убрать неудавшиеся загрузки</string>
<string name="action_edit">Редактировать</string>
<string name="action_empty_notifications">Удалить все уведомления</string>
<string name="action_empty_trashbin">Очистить корзину</string>
@ -158,7 +157,6 @@
<string name="confirmation_remove_folder_alert">Действительно удалить «%1$s» и его содержимое?</string>
<string name="confirmation_remove_folders_alert">Действительно удалить выбранные объекты и их содержимое?</string>
<string name="confirmation_remove_local">Только локально</string>
<string name="conflict_dialog_error">Не удалось создать диалоговое окно разрешения конфликта синхронизации.</string>
<string name="conflict_file_headline">Конфликт версий файла «%1$s»</string>
<string name="conflict_local_file">Локальный файл</string>
<string name="conflict_message_description">Если вы выберете обе версии, локальный файл будет иметь номер, добавленный к его имени.</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Errore in sa creatzione de su contu</string>
<string name="account_icon">Icona de su contu</string>
<string name="account_not_found">Contu no agatadu!</string>
<string name="action_clear_failed_uploads">Boga·nche is carrigamentos faddidos</string>
<string name="action_edit">Modìfica</string>
<string name="action_empty_notifications">Boga·nche totu is notìficas</string>
<string name="action_empty_trashbin">Bòida s\'àliga</string>
@ -144,7 +143,6 @@
<string name="confirmation_remove_folder_alert">A beru boles cantzellare %1$s e is cuntenutos suos?</string>
<string name="confirmation_remove_folders_alert">A beru boles cantzellare is elementos seberados e is cuntenutos issoro?</string>
<string name="confirmation_remove_local">Locale isceti</string>
<string name="conflict_dialog_error">Errore creende una bentana de cunflitu!</string>
<string name="conflict_file_headline">Archìviu in cunflitu %1$s</string>
<string name="conflict_message_description">Si seletzionas ambas is versiones, s\'archìviu locale at a tènnere unu nùmeru in agiunta a su nùmene.</string>
<string name="contactlist_item_icon">Icona utente pro lista de cuntatos</string>

View file

@ -7,7 +7,6 @@
<string name="account_creation_failed">Vytvorenie účtu sa nepodarilo</string>
<string name="account_icon">Ikona účtu</string>
<string name="account_not_found">Účet sa nenašiel!</string>
<string name="action_clear_failed_uploads">Odstrániť zlyhané nahrávania</string>
<string name="action_edit">Upraviť</string>
<string name="action_empty_notifications">Odstrániť všetky upozornenia</string>
<string name="action_empty_trashbin">Vyprázdniť kôš</string>
@ -171,7 +170,6 @@
<string name="confirmation_remove_folder_alert">Naozaj chcete odstrániť %1$s a jeho obsah?</string>
<string name="confirmation_remove_folders_alert">Naozaj chcete odstrániť vybraté položky a ich obsah?</string>
<string name="confirmation_remove_local">Iba lokálne</string>
<string name="conflict_dialog_error">Chyba pri vytváraní konfliktného dialógového okna!</string>
<string name="conflict_file_headline">Konfliktný súbor %1$s</string>
<string name="conflict_local_file">Lokálny súbor</string>
<string name="conflict_message_description">Ak vyberiete obe verzie, miestny súbor bude mať k svojmu názvu pridané číslo.</string>

Some files were not shown because too many files have changed in this diff Show more