diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b6f52d89c6d5..8f068afcada6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -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 diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 1d4ad457af38..47b48ecad89f 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -44,7 +44,7 @@ jobs: echo "repo=${{ github.event.pull_request.head.repo.full_name }}" } >> "$GITHUB_OUTPUT" fi - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: repository: ${{ steps.get-vars.outputs.repo }} ref: ${{ steps.get-vars.outputs.branch }} diff --git a/.github/workflows/assembleFlavors.yml b/.github/workflows/assembleFlavors.yml index 5be9e6321376..c1019c68a0a9 100644 --- a/.github/workflows/assembleFlavors.yml +++ b/.github/workflows/assembleFlavors.yml @@ -22,7 +22,7 @@ jobs: matrix: flavor: [ Generic, Gplay, Huawei ] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 3e0aa24c817a..0bd50da17f3e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,7 +22,7 @@ jobs: matrix: task: [ detekt, spotlessKotlinCheck ] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b05056570d7b..2564164fb4da 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,13 +33,13 @@ jobs: language: [ 'java' ] steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Set Swap Space uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0 with: swap-size-gb: 10 - name: Initialize CodeQL - uses: github/codeql-action/init@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 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@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 diff --git a/.github/workflows/command-rebase.yml b/.github/workflows/command-rebase.yml index f448628256f6..80819d98d0f2 100644 --- a/.github/workflows/command-rebase.yml +++ b/.github/workflows/command-rebase.yml @@ -34,7 +34,7 @@ jobs: reaction-type: "+1" - name: Checkout the latest code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 token: ${{ secrets.COMMAND_BOT_PAT }} diff --git a/.github/workflows/detectWrongSettings.yml b/.github/workflows/detectWrongSettings.yml index fa3a2c74542a..e370c5bde800 100644 --- a/.github/workflows/detectWrongSettings.yml +++ b/.github/workflows/detectWrongSettings.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index e6f8faa74cf3..b72bf62069be 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -25,5 +25,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 diff --git a/.github/workflows/pr-feedback.yml b/.github/workflows/pr-feedback.yml index e5fe74a04ee3..940eda2317ca 100644 --- a/.github/workflows/pr-feedback.yml +++ b/.github/workflows/pr-feedback.yml @@ -3,7 +3,7 @@ # https://github.com/nextcloud/.github # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors +# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors # SPDX-FileCopyrightText: 2023 Marcel Klehr # SPDX-FileCopyrightText: 2023 Joas Schilling <213943+nickvergessen@users.noreply.github.com> # SPDX-FileCopyrightText: 2023 Daniel Kesselberg diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index f2503941f5da..c033c5e8902e 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -23,7 +23,7 @@ jobs: - name: Check if secrets are available run: echo "::set-output name=ok::${{ secrets.KS_PASS != '' }}" id: check-secrets - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 if: ${{ steps.check-secrets.outputs.ok == 'true' }} - name: set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml index ec41e61ef011..57231dcf6cec 100644 --- a/.github/workflows/reuse.yml +++ b/.github/workflows/reuse.yml @@ -1,4 +1,7 @@ -# synced from @nextcloud/android-config +# This workflow is provided via the organization template repository +# +# https://github.com/nextcloud/.github +# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization # SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. # @@ -6,12 +9,12 @@ name: REUSE Compliance Check -on: [push, pull_request] +on: [pull_request] jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: REUSE Compliance Check uses: fsfe/reuse-action@a46482ca367aef4454a87620aa37c2be4b2f8106 # v3.0.0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ab2aa5ed7f1b..4507aadd13bc 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false @@ -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@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: sarif_file: results.sarif diff --git a/.github/workflows/screenShotTest.yml b/.github/workflows/screenShotTest.yml index 72b5229a5899..27ba4ad706f4 100644 --- a/.github/workflows/screenShotTest.yml +++ b/.github/workflows/screenShotTest.yml @@ -17,7 +17,7 @@ concurrency: jobs: screenshot: - runs-on: macOS-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -25,7 +25,7 @@ jobs: color: [ blue ] api-level: [ 27 ] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Gradle cache uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -48,6 +48,12 @@ jobs: distribution: "temurin" java-version: 17 + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # v2.30.1 @@ -93,7 +99,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: scripts/uploadReport.sh "${{ secrets.LOG_USERNAME }}" "${{ secrets.LOG_PASSWORD }}" ${{github.event.number}} "${{ matrix.color }}-${{ matrix.scheme }}" "Screenshot" ${{github.event.number}} - name: Archive Espresso results - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 if: ${{ always() }} with: name: Report-${{ matrix.color }}-${{ matrix.scheme }} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 0820761ac1c3..1740860d103b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -21,7 +21,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up JDK 17 uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: @@ -33,7 +33,7 @@ jobs: if: ${{ always() }} run: scripts/deleteOldComments.sh "test" "Unit" ${{github.event.number}} - name: Run unit tests with coverage - uses: gradle/gradle-build-action@8baac4c8ef753599f92eeb509c246d09d6250fa6 # v3.3.0 + uses: gradle/gradle-build-action@e2097ccd7e8ed48671dc068ac4efa86d25745b39 # v3.3.1 with: arguments: jacocoTestGplayDebugUnitTest - name: Upload failing results @@ -49,7 +49,7 @@ jobs: fail_ci_if_error: true - name: Upload jacoco artifacts if: ${{ failure() }} - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: test-results path: app/build/reports/tests/testGplayDebugUnitTest/ diff --git a/.reuse/dep5 b/.reuse/dep5 index 1babbb7e974e..acdbfdc008a4 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -20,8 +20,8 @@ Copyright: 2015-2016 ownCloud Inc. License: GPL-2.0-only Files: app/src/*/res/mipmap-*dpi/ic_launcher.png app/src/*/ic_launcher-web.png src/generic/fastlane/metadata/android/en-US/images/icon.png src/versionDev/fastlane/metadata/android/en-US/images/icon.png app/src/main/ic_launcher-web-round.png -Copyright: 2017-2024 Nextcloud GmbH. All rights reserved. Trademarks apply, see https://nextcloud.com/trademarks/ -License: AGPL-3.0-or-later +Copyright: 2017-2024 Nextcloud GmbH +License: LicenseRef-NextcloudTrademarks Files: .idea/* app/schemas/com.nextcloud.client.database.NextcloudDatabase/*.json app/screenshots/gplay/debug/*.png app/src/main/res/values-*/strings.xml src/*/fastlane/metadata/android/*/*.txt src/versionDev/fastlane/metadata/android/*/changelogs/*.txt app/src/androidTest/assets/* app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker app/src/*/google-services.json app/src/main/res/drawable-*dpi/checker_16_16.png app/src/main/res/raw/encryption_key_words.txt app/src/main/resources/ical4j.properties app/src/main/res/drawable-*dpi/apk.png app/src/main/res/drawable-*dpi/fdroid.png app/src/main/res/drawable-*dpi/playstore.png app/src/main/res/drawable-*dpi/background.png app/src/main/res/drawable-*dpi/background_nc18.png Copyright: 2016-2024 Nextcloud GmbH and Nextcloud contributors diff --git a/CHANGELOG.md b/CHANGELOG.md index 97df73bf1dba..aa3bbc8d25e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,29 @@ -## 3.28.2 (April 4th, 2024) + +## 3.29.0 (April 24, 2024) + +- NC Assistant +- Client certificates +- Personal files view +- REUSE compliance - Bugfixes +Minimum: NC 16 Server, Android 7.0 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/89 + +## 3.28.2 (April 4th, 2024) + +- Bugfixes Minimum: NC 16 Server, Android 7.0 Nougat For a full list, please see https://github.com/nextcloud/android/milestone/90 - - ## 3.28.1 (March 25th, 2024) - Bugfixes @@ -25,7 +37,6 @@ For a full list, please see https://github.com/nextcloud/android/milestone/90 - E2E sharing - Bugfixes - Minimum: NC 16 Server, Android 7.0 Nougat For a full list, please see https://github.com/nextcloud/android/milestone/88 diff --git a/LICENSES/LicenseRef-NextcloudTrademarks.txt b/LICENSES/LicenseRef-NextcloudTrademarks.txt new file mode 100644 index 000000000000..464a30b58bdc --- /dev/null +++ b/LICENSES/LicenseRef-NextcloudTrademarks.txt @@ -0,0 +1,9 @@ +The Nextcloud marks +Nextcloud and the Nextcloud logo is a registered trademark of Nextcloud GmbH in Germany and/or other countries. +These guidelines cover the following marks pertaining both to the product names and the logo: “Nextcloud” +and the blue/white cloud logo with or without the word Nextcloud; the service “Nextcloud Enterprise”; +and our products: “Nextcloud Files”; “Nextcloud Groupware” and “Nextcloud Talk”. +This set of marks is collectively referred to as the “Nextcloud marks.” + +Use of Nextcloud logos and other marks is only permitted under the guidelines provided by the Nextcloud GmbH. +A copy can be found at https://nextcloud.com/trademarks/ diff --git a/app/build.gradle b/app/build.gradle index 7fb9c7065f37..a2f7e3ca36e2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 @@ -198,6 +198,7 @@ android { packagingOptions { resources { excludes += 'META-INF/LICENSE*' + excludes += 'META-INF/versions/9/OSGI-INF/MANIFEST*' pickFirst 'MANIFEST.MF' // workaround for duplicated manifest on some dependencies } } @@ -262,7 +263,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.5.11" + kotlinCompilerExtensionVersion = "1.5.12" } } @@ -274,12 +275,12 @@ dependencies { } // Jetpack Compose - implementation(platform("androidx.compose:compose-bom:2024.04.00")) + implementation(platform("androidx.compose:compose-bom:2024.04.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.material3:material3") - implementation("androidx.compose.ui:ui-tooling-preview:1.6.5") - debugImplementation 'androidx.compose.ui:ui-tooling:1.6.5' + implementation("androidx.compose.ui:ui-tooling-preview:1.6.6") + debugImplementation 'androidx.compose.ui:ui-tooling:1.6.6' compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2' // remove after entire switch to lib v2 diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json new file mode 100644 index 000000000000..10d076c10d38 --- /dev/null +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/81.json @@ -0,0 +1,1209 @@ +{ + "formatVersion": 1, + "database": { + "version": 81, + "identityHash": "082a63031678a67879428f688f02d3b5", + "entities": [ + { + "tableName": "arbitrary_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "cloudId", + "columnName": "cloud_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assistant", + "columnName": "assistant", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionMajor", + "columnName": "version_mayor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMinor", + "columnName": "version_minor", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionMicro", + "columnName": "version_micro", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "versionString", + "columnName": "version_string", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "versionEditor", + "columnName": "version_edition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "extendedSupport", + "columnName": "extended_support", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "corePollinterval", + "columnName": "core_pollinterval", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingApiEnabled", + "columnName": "sharing_api_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicEnabled", + "columnName": "sharing_public_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicPasswordEnforced", + "columnName": "sharing_public_password_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnabled", + "columnName": "sharing_public_expire_date_enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateDays", + "columnName": "sharing_public_expire_date_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicExpireDateEnforced", + "columnName": "sharing_public_expire_date_enforced", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicSendMail", + "columnName": "sharing_public_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingPublicUpload", + "columnName": "sharing_public_upload", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingUserSendMail", + "columnName": "sharing_user_send_mail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingResharing", + "columnName": "sharing_resharing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationOutgoing", + "columnName": "sharing_federation_outgoing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharingFederationIncoming", + "columnName": "sharing_federation_incoming", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesBigfilechunking", + "columnName": "files_bigfilechunking", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesUndelete", + "columnName": "files_undelete", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesVersioning", + "columnName": "files_versioning", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "externalLinks", + "columnName": "external_links", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverName", + "columnName": "server_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverColor", + "columnName": "server_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverTextColor", + "columnName": "server_text_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverElementColor", + "columnName": "server_element_color", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverSlogan", + "columnName": "server_slogan", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverLogo", + "columnName": "server_logo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serverBackgroundUrl", + "columnName": "background_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endToEndEncryption", + "columnName": "end_to_end_encryption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionKeysExist", + "columnName": "end_to_end_encryption_keys_exist", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "endToEndEncryptionApiVersion", + "columnName": "end_to_end_encryption_api_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "activity", + "columnName": "activity", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundDefault", + "columnName": "background_default", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "serverBackgroundPlain", + "columnName": "background_plain", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocument", + "columnName": "richdocument", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentMimetypeList", + "columnName": "richdocument_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richdocumentDirectEditing", + "columnName": "richdocument_direct_editing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentTemplates", + "columnName": "richdocument_direct_templates", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentOptionalMimetypeList", + "columnName": "richdocument_optional_mimetype_list", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharingPublicAskForOptionalPassword", + "columnName": "sharing_public_ask_for_optional_password", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "richdocumentProductName", + "columnName": "richdocument_product_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "directEditingEtag", + "columnName": "direct_editing_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userStatus", + "columnName": "user_status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userStatusSupportsEmoji", + "columnName": "user_status_supports_emoji", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filesLockingVersion", + "columnName": "files_locking_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "groupfolders", + "columnName": "groupfolders", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dropAccount", + "columnName": "drop_account", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "securityGuard", + "columnName": "security_guard", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "external_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "icon_url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "redirect", + "columnName": "redirect", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filelist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `hidden` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `metadata_live_photo` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT, `e2e_counter` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "encryptedName", + "columnName": "encrypted_filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pathDecrypted", + "columnName": "path_decrypted", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parent", + "columnName": "parent", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creation", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentLength", + "columnName": "content_length", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "storagePath", + "columnName": "media_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "file_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastSyncDate", + "columnName": "last_sync_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastSyncDateForData", + "columnName": "last_sync_date_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modifiedAtLastSyncForData", + "columnName": "modified_at_last_sync_for_data", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etag", + "columnName": "etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "etagOnServer", + "columnName": "etag_on_server", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedViaLink", + "columnName": "share_by_link", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remote_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localId", + "columnName": "local_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "updateThumbnail", + "columnName": "update_thumbnail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isDownloading", + "columnName": "is_downloading", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "favorite", + "columnName": "favorite", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isEncrypted", + "columnName": "is_encrypted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "etagInConflict", + "columnName": "etag_in_conflict", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharedWithSharee", + "columnName": "shared_via_users", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mountType", + "columnName": "mount_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hasPreview", + "columnName": "has_preview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "unreadCommentsCount", + "columnName": "unread_comments_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ownerDisplayName", + "columnName": "owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sharees", + "columnName": "sharees", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "richWorkspace", + "columnName": "rich_workspace", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataSize", + "columnName": "metadata_size", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataLivePhoto", + "columnName": "metadata_live_photo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockType", + "columnName": "lock_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockOwner", + "columnName": "lock_owner", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerDisplayName", + "columnName": "lock_owner_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockOwnerEditor", + "columnName": "lock_owner_editor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lockTimestamp", + "columnName": "lock_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockTimeout", + "columnName": "lock_timeout", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lockToken", + "columnName": "lock_token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "metadataGPS", + "columnName": "metadata_gps", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "e2eCounter", + "columnName": "e2e_counter", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "filesystem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileIsFolder", + "columnName": "is_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileFoundRecently", + "columnName": "found_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSentForUpload", + "columnName": "upload_triggered", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "syncedFolderId", + "columnName": "syncedfolder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "crc32", + "columnName": "crc32", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileModified", + "columnName": "modified_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ocshares", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fileSource", + "columnName": "file_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "itemSource", + "columnName": "item_source", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareType", + "columnName": "share_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareWith", + "columnName": "shate_with", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "permissions", + "columnName": "permissions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sharedDate", + "columnName": "shared_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "expirationDate", + "columnName": "expiration_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareWithDisplayName", + "columnName": "shared_with_display_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isDirectory", + "columnName": "is_directory", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "idRemoteShared", + "columnName": "id_remote_shared", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountOwner", + "columnName": "owner_share", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isPasswordProtected", + "columnName": "is_password_protected", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hideDownload", + "columnName": "hide_download", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "shareLink", + "columnName": "share_link", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shareLabel", + "columnName": "share_label", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "synced_folders", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER, `exclude_hidden` INTEGER, `last_scan_timestamp_ms` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wifiOnly", + "columnName": "wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chargingOnly", + "columnName": "charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "existing", + "columnName": "existing", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledTimestampMs", + "columnName": "enabled_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subfolderByDate", + "columnName": "subfolder_by_date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "account", + "columnName": "account", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploadAction", + "columnName": "upload_option", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subFolderRule", + "columnName": "sub_folder_rule", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "excludeHidden", + "columnName": "exclude_hidden", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastScanTimestampMs", + "columnName": "last_scan_timestamp_ms", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "list_of_uploads", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localPath", + "columnName": "local_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remotePath", + "columnName": "remote_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localBehaviour", + "columnName": "local_behaviour", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadTime", + "columnName": "upload_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "nameCollisionPolicy", + "columnName": "name_collision_policy", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isCreateRemoteFolder", + "columnName": "is_create_remote_folder", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uploadEndTimestamp", + "columnName": "upload_end_timestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastResult", + "columnName": "last_result", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWhileChargingOnly", + "columnName": "is_while_charging_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isWifiOnly", + "columnName": "is_wifi_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "createdBy", + "columnName": "created_by", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderUnlockToken", + "columnName": "folder_unlock_token", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "virtual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ocFileId", + "columnName": "ocfile_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '082a63031678a67879428f688f02d3b5')" + ] + } +} \ No newline at end of file diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png index c0148e4f9524..fe2bf2c55203 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.activity.UploadFilesActivityIT_noneSelected.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png index 4b8e5ba28034..66db64e770d8 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displayJavaSnippetFile.png differ diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png index bba71fe6886d..555930077547 100644 Binary files a/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png and b/app/screenshots/gplay/debug/com.owncloud.android.ui.preview.PreviewTextFileFragmentTest_displaySimpleTextFile.png differ diff --git a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java index 57e97d8ccda5..ba43a43af81a 100644 --- a/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java +++ b/app/src/androidTest/java/com/nextcloud/client/SyncedFoldersActivityIT.java @@ -14,6 +14,7 @@ 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 void testSyncedFolderDialog() { 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); diff --git a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt index 21be19bbc279..e90b81ca73c0 100644 --- a/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt +++ b/app/src/androidTest/java/com/nextcloud/client/jobs/BackgroundJobManagerTest.kt @@ -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()) ) } diff --git a/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt new file mode 100644 index 000000000000..ed119a2a9d68 --- /dev/null +++ b/app/src/androidTest/java/com/owncloud/android/EncryptionIT.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * 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) + } + } +} diff --git a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java index d1100a8b97aa..8c81d28e5cdf 100644 --- a/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java +++ b/app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java @@ -836,7 +836,7 @@ private boolean cryptFile(String fileName, String md5, byte[] key, byte[] iv) // 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 diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt index 55ab2f472b2c..e5b2cf9d0b95 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsIT.kt @@ -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) + } } diff --git a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt index b52460e30eb8..4d72bbda3c01 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/EncryptionUtilsV2IT.kt @@ -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 diff --git a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt index aba890bd062b..5e312853076b 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt @@ -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)) } diff --git a/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt b/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt index 0746c4dd845b..98b410f6707d 100644 --- a/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/utils/theme/CapabilityUtilsIT.kt @@ -17,11 +17,12 @@ import org.junit.Test class CapabilityUtilsIT : AbstractIT() { @Test fun checkOutdatedWarning() { + assertFalse(test(NextcloudVersion.nextcloud_28)) assertFalse(test(NextcloudVersion.nextcloud_27)) - assertFalse(test(NextcloudVersion.nextcloud_26)) - assertFalse(test(NextcloudVersion.nextcloud_25)) - assertFalse(test(NextcloudVersion.nextcloud_24)) + assertTrue(test(NextcloudVersion.nextcloud_26)) + assertTrue(test(NextcloudVersion.nextcloud_25)) + assertTrue(test(NextcloudVersion.nextcloud_24)) assertTrue(test(NextcloudVersion.nextcloud_23)) assertTrue(test(NextcloudVersion.nextcloud_22)) assertTrue(test(NextcloudVersion.nextcloud_21)) diff --git a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt index d7b09d4f0083..4dd567c4fdf9 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt @@ -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 diff --git a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt index fb7eeb0faa91..f2ff257800bd 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/extensions/TaskExtensions.kt @@ -35,3 +35,8 @@ fun Task.statusData(): Pair { } } } + +// TODO add +fun Task.completionDateRepresentation(): String { + return completionExpectedAt ?: "TODO IMPLEMENT IT" +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt index 754a4cb5f16d..04efe9828ba4 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/repository/AssistantMockRepository.kt @@ -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 { return RemoteOperationResult(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), "", "" ) diff --git a/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt new file mode 100644 index 000000000000..c9179881c721 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskStatus.kt @@ -0,0 +1,58 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * 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)) + */ + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt similarity index 56% rename from app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt rename to app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt index c09e98716c8f..865f41bf4cd1 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/task/TaskView.kt @@ -1,27 +1,22 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH + * SPDX-FileCopyrightText: 2024 Your Name * 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( diff --git a/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt new file mode 100644 index 000000000000..e7148de43d0a --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/assistant/taskDetail/TaskDetailBottomSheet.kt @@ -0,0 +1,165 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * 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), + "", + "" + ) + ) { + } +} diff --git a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt similarity index 97% rename from app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt rename to app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt index 6a4690b01e1c..eab26875c263 100644 --- a/app/src/main/java/com/nextcloud/client/assistant/component/TaskTypesRow.kt +++ b/app/src/main/java/com/nextcloud/client/assistant/taskTypes/TaskTypesRow.kt @@ -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 diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index f9386dc30f4b..db98e777db33 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -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 ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt index 72e57bc5de9b..8a4298cef6c0 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/SyncedFolderEntity.kt @@ -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? ) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 945e14ded7ca..0e3fd8838678 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -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) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index caac611f7bc0..718e527277bb 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -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() } diff --git a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt index 651c65aa02d9..58b57284eee7 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/FilesSyncWork.kt @@ -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, diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 1b5f1c86675b..2e231b2def87 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -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() + 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)) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index e0c50de8c3bb..80cb01ee4eb8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -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 { + private fun upload(uploadFileOperation: UploadFileOperation, user: User): RemoteOperationResult { lateinit var result: RemoteOperationResult 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(e) } finally { - cleanupUploadProcess(result, operation) + cleanupUploadProcess(result, uploadFileOperation) } return result } - private fun cleanupUploadProcess(result: RemoteOperationResult, operation: UploadFileOperation) { + private fun cleanupUploadProcess(result: RemoteOperationResult, 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 ) } diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt index 23392fa65a6b..4c2a9c6656ba 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadNotificationManager.kt @@ -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() diff --git a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt index c57b39a203f5..f0de1e08883a 100644 --- a/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt +++ b/app/src/main/java/com/nextcloud/ui/composeActivity/ComposeActivity.kt @@ -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) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt new file mode 100644 index 000000000000..9888e951de2c --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/StringExtensions.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Your Name + * 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 +} diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index ae7e03f7368d..f6d754b402ab 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -124,7 +124,7 @@ * 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(); diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 6245958506ad..2b181d6c436a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -56,7 +56,10 @@ 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.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 OCFile saveFileWithParent(OCFile ocFile, Context context) { 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 void deleteFileInMediaScan(String path) { } } + @SuppressFBWarnings("PSC") public void saveConflict(OCFile ocFile, String etagInConflict) { ContentValues cv = new ContentValues(); if (!ocFile.isDown()) { @@ -2297,6 +2332,59 @@ public void deleteAllFiles() { } } + 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 files = getAllFilesRecursivelyInsideFolder(folder); + List> decryptedFileNamesAndEncryptedRemotePaths = getDecryptedFileNamesAndEncryptedRemotePaths(files); + + String decryptedFileName = decryptedRemotePath.substring( decryptedRemotePath.lastIndexOf('/') + 1); + + for (Pair item : decryptedFileNamesAndEncryptedRemotePaths) { + if (item.getFirst().equals(decryptedFileName)) { + return item.getSecond(); + } + } + + return null; + } + + @SuppressFBWarnings("OCP") + private List> getDecryptedFileNamesAndEncryptedRemotePaths(List fileList) { + List> result = new ArrayList<>(); + + for (OCFile file : fileList) { + if (file.isEncrypted()) { + Pair 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())); diff --git a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java index 6e088e721150..aec6fda1bcd0 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FilesystemDataProvider.java @@ -107,6 +107,7 @@ public Set getFilesForUpload(String localPath, String syncedFolderId) { 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 void storeOrUpdateFileValue(String localPath, long modifiedAt, boolean is } } - + // updating data takes multiple milliseconds (around 25% of exec time) (2 ms) int result = contentResolver.update( ProviderMeta.ProviderTableMeta.CONTENT_URI_FILESYSTEM, cv, diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index f787a550adb0..3e5b8faad590 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -21,6 +21,7 @@ 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 SyncedFolder(String localPath, MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { this(UNPERSISTED_ID, localPath, remotePath, @@ -91,7 +94,8 @@ public SyncedFolder(String localPath, type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); } /** @@ -114,7 +118,8 @@ protected SyncedFolder(long id, 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 @@ protected SyncedFolder(long id, this.hidden = hidden; this.subfolderRule = subFolderRule; this.excludeHidden = excludeHidden; + this.lastScanTimestampMs = lastScanTimestampMs; } /** @@ -271,4 +277,8 @@ public void setExcludeHidden(boolean excludeHidden) { public boolean containsFile(String filePath){ return filePath.contains(localPath); } + + public long getLastScanTimestampMs() { return lastScanTimestampMs; } + + public void setLastScanTimestampMs(long lastScanTimestampMs) { this.lastScanTimestampMs = lastScanTimestampMs; } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 5b156f570987..2bef272cf99e 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -60,7 +60,8 @@ public SyncedFolderDisplayItem(long id, MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { super(id, localPath, remotePath, @@ -76,7 +77,8 @@ public SyncedFolderDisplayItem(long id, type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; @@ -98,7 +100,8 @@ public SyncedFolderDisplayItem(long id, MediaFolderType type, boolean hidden, SubFolderRule subFolderRule, - boolean excludeHidden) { + boolean excludeHidden, + long lastScanTimestampMs) { super(id, localPath, remotePath, @@ -114,7 +117,8 @@ public SyncedFolderDisplayItem(long id, type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); this.folderName = folderName; } diff --git a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index d867684829ae..51478892643a 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/app/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -360,7 +360,9 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { 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 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { type, hidden, subFolderRule, - excludeHidden); + excludeHidden, + lastScanTimestampMs); } return syncedFolder; } @@ -407,8 +410,8 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol 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; } diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 5c948f39ca58..a22d5d8400c1 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -25,7 +25,7 @@ */ 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 @@ static public class ProviderTableMeta implements BaseColumns { 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"; diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 8223c22a9f7e..7da4f995172c 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -58,15 +58,14 @@ 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 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 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 Map getForgottenLocalFiles() { } /** - * 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 List getChildren() { /** * Performs the synchronization. - * + *

* {@inheritDoc} */ @Override @@ -246,7 +263,7 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (!mSyncFullAccount && mRemoteFolderChanged) { sendLocalBroadcast( EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result - ); + ); } if (result.isSuccess() && !mSyncFullAccount && !mOnlyFileMetadata) { @@ -256,7 +273,7 @@ protected RemoteOperationResult run(OwnCloudClient client) { if (!mSyncFullAccount) { sendLocalBroadcast( EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result - ); + ); } return result; @@ -371,7 +388,7 @@ private RemoteOperationResult checkForChanges(OwnCloudClient client) { 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 @@ private RemoteOperationResult checkForChanges(OwnCloudClient client) { } 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 @@ private void removeLocalFolder() { /** - * 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. + *

* Grants that mChildren is updated with fresh data after execution. * * @param folderAndFiles Remote folder and children files in Folder @@ -553,6 +570,7 @@ private void synchronizeData(List folderAndFiles) { 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 static Object getDecryptedFolderMetadata(boolean encryptedAncestor, 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 static void updateFileNameForEncryptedFileV1(FileDataStorageManager stora } else { com.owncloud.android.datamodel.e2e.v1.decrypted.DecryptedFile decryptedFile = metadata.getFiles().get(updatedFile.getFileName()); - decryptedFileName = decryptedFile.getEncrypted().getFilename(); - mimetype = decryptedFile.getEncrypted().getMimetype(); - } - - OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + if (decryptedFile == null) { + throw new NullPointerException("decryptedFile cannot be null"); + } - if (updatedFile.isFolder()) { - decryptedRemotePath += "/"; + decryptedFileName = decryptedFile.getEncrypted().getFilename(); + mimetype = decryptedFile.getEncrypted().getMimetype(); } - 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 static void updateFileNameForEncryptedFile(FileDataStorageManager storage mimetype = MimeType.DIRECTORY; } else { DecryptedFile decryptedFile = metadata.getMetadata().getFiles().get(updatedFile.getFileName()); - decryptedFileName = decryptedFile.getFilename(); - mimetype = decryptedFile.getMimetype(); - } - - OCFile parentFile = storageManager.getFileById(updatedFile.getParentId()); - String decryptedRemotePath = parentFile.getDecryptedRemotePath() + decryptedFileName; + if (decryptedFile == null) { + throw new NullPointerException("decryptedFile cannot be null"); + } - if (updatedFile.isFolder()) { - decryptedRemotePath += "/"; + decryptedFileName = decryptedFile.getFilename(); + mimetype = decryptedFile.getMimetype(); } - 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 @@ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, 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 @@ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, // 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 @@ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, 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 @@ private void setLocalFileDataOnUpdatedFile(OCFile remoteFile, OCFile localFile, } @NonNull + @SuppressFBWarnings("OCP") public static Map prefillLocalFilesMap(Object metadata, List localFiles) { Map localFilesMap = Maps.newHashMapWithExpectedSize(localFiles.size()); @@ -731,13 +757,12 @@ public static Map prefillLocalFilesMap(Object metadata, List + * 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 filesToSyncContents) { RemoteOperationResult contentsResult; @@ -750,10 +775,10 @@ private void startContentSynchronizations(List filesTo 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 @@ private void startContentSynchronizations(List filesTo /** * 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 @@ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { } /** - * 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) { diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 5c2e793001dc..18de79ad31df 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -442,6 +442,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare File temporalFile = null; File originalFile = new File(mOriginalStoragePath); File expectedFile = null; + File encryptedTempFile = null; FileLock fileLock = null; long size; @@ -552,7 +553,7 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare 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 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare } } - File encryptedTempFile = encryptedFile.getEncryptedFile(); - - /***** E2E *****/ + encryptedTempFile = encryptedFile.getEncryptedFile(); FileChannel channel = null; try { @@ -712,8 +711,6 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare user, getStorageManager()); } - - encryptedTempFile.delete(); } } catch (FileNotFoundException e) { Log_OC.d(TAG, mFile.getStoragePath() + " not exists anymore"); @@ -755,6 +752,13 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare 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()) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt index 31a85c9eeb90..a83e5c6dd000 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt @@ -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, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index a8a0ff1c4efb..98d03372eb1f 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -286,10 +286,10 @@ private void setupQuotaElement() { 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,45 +339,55 @@ public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { } // 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); - } + showTopBanner(banner, primaryColor); + } + } - List views = Arrays.asList(notesView, talkView, moreView, assistantView); + private void hideTopBanner(LinearLayout banner) { + banner.setVisibility(View.GONE); + } - int iconColor; - if (Hct.fromInt(primaryColor).getTone() < 80.0) { - iconColor = Color.WHITE; - } else { - iconColor = getColor(R.color.grey_800_transparent); - } + 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); + } - 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); - } + List 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); + } - ecosystemApps.setVisibility(View.VISIBLE); + 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); } /** @@ -463,9 +473,7 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 @@ private void onNavigationItemClicked(final MenuItem menuItem) { 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 optionalUser = getUser(); if (optionalUser.isPresent()) { UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager()); @@ -627,6 +643,11 @@ private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) { 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 boolean isDrawerOpen() { return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START); } + public void toggleDrawer() { + if (isDrawerOpen()) { + closeDrawer(); + } else { + openDrawer(); + } + } + /** * closes the drawer. */ @@ -1115,6 +1144,11 @@ public void showFiles(boolean onDeviceOnly, boolean onlyPersonalFiles) { 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 void showFiles(boolean onDeviceOnly, boolean onlyPersonalFiles) { @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) { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 94a4ed270d13..4e7496dc62ec 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -54,7 +54,6 @@ 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 @@ private void requestUploadOfFilesFromFileSystem(Intent data, int resultCode) { 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 @@ private void requestUploadOfFilesFromFileSystem(String localBasePath, String[] f FileUploadHelper.Companion.instance().uploadNewFiles(getUser().orElseThrow(RuntimeException::new), filePaths, - remotePaths, + decryptedRemotePaths, behaviour, true, UploadFileOperation.CREATED_BY_USER, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index bbdd2870f188..53fa9c3e60ec 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -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) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 5e6dec41f0b7..b018647ed0e2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -104,6 +104,22 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB 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 void updateToolbarSubtitle(@NonNull String subtitle) { public void clearToolbarSubtitle() { ActionBar actionBar = getSupportActionBar(); - if(actionBar != null){ + if (actionBar != null) { actionBar.setSubtitle(null); } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 2a747669bd58..2ee4e4e20576 100755 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -141,9 +141,11 @@ private void showFailedPopupMenu(HeaderViewHolder headerViewHolder) { 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 @@ private void showFailedPopupMenu(HeaderViewHolder headerViewHolder) { powerManagementService); parentActivity.runOnUiThread(this::loadUploadItemsFromDb); }).start(); - } + return true; }); + failedPopup.show(); } @@ -169,6 +172,7 @@ private void showCancelledPopupMenu(HeaderViewHolder headerViewHolder) { 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 @@ private void showCancelledPopupMenu(HeaderViewHolder headerViewHolder) { popup.show(); } + private void clearTempEncryptedFolder() { + Optional 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( diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 3b49cdc5014c..150d0e39d26b 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -650,10 +650,22 @@ public static Snackbar showSnackMessage(Activity activity, @StringRes int messag */ public static Snackbar showSnackMessage(Activity activity, String message) { final Snackbar snackbar = Snackbar.make(activity.findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG); + var fab = findFABView(activity); + if (fab != null && fab.getVisibility() == View.VISIBLE) { + snackbar.setAnchorView(fab); + } snackbar.show(); return snackbar; } + private static View findFABView(Activity activity) { + return activity.findViewById(R.id.fab_main); + } + + private static View findFABView(View view) { + return view.findViewById(R.id.fab_main); + } + /** * Show a temporary message in a {@link Snackbar} bound to the given view. * @@ -663,6 +675,10 @@ public static Snackbar showSnackMessage(Activity activity, String message) { */ public static Snackbar showSnackMessage(View view, @StringRes int messageResource) { final Snackbar snackbar = Snackbar.make(view, messageResource, Snackbar.LENGTH_LONG); + var fab = findFABView(view.getRootView()); + if (fab != null && fab.getVisibility() == View.VISIBLE) { + snackbar.setAnchorView(fab); + } snackbar.show(); return snackbar; } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index d2d942f9421e..0f39645319c0 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -27,7 +27,7 @@ public static void filterSearchMenuItems(Menu menu, 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 static void filterSearchMenuItems(Menu menu, 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 static void setupHomeMenuItem(Menu menu, Resources resources) { } } - 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); diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java index aebe37816a72..1ca499e0776a 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java @@ -547,12 +547,12 @@ public static byte[] decodeStringToBase64Bytes(String string) { 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 static Cipher getCipher(int mode, byte[] encryptionKeyBytes, byte[] iv) t } 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]; diff --git a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index 878784b8edbf..630a1e3def90 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -106,6 +106,17 @@ public static String getTemporalPath(String accountName) { // 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. */ diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index c68c54329c1f..ddc8a80776e3 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -20,7 +20,6 @@ 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 @@ private 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 @@ private static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) 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() { - @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 @@ private static String getFileFromURI(String uri){ 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 @@ private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { 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); } } diff --git a/app/src/main/res/drawable/file_pdf.xml b/app/src/main/res/drawable/file_pdf.xml index 5dad2be84e59..8a39afb69257 100644 --- a/app/src/main/res/drawable/file_pdf.xml +++ b/app/src/main/res/drawable/file_pdf.xml @@ -7,10 +7,11 @@ + android:viewportWidth="96" + android:viewportHeight="96"> + + android:fillColor="#FF2116" + android:pathData="M76,12H20C15.6,12 12,15.6 12,20V76C12,80.4 15.6,84 20,84H76C80.4,84 84,80.4 84,76V20C84,15.6 80.4,12 76,12ZM38,46C38,49.2 35.2,52 32,52H28V60H22V36H32C35.2,36 38,38.8 38,42V46ZM58,54C58,57.2 55.2,60 52,60H42V36H52C55.2,36 58,38.8 58,42V54ZM74,42H68V46H74V52H68V60H62V36H74V42ZM48,42H52V54H48V42ZM28,42H32V46H28V42Z" /> + diff --git a/app/src/main/res/drawable/ic_expand_less.xml b/app/src/main/res/drawable/ic_expand_less.xml deleted file mode 100644 index a625653901e1..000000000000 --- a/app/src/main/res/drawable/ic_expand_less.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index b15a7699c382..0235efa7e6fb 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,9 +1,8 @@ - ~ SPDX-License-Identifier: AGPL-3.0-or-later + ~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH + ~ SPDX-License-Identifier: LicenseRef-NextcloudTrademarks --> + + + + + + + + + فشل إنشاء الحساب أيقونة الحساب الحساب غير موجود - مسح الملفات التي فشل رفعها تعديل إزالة جميع التنبيهات تفريغ سلة المهملات @@ -57,6 +56,8 @@ تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت. المُساعِد غير معروف + مُدخَلات + المُخرَجَات الحساب المرتبط غير موجود! فشل الوصول لـ: %1$s هذا الحساب لم تتم إضافته في هذا الجهاز بعد @@ -171,7 +172,7 @@ هل توَدُّ حقاً حذف %1$s وما يحتويه؟ هل توَدُّ حقاً حذف العناصر المختارة وما يحتوّه؟ محلياً فقط - خطأ في إنشاء حوار التعارض! + تعذّر إنشاء نافذة حوار حل التعارضات ملف متضارب %1$s ملف محلي إذا قمت باختيار كلا الاصدارين, الملف المحلي سيحتوي على رقم ملحق باسم الملف. diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml new file mode 100644 index 000000000000..ca5a3aeff86c --- /dev/null +++ b/app/src/main/res/values-ast/strings.xml @@ -0,0 +1,640 @@ + + + Aplicación %1$s p\'Android + Tocante + versión %1$s + Iconu de la cuenta + Editar + Balerar la papelera + Vista de rexella + Vista de llista + Carpeta nueva + Abrir con + Guetar + Detalles + Unviar + Axustes + Ordenar + Usuariu activu + Entá nun hai actividá + Unviar + Actividá + Amestar otru enllaz + Configuración avanzada + Buscar en: %s + Too + Escribi daqué + Falló + Planificóse + Completóse + Nun ye posible dir en cata de la llista de xeres. Comprueba la conexón a internet. + Nun ye posible dir en cata de los tipos de xeres. Comprueba la conexón a internet. + Asistente + Desconocí­u + Entrada + Salida + Yá esiste nel preséu una cuenta pal mesmu usuariu y sirvidor + L\'usuariu inxertáu nun concasa col usuariu d\'esta cuenta + Versión del sirvidor non reconocida + Conexón afitada + Nun s\'alcontró\'l sirvidor + Ensin conexón de rede + Conexón segura non disponible + Configuración del sirvidor mal fecha + Autorización ensin ésitu + Accesu refugáu pol sirvidor d\'autenticación + Conexón segura redirixida pente una ruta insegura. + Afitada conexón segura + Falló la inicialización SSL + Nun pudo verificase la identidá del sirvidor SSL + Probando conexón + El sirvidor tardó muncho en responder + Nome d\'usuariu o contraseña erroneos + ¡Asocedió un fallu desconocíu! + Nun pudo alcontrase l\'agospiu + %1$s nun permite cuentes múltiples + Nun pudo afitase la conexón + Avatar + Ausente + Zarrar + Desactivar + Calendariu + Calendarios + Hai un problema cargando\'l certificáu + Caxellu + Nun se puen borrar los avisos. + Borrar el mensaxe del estáu + Borrar el mensaxe l\'estáu dempués de + Testu copiáu dende %1$s + Copióse l\'enllaz + Atrás + Encaboxar + Encaboxar sincronización + Esbillar cuenta + Confirmar + Copiar + Desaniciar + Error + Nun hai abonda memoria + Fallu desconocíu + Cargando… + Siguiente + Non + Aceutar + Pendiente + Desaniciar + Renomar + Guardar + Unviar + Compartir + Saltar + Cambiar de cuenta + + Traducir + Namái llocal + Ficheru llocal + Contautos + Copióse nel cartafueyu + Asocedió un fallú entrín s\'intentaba copiar esta carpeta o ficheru + El ficheru yá ta presente na carpeta destín + Nun ye posible copiar. Comprueba si\'l ficheru esiste. + Copiar enllaz + Crear + Nun pudo crease la carpeta + Crear una carpeta + Desaniciar cuenta + Nun hai nenguna información disponible + Nun hai nengún anovamientu disponible. + Zarrar + Esti algorítmu de dixestión nun ta disponible nel tu teléfonu. + Desactivar + Escartar + Escargar l\'avisu + Nun molestar + Ficheru PDF + Xenerando\'l PDF… + Fecho + Nun borrar + Nun se pue crear el ficheru llocal + El nome ye inválidu pal ficheru llocal + Baxar la última versión de desendolcu + Nun pudo baxase %1$s + La descarga falló, volvi aniciar la sesión + Descarga fallida + Esti ficheru yá nun ta nel sirvidor + %1$d%% %2$s + %1$d%% Descargando %2$s + Baxando… + %1$s descargáu + Prodúxose un error mentanto se baxaben los ficheros + Entá non baxáu + Prodúxose un error inesperáu mentanto se baxaben los ficheros + Zarrar la barra llateral + Comunidá + Imaxe del fondu de la testera del caxón + Actividaes + Tolos ficheros + Asistente + Favoritos + Multimedia + Aniciu + Avisos + Nel preséu + Ficheros personales + Modificao apocayá + Compartío + Ficheros desaniciaos + Xubes + Zarrar la sesión + Abrir la barra llateral + %1$s de %2$s usao + %1$s n\'usu + Xuba automática + E2E nun ta configuráu + Nun ye posible ensin la conexón a internet + Asistente + Más + Notes + Charra + Más aplicaciones de Nextcloud + Nextcloud Notes + Nextcloud Talk + Nun se pue escoyer la direición de corréu electrónicu. + Configurar el cifráu + Descifrando… + Zarrar + Introduz la contraseña pa descrifrar la clave privada. + Esta carpeta nun ta balera. + Xenerando les claves nueves… + Contraseña… + Recuperando les claves… + Configurar el cifráu + Nun se pudieron guardar les claves, volvi tentalo. + Hebo un error mentanto se descifraba. ¿La contraseña ye incorreuta? + Introduz el nome del ficheru de destín + Introduz un nome pal ficheru + Nun se pudo copiar %1$s al ficheru llocal %2$s + Fallu críticu: Nun puen facese les operaciones + Hebo un error al escoyer la data + Hebo un error al comentar el ficheru + «%1$s» cascó + Hebo un error al crear a partir de la plantía + Hebo un error al amosar les aiciones el ficheru + Hebo un error al camudar l\'estáu del bloquéu + Informar + Hebo un error al recuperar el ficheru + Hebo un error al recuperar les plantíes + ¡Hebo un error al amosar el diálogu de configuración del cifráuª + Hebo un error al aniciar la cámara + Hebo un error al aniciar l\'escanéu del documentu + Cuentes + Nome del trabayu + Progresu + Estáu + Usuariu + UUID + Trabayos en segundu planu + Aniciar esti trabayu de prueba + Parar el trabayu de prueba + Preferencies + Tresferencia de ficheros + Camín remotu + Tresferir + Baxar + Xunir + Amestar o xubir + Nun se pue pasar el ficheru al xestor de descargues + Nun se pue imprentar el ficheru + Nun se pue aniciar l\'editor + Nun se pue anovar la IU + Meter en Favoritos + Meter en Favoritos + El nome yá esiste + Desaniciar + Hebo un error al recuperar les activadaes pal ficheru + Nun se puen cargar les detalles + Ficheru + Caltener + Nun se metió nada en Favoritos + Equí apaecen los ficheros y carpete que metas en Favoritos. + Equí nun hai ficheros + Ensin resultaos nesta carpeta + Ensin resultaos + Equí nun hai nada. Pes amestar una carpeta + ¿Quiciabes tea nuna carpeta diferente? + Entá nun se compartió nada + Nun s\'atopó nengún resultáu pa la to consulta + Carpeta + EN DIREUTO + Cargando… + Nun s\'afitó una aplicación pa remanar esta triba de ficheru. + hai segundos + Precísense permisos + Permisos d\'almacenameintu + Comprobando\'l destín… + Precísase más espaciu + Nun se pudo lleer el ficheru d\'orixe + Nun se pudo escribir nel ficheru de destín + Nun se pue anovar l\'índiz + Moviendo los datos… + Finó + Trocar + Restaurando la configuración de la cuenta… + Guardando la configuración de la cuenta… + ¡Nun se pue lleer la carpeta d\'orixe! + Anovando l\'índiz… + Usar + Esperando la sincronización completa… + Nun s\'atopó\'l ficheru + El ficheru nun se pudo sincronizar. Amuésase la última versión disponible + Renomar + ¡Hebo un error al restaurar la versión del ficheru! + Detalles + Descargar + Esportar + Sincronizar + Nun s\'esbillaron ficheros + El nome del ficheru nun pue tar baleru. + Caráuteres prohibíos: / \\ < > : \" | ? * + El nome del ficheru contién polo menos un caráuter inváldu + Nome del ficheru + La carpeta yá esiste + Crear + Equí nun hai nenguna carpeta + Escoyer + Escoyer la carpeta de destín + Copiar + Mover + Nun tienes permisu %s + pa copiar esti ficheru + pa crear esti ficheru + pa desaniciar esti ficheru + pa mover esti ficheru + pa renomar esti ficheru + Xubiendo los ficheros… + Nun se pudieron copiar dalgunos ficheros + Llocal: %1$s + Mover too + Llugar remotu: %1$s + Moviéronse tolos ficheros + Avanzar + 4 hores + Esti iconu amuesa la disponibilidá de la semeya en direuto + Nome + Nota + Contraseña + El sirvidor nun ta disponible + L\'agospiador del to sirvidor + Nun ye posible editar la imaxe. + ƒ/%s + ISO %s + %s MP + %s mm + %s s + na carpeta %1$s + Namái xubir al cargar + /XubidaNelIntre + La URL ye inválisa + Invisible + La etiqueta nun pue tar baleru + Enllaz + Nome del enllaz + Permitir la xuba y la edición + Suelta de ficheros (namás xuba) + Ver namás + Cargar más resultaos + Nun hai ficheros nesta carpeta. + %1$s/%2$s + Nun hai más carpetes. + Data de caducidá: %1$s + Aplicación de rexistros d\'Android %1$s + Aniciar la sesión + Anovar + Cargando… + Llimpiar datos + Alministrar espaciu + Nun pudo lleese\'l ficheru de medios + El ficheru de medios tien una codificación incorreuta + Escoso\'l tiempu pa reproducir el ficheru + El reproductor multimedia integráu nun ye a reproducir el ficheru + Códec multimedia non soportáu + Botón d\'avance rápidu + Botón de reproducción o posa + Botón de rebobináu + Lo más nuevo + Lo más vieyo + A - Z + Z - A + Lo más grande + Lo más pequeño + Más + Asocedió un fallu entrín s\'intentaba mover esta carpeta + Nun ye posible mover una carpeta a les sos soxacentes + El ficheru yá ta presente na carpeta de destín + Nun ye posible mover el ficheru. Comprueba si esiste. + Comentariu nuevu… + semeya + videu + Avisu nuevu + Creóse la versión + Nun hai nenguna aición pa esti usuariu + Nun hai nenguna aplicación pa remanar esto + Nun esiste nengún calendariu + Nun hai nenguna aplicación pa remanar esta direición de corréu electrónica + Nun hai nengún elementu + Nun s\'atopón nenguna aplicación pa remanar mapes + Namás se permite una cuetna + Nun hai nenguna aplicación disponible pa remanar PDF + Nun hai nenguna aplicación disponible pa unviar los ficheros seleicionaos + Seleiciona polo menos un permisu pa compartir + Nun se pudo unviar la nota + Iconu de la nota + Nun se pue executar l\'aición. + Amuesa\'l progresu de la descarga + Descargués + Amuesa\'l progresu y los resultaos de la sincronización de ficheros + Sincronización de ficheros + Avisos xenerales + Progresu del reproductor de música + Reproductor multimedia + Avisos push + Amuesa\'l progresu de la xuba + Xubes + Ensin avisos + Volvi equí dempués. + Nun hai conexón a internet + 1 hora + En llinia + Estáu en llinia + El sirvidor algamó la fin de vida. ¡Anueva! + Menú de Más + Introduz el to códigu de pasu + La contraseña va ser solicitada cada vegada que s\'anicie l\'aplicación + Introduz el to códigu de pasu, por favor + Les contraseñes nun son les mesmes + Por favor, vuelvi inxertar la contraseña + Desaniciar el códigu d\'accesu + Desanicióse\'l códigu d\'accesu + Contraseña almacenada + Contraseña incorrecta + Nun ye posible abrir el PDF protexíu con contraseña. Usa un visor de PDFs esternu. + Toca nuna páxina p\'averar + Permitir + Negar + Ríquense permisos adicionales pa xubir y baxar ficheros. + Abrir «%1$s» + 389 KB + MarcadorDePosicion.txt + 12:23:45 + Esto ye un marcador de posición + 2012/05/18 12:23 PM + desanicióse + guardáu en carpeta orixinal + movíu a la carpeta d\'aplicaciones + ¿Qué faigo que\'l ficheru yá esiste? + Saltar la xuba + Sobrescribir la versión remota + Renomar la versión nueva + ¿Qué faigo que\'l ficheru yá esiste? + Amestar cuenta + Tocante a + Detalles + Xeneral + Más + Ayuda + Usar socarpetes + Llicencia + Nada + Còdigu d\'accesu + Xestionar cuentes + Quitar llocalmente\'l cifráu + Amosar el conmutador d\'aplicaciones + Amosar ficheros anubríos + Consiguir el códigu fonte + Carpeta llocal + Carpeta remota + Estilu + Escuridá + Claridá + Previsualización d\'imaxe + Nun pue amosase la imaxe + Sentímoslo + Privacidá + Nome nuevu + ¡Nun se pudo lleer el códigu QR! + ¡Prueba %1$s nel preséu! + %1$s o %2$s + Anovar el conteníu + Volver cargar + (remotu) + ¡Nun se pue atopar el ficheru! + Pues quitar el cifráu d\'estremu a estremu llocalmente nesti veceru + El desaniciu falló + Quitar la cuenta llocal + Quitar la cuenta del preséu y desaniciar tolos ficheros llocales + Nun se pue quitar l\'avisu. + Quitar + Desanicióse + Introduz un nome nuevu + Nun se pudo renomar la copia llocal, prueba con otru nome + El cambéu de nome nun ye posible, el nome yá ta n\'usu + Solicitar el desaniciu de la cuenta + Solicitar el desaniciu + Nun se pue volver compartir + Nun se pue volver compartir + Restaurar el ficheru + Restaurar el ficheru desainiciáu + Recuperando\'l ficheru… + ¡Nun se pue cargar el documentu! + Aniciar la sesión per QR + Escaniar la páxina + nun llugar + Xuba automática + pa les semeyes y vídeos + Calendariu y contautos + Hebo un error al consiguir los resultaos de busca + Esbillar too + Unviar + Afitar l\'estáu + Afitar el mensaxe del estáu + Compartir + Compartiendo + %1$s + Compartir %1$s + %1$s (gropu) + Compartir l\'enllaz internu + Compartir l\'enllaz + Tienes qu\'introducir una contraseña + Nun ye posible compartir. Comprueba si\'l ficheru esiste. + pa compartir esti ficheru + Introduz una contraseña + Afitar la data de caducidá + Afitar la contraseña + Pue editar + Suelta de ficheros + Ver namás + %1$s (remotu) + %1$s (conversación) + Nota al destinatariu + Configuración + Anubrir la descarga + Compartir enllaz + Unviar l\'enllaz + Amosar les semeyes + Amosar los vídeos + Rexistrase con un fornidor + ¿Quies permitr que %1$s acceda a la cuenta de Nextcloud %2$s? + Ordenar per + Anubrir + Detalles + Nun se pudo verificar la identidá del sirvidor + País: + Nome común: + Llocalización: + Organización: + Unidá organizativa: + Estáu: + Robla + Emitíu por: + Robla: + Algoritmu: + Emitíu pa: + Validez: + De: + A: + - Nun atopamos información tocante a esti fallu + Nun pudo guardase\'l certificáu + Nun pudo amosase\'l certificáu. + ¿Quies enfotate nesti certificáu de toes formes? + - El certificáu del sirvidor caducó + - El certificáu del sirvidor nun ye de confianza + - Les dates de validez del certificáu tán nel futuru + - La URL nun casa col nome del sirvidor nel certificáu + Mensaxe del estáu + Cámara + Por defeutu + Documentos + Descargués + Almacenamientu internu + Filmes + Música + Accesu completu + Semeyes + Añu/Mes/Día + Añu/Mes + Añu + Compartiose \"%1$s\" contigo + %1$s compartío \"%2$s\" contigo + Namás les semeyes + Semeyes y vídeos + Namás los vídeos + Suxerir + Conflictos alcontraos + La carpeta %1$s yá nun esiste + Nun se pudo sincronizar «%1$s» + Contraseña incorreuta pa %1$s + Fallu al caltener ficheros sincronizaos. + Sincronización fallida + La sincronización fallo, volvi aniciar la sesión + El conteníu del ficheru yá ta sincronizáu + Dexáronse dalgunos ficheros llocales. + Nun hai abondu espaciu + Ficheros + Botón de Configuración + Configurar les carpetes + Nun s\'atopó nenguna carpeta multimedia + Pa %1$s + Tipu + Etiquetes + Probar la conexón del sirvidor + 30 minutos + Esta selmana + Miniatura + Güei + Ficheros desaniciaos + Nun hai nengún ficheros desnaiciáu + ¡Nun se pudo restaurar el ficheru «%1$s»! + Desaniciar permanentemente + ¡La carga de la papelera falló! + ¡Los ficheros nun se pudieron desaniciar permanentemente! + Desbloquiar el ficheru + Esisten comentarios ensin lleer + Quitar de Favoritos + Prodúxose un error mentanto se dexaba de compartir esti ficheru o esta carpeta + Nun ye posible dexar de compartir. Comprueba si\'l ficheru esiste. + pa dexar de compartir esti ficheru + Nun ye posible anovar. Comprueba si\'l ficheru esiste. + p\'anovar esta compartición + Siguir coles descargues anulaes + Posar toles descargues + Siguir con toles descargues + Nun se pue crear el ficheru llocal + Nome de ficheru + Triba de ficheru + Toles descargues tán posaes + Desaniciar + Nun hai xubes disponibles + Error desconocíu + Escoyer + Xubir + Los datos recibíos nun inclúin un ficheru válidu. + %1$s nun tien permisu pa lleer un ficheru recibíu + Nun pudo copiase\'l ficheru a una carpeta temporal. Prueba a reunvialu. + Esti ficheru nun se pue xubir + Ensin ficheru pa xubir + Nome de la carpeta + Escueyi una carpeta de xuba + Nun pudo xubise %1$s + Xuba fallida + Opción de xuba: + Caltener ficheru na carpeta fonte + pa xubir esta carpeta + %1$d%% %2$s + %1$d%% Xubiendo %2$s + Xubiendo… + %1$s xubíu + Colar + Configuración + Nun hai nenguna cuenta %1$s nel to preséu. Por favor configura una cuenta enantes. + Nun s\'alcontraron cuentes + Actual + Xubíu + Encaboxáu + Esperando pa xubir + Xubes + Encaboxáu + Conflictu + Fallu de conexón + Fallu de credenciales + Fallu de ficheru + Fallu de carpeta + Ficheru llocal nun s\'atopó + Fallu de permisu + Diendo en cata de la versión del sirvidor… + Aplicación finada + Completáu + Fallu desconocíu + Detectóse un virus. ¡Nun se pue completar la xuba! + Usuariu + Direición + Corréu electrónicu + Númberu telefónicu + Twitter + Sitiu web + Hebo un error al recuperar la información del usuariu + Nome d\'usuariu + Baxar + Espera un momentu… + Comprobación de credenciales almacenaes + Copiando ficheru dende l\'almacenamientu priváu + Anovar + Saltar + ¿Cuál ye\'l to estáu? + Nun ta disponible + Baxando los ficheros… + Unviar un corréu electrónicu + + %d na seleición + %d na seleición + + diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index cb05124dde4e..8d57743fb01e 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -7,7 +7,6 @@ Account creation failed Account icon Account not found! - Clear failed uploads Edit Clear all notifications Empty trash bin @@ -57,6 +56,8 @@ Unable to fetch task types, please check your internet connection. Assistant Unknown + Input + Output Associated account not found! Access failed: %1$s The account is not added on this device yet @@ -171,7 +172,7 @@ Do you really want to delete %1$s and its content? Do you really want to delete the selected items and their contents? Local only - Error creating conflict dialogue! + Conflict resolver dialog cannot be created Conflicting file %1$s Local file If you select both versions, the local file will have a number appended to its name. @@ -353,7 +354,7 @@ File Keep Upload some content or sync with your devices. - Nothing favourited yet + Nothing has been marked as favourite yet Files and folders you mark as favorites will show up here. Found no images or videos No files here diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 33f8be278b6a..ed821129395f 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -7,7 +7,6 @@ Създаването на профила се провали Профил Няма намерен профил! - Изчисти провалените качвания Променяне Премахнете известията Изпразване на кошчето @@ -153,7 +152,6 @@ Наистина ли желаете %1$s и съдържанието ѝ да бъдат изтрито? Наистина ли желаете избраните елементи и съдържанието им да бъдат премахнати? Само локално - Грешка при създаване на диалог за конфликт! Несъвместим файл %1$s Локален файл Ако изберете и двете версии, ще бъде добавен номер към името на локалния файл. diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index f2b9de3335e5..ddafa9066641 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -7,7 +7,6 @@ C\'hwitet eo bet ar c\'houiñ eus anr c\'hont Ikonenn kont N\'o ket bet kavet ar c\'hont - Lemmel ar pellgasoù chwitet Cheñch Lemmel toud ar c\'hemenadennoù Kas/Rannan @@ -136,7 +135,6 @@ Sur oc\'h lemel %1$s ha pep tra a zo barzh ? Sur oc\'h lemel an traoù choazet ha pep tra a zo barzh ? E-berzh nemetken - Ur fazi en deus krouet divizoù diemglev ! Ma choazit an daou stumm, ar restr diabarzh en do un niver ouzhpennet war e anv. Skeudenn implijer evit ar roll darempredoù Oaetre ebet roet, netra a zo bet emporzhiet. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 3f595ad52985..2347b3dd86aa 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -7,7 +7,6 @@ Ha fallat la creació del compte Icona del compte No s\'ha trobat el compte! - Esborra les pujades amb errors Edita Neteja totes les notificacions Buida la paperera @@ -152,7 +151,6 @@ Esteu segur que voleu suprimir %1$s i els seus continguts? Esteu segur que voleu suprimir els elements seleccionats i el seu contingut? Només local - Error creant diàleg de conflicte! Fitxer en conflicte %1$s Fitxer local Si seleccioneu ambdues versions, s\'afegirà un numero al nom del fitxer local. diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 9ede6b4bddbf..0341e0184497 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -7,7 +7,6 @@ Vytvoření účtu se nezdařilo Ikona účtu Účet nenalezen! - Vyčistit nezdařená nahrávání Upravit Vyčistit všechna upozornění Vyprázdnit koš @@ -43,6 +42,8 @@ Dokončeno Úloha úspěšně smazána Neznámé + Vstup + Výstup Související účet nenalezen! Přístup se nezdařil: %1$s Účet zatím není na tomto zařízení přidán @@ -157,7 +158,6 @@ Opravdu chcete %1$s a jeho obsah odstranit? Opravdu chcete vybrané položky a jejich obsah odstranit? Pouze místní - Chyba při vytváření dialogu ke konfliktu! Kolidující soubor %1$s Místní soubor Pokud zvolíte obě verze, k názvu místního souboru bude připojeno číslo. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 0b2cce94bdef..3acb2da04274 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -7,7 +7,6 @@ Fejl ved oprettelse af konto Kontoikon Konto blev ikke fundet. - Fjern fejlede uploads Redigér Ryd alle notifikationer Tøm papirkurv @@ -47,6 +46,7 @@ Planlagt Fuldført Opgaven er oprettet + En fejl opstod under sletning af opgaven Opgaven er slettet Slet opgave Assistent @@ -164,7 +164,6 @@ Er du sikker på at du vil slette %1$s med indhold? Er du sikker på at du vil slette de valgte artikler med indhold? Kun lokal - Fejl ved skabelse af konfliktdialog Fil i konflikt %1$s Lokal fil Hvis du vælger begge versioner, vil den lokale fil få tilføjet et nummer til sit navn. @@ -354,6 +353,7 @@ Intet er delt endnu Ingen resultater fundet for din forespørgsel mappe + LIVE Indlæser... Ingen app er sat op til at håndtere denne filtype. sekunder siden @@ -592,6 +592,7 @@ Udvikling Generel Mere + Daglig backup af din kalender og kontakter Daglig backup af dine kontakter Uventet fejl under opsætning af DAVx5 (tidligere kendt som DAVdroid) End-to-end kryptering er blevet konfigureret! @@ -665,6 +666,7 @@ Enheds legitimationsoplysninger er sat op Lokal kopi kunne ikke omdøbes, prøv et andet navn Omdøbning ikke mulig, navnet eksisterer Anmodning om sletning af konto + Anmod om sletning Gendeling er ikke tilladt Videredeling ikke tilladt Intet skaleret billede tilgængeligt. Hent fuldt billede? @@ -685,9 +687,11 @@ Enheds legitimationsoplysninger er sat op Alle dine konti på et sted Automatisk upload + for dine billeder og videoer Kalender og kontakter Synkronisér med DAVx5 Fejl ved indhenting af søgeresultater + Sikker deling er ikke sat op for denne bruger Vælg alle Indstil mediemappe Vælg venligst en skabelon @@ -800,6 +804,7 @@ Enheds legitimationsoplysninger er sat op \"%1$s\" er blevet delt med dig %1$s delte \"%2$s\" med dig Kun billeder + Billeder og videoer Kun videoer Foreslå Konflikter fundet diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bff7d365d391..5761430919e7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -7,7 +7,6 @@ Kontoerstellung fehlgeschlagen Konto-Icon Kein Konto gefunden - Fehlgeschlagene Uploads entfernen Bearbeiten Alle Benachrichtigungen löschen Papierkorb leeren @@ -57,6 +56,8 @@ Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung. Assistent Unbekannt + Eingabe + Ausgabe Verknüpftes Konto nicht gefunden! Zugriffsfehler: %1$s Das Konto ist bislang auf dem Gerät nicht vorhanden @@ -171,7 +172,7 @@ Wollen Sie %1$s und deren Inhalte wirklich löschen? Möchten Sie die ausgewählten Elemente und deren inhalt wirklich löschen? Nur lokal - Fehler beim Erstellen des Konfliktdialoges + Konfliktlösungsdialog konnte nicht erstellt werden Konflikt-Datei %1$s Lokale Datei Falls beide Versionen gewählt werden, wird bei der lokalen Datei eine Zahl am Ende des Dateinamens hinzugefügt. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 16962a8c980b..f1be75ab8a5e 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -7,7 +7,6 @@ Αποτυχία δημιουργίας λογαριασμού Εικονίδιο λογαριασμού Δεν βρέθηκε λογαριασμός! - Εκκαθάριση αποτυχημένων μεταφορτώσεων Επεξεργασία Εκκαθάριση ειδοποιήσεων Άδειασμα απορριμάτων @@ -151,7 +150,6 @@ Θέλετε σίγουρα να διαγράψετε το %1$s και τα περιεχόμενά του; Θέλετε να διαγράψετε τα επιλεγμένα αντικείμενα και τα περιεχόμενά τους; Μόνο τοπικά - Σφάλμα δημιουργίας αναφοράς διένεξης! Αρχείο σε αντίφαση %1$s Τοπικό αρχείο Εάν επιλέξετε και τις δύο εκδόσεις, στο όνομα του τοπικού αρχείου θα προστεθεί ένας αριθμός. diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index fd216e897d92..dc5eb4c77df6 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -7,7 +7,6 @@ Kreado de konto malsukcesis Konta piktogramo Konto ne trovita! - Vakigi malsukcesajn alŝutojn Modifi Forviŝi ĉiujn sciigojn Malpleni rubujon diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 743254e1237e..7e55c9bf6eba 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -7,7 +7,6 @@ Falló la creación de la carpeta Icono de cuenta Cuenta no encontrada! - Borrar subidas fallidas Editar Borrar todas las notificaciones Vaciar la papelera @@ -150,7 +149,6 @@ ¿Realmente quieres eliminar %1$s y su contenido? ¿Realmente desea eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Error al crear el diálogo de conflicto! Archivo en conflicto %1$s Archivo local Si selecciona ambas versiones, se le agregará un número al nombre del archivo copiado. diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index e156f7a25574..65ce9cf03eb5 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rCO/strings.xml b/app/src/main/res/values-es-rCO/strings.xml index db582242d556..0d6222e01963 100644 --- a/app/src/main/res/values-es-rCO/strings.xml +++ b/app/src/main/res/values-es-rCO/strings.xml @@ -6,7 +6,6 @@ Fallo en la creación de la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Vacíar la papelera diff --git a/app/src/main/res/values-es-rCR/strings.xml b/app/src/main/res/values-es-rCR/strings.xml index b1315dd71b0b..d62040810d2b 100644 --- a/app/src/main/res/values-es-rCR/strings.xml +++ b/app/src/main/res/values-es-rCR/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rDO/strings.xml b/app/src/main/res/values-es-rDO/strings.xml index 80762f0262a5..2df7ebef158d 100644 --- a/app/src/main/res/values-es-rDO/strings.xml +++ b/app/src/main/res/values-es-rDO/strings.xml @@ -7,7 +7,6 @@ Error al crear la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Enviar/Compartir diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 5e4079a39438..d47f677e77c6 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -7,7 +7,6 @@ Creación de cuenta fallida Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Borrar todas las notificaciones Eliminar papelera de reciclaje @@ -153,7 +152,6 @@ ¿Realmente quieres eliminar %1$s y sus contenidos? ¿Reamente deseas eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Error al crear el diálogo de conflicto! Archivo en conflicto %1$s Archivo local Si selecciona ambas versiones, el archivo local tendrá un número agregado a su nombre. diff --git a/app/src/main/res/values-es-rGT/strings.xml b/app/src/main/res/values-es-rGT/strings.xml index db65d670f3de..fff2dea4f39e 100644 --- a/app/src/main/res/values-es-rGT/strings.xml +++ b/app/src/main/res/values-es-rGT/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 2854e8c326c9..491cc36e7082 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -7,7 +7,6 @@ Falló la creación de la cuenta Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Limpiar todas las notificaciones Vaciar la papelera de reciclaje @@ -57,6 +56,8 @@ No se pudieron obtener los tipos de tareas, por favor, revise su conexión a internet. Asistente Desconocido + Entrada + Salida ¡No se encontró la cuenta asociada! Acceso fallido:%1$s La cuenta aún no ha sido agregada a este dispositivo @@ -171,7 +172,7 @@ ¿Realmente quieres eliminar %1$s y sus contenidos? ¿Reamente deseas eliminar los elementos seleccionados y sus contenidos? Sólo local - ¡Se presentó un error al crear el diálogo de conflictos! + El diálogo de resolución de conflictos no puede ser creado Archivo conflictivo %1$s Archivo local Si seleccionas ambas versiones, el archivo local tendrá un número al final del nombre. diff --git a/app/src/main/res/values-es-rSV/strings.xml b/app/src/main/res/values-es-rSV/strings.xml index b1315dd71b0b..d62040810d2b 100644 --- a/app/src/main/res/values-es-rSV/strings.xml +++ b/app/src/main/res/values-es-rSV/strings.xml @@ -5,7 +5,6 @@ versión %1$s Ícono de la cuenta ¡No se encontró la cuenta! - Borrar cargas fallidas Editar Enviar/Compartir Vista de cuadrícula diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 10f3ec42ef1e..55ad1cdc1843 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -7,7 +7,6 @@ Ha fallado la creación de la cuenta Icono de la cuenta ¡Cuenta no encontrada! - Borrar subidas fallidas Editar Limpiar todas las notificaciones Vaciar papelera @@ -47,6 +46,8 @@ ¡La tarea fue eliminada exitósamente! Eliminar tarea Desconocido + Entrada + Salida ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta no se ha añadido aún en este dispositivo @@ -160,7 +161,6 @@ ¿Realmente deseas eliminar %1$s y todo su contenido? ¿Estás seguro de que quieres eliminar los elementos seleccionados y sus contenidos? Solo local - ¡Error al crear el diálogo de conflicto! Conflicto en archivo %1$s Archivo local Si seleccionas ambas versiones, el archivo local tendrá un número añadido a su nombre. diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 3fd7190e6492..bd34acccb120 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -7,7 +7,6 @@ Konto loomine ebaõnnestus Konto ikoon Kontot ei leitud! - Puhasta ebaõnnestunud üleslaadimised Redigeeri Kustuta kõik teavitused Tühjenda prügikast @@ -136,7 +135,6 @@ Oled sa kindel, et soovid %1$s ja selle sisu kustutada? Oled sa kindel, et soovid valitud objektid ja nende sisud kustutada? Ainult kohalik - Konfliktidialoogi loomine ebaõnnestus! Kohalik fail Kui valid mõlemad versioonid, siis lisatakse kohaliku faili nimele number. Serveri fail diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 4fdfacf562e3..732363ee662f 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -7,7 +7,6 @@ Kontuaren sorrerak huts egin du Kontuaren ikonoa Ez da kontua aurkitu! - Garbitu huts egin duten igoerak Aldatu Garbitu jakinarazpen guztiak Hustu zakarrontzia @@ -47,6 +46,8 @@ Ezabatu zeregina Morroia Ezezaguna + Sarrera + Irteera Ez da aurkitu lotutako konturik Huts egin du atzitzean: %1$s Kontua ez da gailu honetan gehitu oraindik @@ -161,7 +162,6 @@ Ziur zaude %1$s eta bere edukia ezabatu nahi duzula? Ziur zaude hautatutako elementuak eta beren edukiak ezabatu nahi dituzula? Lokala bakarrik - Errorea gatazkaren elkarrizketa-koadroa sortzean! %1$sfitxategi gatazkatsua Fitxategi lokala Bi bertsioak hautatzen badituzu, fitxategi lokalaren izenari zenbaki bat gehituko zaio. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index ffa200e71365..a11913415a69 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -7,7 +7,6 @@ ایجاد حساب ناموفق بود آیکون حساب حساب پیدا نشد! - پاک کردن آپلودهای ناموفق ویرایش پاک کردن تمام اعلانها خالی کردن سطل زباله @@ -158,7 +157,6 @@ آیا واقعا می خواهید %1$s و محتویات آن را حذف کنید؟ آیا واقعاً می‌خواهید موارد انتخاب شده و محتوای آنها حذف شود؟ فقط محلی - هنگام ساختن دیالوگ مغایرت‌ها خطایی رخ داده است! فایل متناقض %1$s پروندهٔ محلّی اگر هردو نسخه را انتخاب کنید، یک شماره به نام فایل محلی اضافه خواهد شد. diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 0acc4e060dbc..e68acdc7ad27 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -7,7 +7,6 @@ Tilin luonti epäonnistui Tilikuvake Tiliä ei löydy! - Tyhjennä epäonnistuneet lähetykset Muokkaa Hylkää kaikki ilmoitukset Tyhjennä roskakori @@ -158,7 +157,6 @@ Haluatko varmasti poistaa kohteen %1$s ja sen sisällön? Haluatko varmasti poistaa valitut kohteet ja niiden sisällön? Vain paikallisen - Virhe konfliktitietojen näyttämisessä! Ristiriitainen kohde %1$s Paikallinen tiedosto Jos valitset molemmat versiot, paikallisen tiedoston nimeen lisätään numero. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ae580052e56a..10a08b91346f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -7,16 +7,15 @@ Échec de création du compte Icône du compte Compte introuvable ! - Effacer les téléversements échoués Modifier Effacer toutes les notifications Vider la corbeille Envoyer / Partager Affichage mosaïque Affichage liste - Restore contacts and calendar + Restaurer les contacts et l\'agenda Nouveau dossier - Move or Copy + Déplacer ou copier Ouvrir avec Rechercher Propriétés @@ -37,26 +36,28 @@ Autoriser le repartage Affiche un widget du tableau de bord Recherche dans %s - All - Type some text - Are you sure you want to delete this task? - Delete Task + Tout + Tapez du texte + Êtes-vous sûr de vouloir supprimer cette tâche ? + Supprimer la tâche Échec - Task List are loading, please wait - No task available. Select a task type to create a new task. - No task available for %s task type, you can create a new task from bottom right. + La liste des tâches charge, veuillez patienter + Aucune tâche disponible. Sélectionner un type de tâche pour créer une nouvelle tâche. + Aucune tâche disponible pour le type de tâche %s, vous pouvez créer une nouvelle tâche en bas à droite. En cours Planifié Terminé - An error occurred while creating the task - Task successfully created + Une erreur est survenue lors de la création de la tâche + La tâche a bien été créée Une erreur est survenue lors de la suppression de la tâche Tâche supprimée avec succès Impossible de récupérer la liste des tâches, veuillez vérifier votre connexion Internet. - Delete Task + Supprimer la tâche Impossible de récupérer les types des tâches, veuillez vérifier votre connexion Internet. Assistant Inconnu + Entrée + Sortie Compte associé introuvable ! L\'accès a échoué: %1$s Le compte n\'est pas encore ajouté sur cet appareil @@ -95,11 +96,11 @@ Définir un dossier personnalisé Désactiver l\'économie de batterie Masquer le dossier - Preparing auto upload + Préparation du téléversement automatique Avatar Absent Paramètres de sauvegarde - Contacts and calendar backup + Sauvegarder les contacts et l’agenda Fermer Désactiver L\'optimisation de la batterie est peut-être active sur votre appareil. Le téléversement automatique ne fonctionne correctement que si vous excluez cette application de l\'optimisation. @@ -171,13 +172,12 @@ Voulez-vous vraiment supprimer %1$s et ses contenus ? Souhaitez-vous vraiment supprimer les éléments sélectionnés ainsi que leurs contenus ? Local seulement - Une erreur qui crée un dialogue conflictuel ! Fichier %1$s en conflit fichier local Si vous sélectionnez les deux versions, le fichier local aura un numéro ajouté à son nom. Fichier serveur Sauvegarde des contacts - Contact permission is required. + La permission d’accès à vos contacts est requise L\'icône de l\'utilisateur pour la liste des contacts Pas de permission donnée, rien n\'a été importé. Contacts @@ -246,10 +246,10 @@ Réception en cours… %1$s reçu Reçu - Certain files were canceled during the download by user - Error occurred while downloading files + Certains fichiers ont été annulés pendant le téléchargement par l’utilisateur + Une erreur est survenue lors du téléchargement des fichiers Pas encore téléchargé - Unexpected error occurred while downloading files + Erreur inattendue survenue lors du téléchargement des fichiers Fermer le panneau latéral Communauté Image d’arrière-plan de l’en-tête du menu-tiroir @@ -262,7 +262,7 @@ Accueil Notifications Sur l’appareil - Personal files + Fichiers personnels Modifiés récemment Partagé Fichiers supprimés @@ -281,7 +281,7 @@ Plus d’applications Nextcloud Nextcloud Notes Nextcloud Talk - Failed to pick email address. + Le choix de l’adresse électronique a échoué. Définir comme chiffré Configurer le chiffrement Décryptage… @@ -316,8 +316,7 @@ Erreur au démarrage de l\'appareil photo Erreur lors de la numérisation du document Comptes - Times run in 48h - Created + Créée Nom de la tâche Progression État @@ -367,7 +366,7 @@ Aucun partage Aucun résultat trouvé pour votre requête dossier - LIVE + EN DIRECT Chargement… Aucune application trouvée pour ce type de fichier. à l’instant @@ -412,16 +411,16 @@ Le nom du fichier contient au moins un caractère invalide Nom du fichier Sécurisez et contrôlez vos données - Secure collaboration and file exchange - Easy-to-use webmail, calendar and contacts - Screensharing, online meetings and web conferences + Collaboration et échange de fichiers sécurisés + Webmail, agenda, contacts faciles à utiliser + Partage d’écran, réunions en ligne et conférences en ligne Le dossier existe déjà Créer Aucun dossier Choisir - Choose target folder - Copy - Move + Sélectionner le dossier cible + Copier + Déplacer Vous n\'avez pas la permission %s de copier ce fichier de créer ce fichier @@ -436,7 +435,7 @@ Tous les fichiers ont été déplacés Suivant 4 heures - This icon indicates availability of live photo + Cette icône indique la disponibilité de live photo Le nom résultera en un fichier caché Nom Note @@ -461,7 +460,7 @@ %s s dans le dossier %1$s Téléverser aussi les fichiers existants - Téléverser uniquement si le chargeur est branché + Téléverser uniquement lors de la recharge /InstantUpload URL invalide Invisible @@ -602,16 +601,16 @@ Renommer la nouvelle version Que faire si le fichier existe déjà ? Ajouter un compte - Sync calendar and contacts + Synchroniser l’agenda et les contacts Ni Google Play Store ou F-Droid ne sont installés Configurer DAVx5 (autrefois connu comme DAVdroid) (v1.3.0+) pour le compte actuel - Calendar and contacts sync set up + La synchronisation de l\'agenda et des contacts a bien été configurée. À propos Préférences Dév Général Plus - Daily backup of your calendar and contacts + Sauvegarde quotidienne de votre agenda et des contacts Sauvegarde quotidienne de vos contacts Erreur inattendue lors de la configuration de DAVx5 (anciennement connu sous le nom de DAVdroid) Le chiffrement de bout en bout est configuré ! @@ -624,8 +623,8 @@ Mentions Le fichier original sera… Le fichier original sera… - Exclude hidden files and folders - Exclude hidden + Exclure les fichiers et dossiers cachés + Exclure les cachés Stockage dans des sous-dossiers se basant sur la date Utiliser des sous-dossiers Options du sous-dossier @@ -674,22 +673,22 @@ Vous pouvez retirer le chiffrement de bout en bout localement sur ce client Vous pouvez retirer le chiffrement de bout en bout localement sur ce client. Les fichiers chiffrés resteront sur le serveur, mais ne seront plus synchronisés avec cet ordinateur. Échec de la suppression - Remove local account - Remove account from device and delete all local files + Supprimer le compte local + Supprimer le compte de l\'appareil et supprimer tous les fichiers locaux Erreur lors de la suppression des notifications. Supprimer Supprimé Entrez un nouveau nom Impossible de renommer la version locale; veuillez réessayer avec un nom différent Impossible de renommer, le nom est déjà utilisé - Request account deletion - Request deletion - Request permanent deletion of account by service provider + Demander la suppression du compte + Demander la suppression + Demander la suppression permanente du compte par le fournisseur de services Le repartage n\'est pas autorisé Le repartage est interdit Aucune image réduite disponible. Télécharger l\'image originale ? Restaurer le fichier - Restaurer la sauvegarde + Restaurer une sauvegarde Restaurer le fichier supprimé Restaurer la sélection Téléchargement en cours… @@ -705,12 +704,12 @@ Tous vos comptes en un seul endroit Téléversement automatique - for your photos and videos - Calendar and contacts + pour vos photos et vidéos + Agenda et contacts Synchroniser avec DAVx5 Erreur lors de l\'obtention des résultats de recherche - Secure sharing is not set up for this user - Secure share … + Le partage sécurité n’est pas configuré pour cet utilisateur + Partage sécurisé… Tout sélectionner Paramétrer le dossier média Veuillez sélectionner un modèle @@ -725,7 +724,7 @@ Enregistrer le message d\'état Pendant la configuration du chiffrement de bout en bout, vous recevrez une phrase secrète aléatoire de 12 mots dont vous aurez besoin pour ouvrir vos fichiers sur d\'autres appareils. Cette phrase secrète ne sera stockée que sur cet appareil, et pourra être affichée à nouveau sur cet écran. Veuillez la noter en lieu sûr ! Partager - Share and Copy Link + Partager et copier le lien Partage %1$s Expire le %1$s @@ -745,7 +744,7 @@ Partager le lien (%1$s) Définir une date d\'expiration Définir un mot de passe - Resharing is not allowed during secure file drop + Le repartage n’est pas autorisé pendant le dépôt d’un fichier sécurisé Protégé par mot de passe Peut modifier Dépôt de fichier @@ -769,7 +768,7 @@ partagé par lien Partagé avec vous par %1$s L\'ajout du partage a échoué - Adding share failed. This file or folder has already been shared with this person or group. + L\'ajout du partage a échoué. Ce fichier ou dossier a déjà été partagé avec cette personne ou ce groupe. Afficher les photos Afficher les vidéos Se connecter avec un fournisseur @@ -812,7 +811,7 @@ Accès complet Média en lecture seule Images - The self-hosted productivity platform that keeps you in control.\n\nFeatures:\n* Easy, modern interface, suited to the theme of your server\n* Upload files to your Nextcloud server\n* Share them with others\n* Keep your favorite files and folders synced\n* Search across all folders on your server\n* Auto Upload for photos and videos taken by your device\n* Keep up to date with notifications\n* Multi-account support\n* Secure access to your data with fingerprint or PIN\n* Integration with DAVx5 (formerly known as DAVdroid) for easy setup of calendar and contacts synchronization\n\nPlease report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android\n\nNew to Nextcloud? Nextcloud is a private file sync and share and communication server. It is libre software, and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else.\n\nCheck out Nextcloud at https://nextcloud.com + La plateforme de productivité auto-hébergée qui vous redonne le contrôle.\n\nFonctionnalités: \n* Intuitif, interface moderne, adaptée au thème de votre serveur.\n* Envoyez des fichiers sur votre serveur Nextcloud\n* Partagez avec d\'autres personnes\n* Gardez vos fichiers et dossiers préférés synchronisés\n* Recherchez dans tous les dossiers de votre serveur\n* Téléversement automatique des photos et vidéos prises par votre appareil\n* Restez à jour avec les notifications\n* Supporte plusieurs comptes simultanément\n* Accès sécurisé avec votre empreinte digitale ou un PIN\n* Intégration avec DAVx5 (ancien nom : DAVdroid) pour configurer facilement l\'agenda&et synchroniser les contacts\n\nMerci de signaler les problèmes sur https://github.com/nextcloud/android/issues et discutez de cette application sur https://help.nextcloud.com/c/clients/android\n\nNew sur Nextcloud ? Nextcloud est un serveur privé de synchronisation de fichiers&de partage et de communications. C\'est un logiciel libre, et vous pouvez l\'héberger vous-même ou payer une entreprise pour le faire à votre place. De cette façon, vous avez le contrôle de vos photos, votre agenda et vos données de contacts, vos documents et tout le reste.\n\nApprenez en plus sur Nextcloud sur https://nextcloud.com La plateforme de productivité auto-hébergée qui vous permet de garder le contrôle.\nCeci est la version officielle de développement, qui présente chaque jour quelques-unes des fonctionnalités non-testées les plus récentes, causant parfois des instabilités ou des pertes de données. Cette application s\'adresse aux utilisateurs volontaires souhaitant tester et rapporter les problèmes qui peuvent arriver. Ne l\'utilisez jamais pour un environnement de production!\n\nLes versions officielles et en développement sont disponibles sur F-droid, et peuvent être utilisées en même temps. La plateforme de productivité auto-hébergée qui vous permet de garder le contrôle La plateforme de productivité auto-hébergée qui vous permet de garder le contrôle (version de développement en démonstration) @@ -826,7 +825,7 @@ \"%1$s\" a été partagé avec vous %1$s a partagé \"%2$s\" avec vous Photos uniquement - Photos and videos + Photos et vidéos Vidéos uniquement Suggérer Des conflits ont été trouvés @@ -868,7 +867,7 @@ Les fichiers supprimés s\'afficheront ici et vous pourrez les restaurer. Le fichier %1$s n\'a pas pu être supprimé ! Le fichier %1$s n\'a pas pu être restauré ! - Delete permanently + Supprimer définitivement Le chargement de la corbeille a échoué ! Des fichiers n\'ont pas pu être supprimés de manière définitive ! Déverrouiller le fichier @@ -884,13 +883,13 @@ Impossible de mettre à jour. Vérifiez que le fichier existe de modifier ce partage La mise à jour du partage a échoué - Clear cancelled uploads - Resume cancelled uploads - Clear failed uploads - Retry failed uploads - Some files not exists those files cannot be resumed - Pause all uploads - Resume all uploads + Effacer les téléversements annulés + Poursuivre les téléversements annulés + Effacer les téléversements échoués + Réessayer les téléversements qui ont échoué + Certains fichiers n’existent pas et ne peuvent pas être relancés. + Suspendre tous les téléversements + Poursuivre tous les téléversements Impossible de créer le fichier local Téléverser depuis… Téléverser du contenu à partir d\'autres applications @@ -902,7 +901,7 @@ Extrait du fichier texte (.txt) Spécifiez le nom et le type du fichier à téléverser Téléverser des fichiers - All uploads are paused + Tous les téléversements sont suspendus Bouton d\'action d\'upload Supprimer Aucun téléversement disponible @@ -911,10 +910,10 @@ Stockage local plein Le fichier n\'a pas pu être copié sur le stockage local Impossible de verrouiller le dossier - Upload was cancelled by user + Le téléversement a été annulé par l\'utilisateur Le chiffrement n\'est disponible que pour les versions d\'Android>= 5.0 Il n\'y a pas assez d\'espace pour copier les fichiers sélectionnés dans le dossier %1$s. Souhaitez-vous plutôt les déplacer ? - Storage quota exceeded + Quota de stockage dépassé Numériser un document depuis l\'appareil photo Conflit de synchronisation, veuillez le résoudre manuellement Erreur inconnue @@ -926,14 +925,14 @@ Le fichier à téléverser n\'a pas été trouvé. Merci de vérifier si ce fichier existe. le fichier ne peut être téléchargé Aucun fichier à téléverser - File not found. Are you sure that this file exists or has a previous conflict not been resolved? - We couldnt locate the file on server. Another user may have deleted the file + Fichier non trouvé. Êtes-vous sûr que ce fichier existe ou qu\'un conflit précédent n\'a pas été résolu ? + Impossible de retrouver le fichier sur le serveur. Un autre utilisateur l\'a peut-être supprimé Nom du dossier - Retry to upload failed local files + Réessayer de téléverser les fichiers locaux qui ont échoué Sélectionnez le dossier de téléversement Impossible d\'envoyer %1$s Le téléversement a échoué, reconnectez-vous - Conflit de fichier lors de l\'envoi + Conflit de téléversement de fichiers Sélectionnez la version de %1$s à conserver Échec du téléversement Option de téléversement : @@ -953,7 +952,7 @@ En cours Échec / En attente de reprise Envoyé - Cancelled + Annulé En attente de téléversement Téléversements Annulé @@ -968,7 +967,7 @@ Récupération de la version serveur … L\'application s\'est arrêtée Terminé - Same file found on remote, skipping upload + Le même fichier a été trouvé sur le serveur distant, le téléversement est ignoré Erreur inconnue Un virus a été détecté. Le téléversement ne peut donc pas être réalisé ! En attente de sortie du mode d’économie d’énergie @@ -989,16 +988,16 @@ Veuillez patienter… Vérification des identifiants enregistrés Copie du fichier depuis le stockage privé - Please update the Android System WebView app for a login - Update - Update Android System WebView + Veuillez mettre à jour l\'application Android System WebView pour vous connecter + Mise à jour + Mettre à jour Android System WebView Image quoi de neuf Ignorer Nouveautés dans %1$s Quel est votre statut ? Les widgets ne sont disponibles que sur %1$s 25 ou plus tard Non disponible - Downloading files… + Réception de fichiers… Envoyer un courriel Le dossier de stockage des données n\'existe pas ! Cela peut être dû à une restauration de sauvegarde sur un autre appareil. Retour aux valeurs par défaut. Veuillez vérifier les paramètres pour ajuster le chemin de stockage. diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index cafc2e6030f8..0782068f8dab 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -7,7 +7,6 @@ Dh’fhàillig cruthachadh a’ chunntais Ìomhaigheag a’ chunntais Cha deach an cunntas a lorg! - Falamhaich na luchdaidhean suas a dh’fhàillig Deasaich Falamhaich a h-uile brath Falamhaich an sgudal @@ -138,7 +137,6 @@ A bheil thu cinnteach gu bheil thu airson %1$s ’s a shusbaint a sguabadh às? A bheil thu cinnteach gu bheil thu airson na nithean a thagh thu ’s an susbaint a sguabadh às? Ionadail a-mhàin - Mearachd le cruthachadh còmhradh na còmhstri! Faidhle %1$s ann an còmhstri Ma thaghas tu an dà thionndadh, thèid àireamh a chur ri ainm an fhaidhle ionadail. Ìomhaigheag a’ chleachdaiche air liosta an luchd-aithne diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 78bc6ce05b75..372563b1986a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -7,7 +7,6 @@ Produciuse un fallo ao crear a conta Icona da conta Non se atopou a conta! - Limpar os envíos fallados Editar Limpar todas as notificacións Baleirar o lixo @@ -43,6 +42,7 @@ Completado A tarefa foi eliminada satisfactoriamente Descoñecido + Entrada Non se atopou unha conta asociada! Acceso fallado: %1$s A conta aínda non existe no dispositivo @@ -155,7 +155,6 @@ Confirma que quere eliminar %1$s e todo o seu contido? Confirma que quere eliminar os elementos seleccionados e o seu contido? Só local - Produciuse un erro ao crear o diálogo de conflitos! Ficheiro en conflito %1$s Ficheiro local Se selecciona ambas versións, o ficheiro local terá un número engadido ao nome. diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 1ceb37ce3032..c4dad0a47fd5 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -7,7 +7,6 @@ Kreiranje korisničkog računa neuspjelo Ikona korisničkog računa Korisnički račun nije pronađen! - Izbriši neuspješne otpreme Uredi Izbriši sve obavijesti Isprazni kantu za smeće @@ -145,7 +144,6 @@ Želite li zaista izbrisati %1$s i pripadajući sadržaj? Želite li zaista izbrisati odabrane stavke i pripadajući sadržaj? Samo lokalno - Pogreška pri stvaranju dijaloškog okvira o nepodudaranju! Nepodudarna datoteka %1$s Lokalna datoteka Ako odaberete obje inačice, lokalna će datoteka uz naziv imati i broj. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index f78a9c10e7ad..3e2123ce5724 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -7,7 +7,6 @@ A fiók létrehozása sikertelen Fiók ikon A fiók nem található! - Sikertelen feltöltések eltávolítása Szerkesztés Összes értesítés törlése Kuka ürítése @@ -155,7 +154,6 @@ Biztos, hogy törli ezt: %1$s és a tartalmát? Biztos, hogy törli a kiválasztott elemeket és tartalmukat? Csak a helyi példány - Hiba az ütközési párbeszédablak létrehozásakor! Ütköző fájl: %1$s Helyi fájl Amennyiben mindkét verziót kiválasztja, a helyi fájl nevéhez egy szám lesz hozzáfűzve. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index bd58c42484f5..0032cd0c8408 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -7,13 +7,13 @@ Pembuatan akun gagal Ikon akun Akun tidak ditemukan! - Bersihkan unggahan gagal. Sunting Hapus semua notifikasi Kosongkan tempat sampah Kirim/Bagi Tampilan kotak Tampilan lis + Pulihkan kontak dan kalender Folder baru Pindah atau Salin Buka dengan @@ -33,10 +33,30 @@ Tambahkan ke %1$s Pengaturan Tambahan Izinkan pembagian ulang + Menampilkan satu gawit dari dasbor Cari dalam %s Semua + Tulis beberapa teks + Apakah Anda yakin ingin menghapus tugas ini? + Hapus Tugas + Gagal + Daftar Tugas sedang dimuat, mohon tunggu + Tidak ada tugas yang tersedia. Pilih jenis tugas untuk membuat tugas baru. + Tidak ada tugas yang tersedia untuk jenis tugas %s, Anda dapat membuat tugas baru dari pojok kanan bawah. + Sedang Berlangsung + Dijadwalkan Selesai + Terjadi kesalahan saat membuat tugas + Tugas berhasil dibuat + Terjadi kesalahan saat menghapus tugas + Tugas berhasil dihapus + Tidak dapat memperoleh daftar tugas, mohon periksa koneksi internet Anda. + Hapus Tugas + Tidak dapat memperoleh jenis tugas, mohon periksa koneksi internet Anda. + Asisten Tidak diketahui + Input + Output Akun terkait tidak ditemukan! Akses gagal: %1$s Akun ini belum ditambahkan ke perangkat ini @@ -75,9 +95,11 @@ Pasang folder kostum. Non-aktifkan pemeriksaan mode hemat daya Sembunyikan folder + Menyiapkan unggah otomatis Avatar Jauh Pengaturan Pencadangan + Pencadangan kontak dan kalender Tutup Nonaktifkan Optimisasi baterai perangkat Anda mungkin sedang aktif. @@ -88,11 +110,13 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Kalender Terjadi permasalahan memuat sertifikat. Perubahan versi dev + Periksa kembali nanti atau muat ulang. Kotak centang Pilih folder lokal… Pilih folder remot… Mohon pilih templat dan masukkan nama file. Pilih file mana yang ditahan! + Pilih gawit Gagal membersihkan notifikasi Kosongkan status pesan Kosongkan status pesan setelah @@ -128,6 +152,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Ini termasuk semua fitur yang akan datang. Bug/galat bisa terjadi, bila terjadi harap laporkan ke kami. forum Bantu yang lain di + Tinjau, ubah, dan tulis kode, lihat %1$suntuk detailnya. Berkontribusi Aktif aplikasi Terjemahkan @@ -147,7 +172,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Apakah anda yakin ingin menghapus %1$s beserta isinya? Apa anda yakin ingin menghapus item yang terpilih beserta isinya? Lokal saja - Galat saat membuat dialog konflik! + Dialog penyelesaian konflik tidak dapat dibuat File konflik %1$s File lokal Jika Anda memilih kedua versi, nama dari berkas lokal akan ditambahi angka. @@ -220,18 +245,23 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Mengunduh… %1$s terunduh Unggahan + Berkas tertentu dibatalkan saat pengunduhan oleh pengguna + Terjadi kesalahan saat mengunduh berkas Belum diunduh + Terjadi kesalahan tidak terduga saat mengunduh berkas Tutup jendela samping Komunitas Gambar latar belakang tajuk laci Aktivitas Semua berkas + Asisten Disukai Media Folder kelompok Beranda Pemberitahuan Dalam perangkat + Berkas pribadi Baru diubah Dibagikan Berkas terhapus @@ -243,6 +273,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Unggah otomatis. E2E belum dipersiapkan Tidak bisa dilakukan tanpa koneksi internet + Asisten Lainnya Catatan Talk @@ -272,6 +303,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Terjadi kesalahan dalam memilih tanggal Error saat mengomentari file Galat saat membuat berkas dari templat + Kesalahan menampilkan aksi berkas Error saat mengubah status kunci file Laporan Galat saat menerima file @@ -280,6 +312,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Galat saat mengaktifkan kamera Galat memulai pemindaian dokumen Akun + Dibuat Nama pekerjaan Kemajuan Kondisi diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 8c96737ff070..74c49d144cf2 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -7,7 +7,6 @@ Gerð notandaaðgangs mistókst Táknmynd fyrir notandaaðgang Notandaaðgangur fannst ekki! - Hreinsa lista yfir innsendingar sem mistókust Breyta Hreinsa allar tilkynningar Tæma ruslið @@ -150,7 +149,6 @@ Ertu viss um að þú viljir eyða %1$s og innihaldi þess? Ertu viss um að þú viljir eyða völdum atriðum og innihaldi þeirra? Einungis staðvært - Villa við að búa til árekstraglugga! Skrá á tölvunni Ef þú velur báðar útgáfur, þá mun verða bætt tölustaf aftan við heiti afrituðu skrárinnar. Skrá á netþjóni diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index cb8efdebdedc..bdcc76abb7c3 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -7,7 +7,6 @@ Creazione dell\'account non riuscita Icona dell\'account Account non trovato - Cancella caricamenti non riusciti Modifica Cancella tutte le notifiche Svuota cestino @@ -153,7 +152,6 @@ Vuoi davvero rimuovere %1$s e il relativo contenuto? Vuoi davvero eliminare gli elementi selezionati e il loro contenuto? Solo localmente - Errore di creazione della finestra di conflitto! File %1$s in conflitto File locale Se selezioni entrambe le versioni, il file locale ha un numero aggiunto al suo nome. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index db059895baac..5def87a8eecb 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -7,7 +7,6 @@ יצירת החשבון נכשלה סמל חשבון החשבון לא נמצא! - פינוי העלאות שנכשלו עריכה פינוי כל ההתראות פינוי סל האשפה @@ -144,7 +143,6 @@ למחוק את %1$s על תוכנו? למחוק את הפריטים הנבחרים ואת תוכנם? מקומי בלבד - שגיאה ביצירת תיבת דו־שיח סתירה! קובץ מקומי בבחירה של שתי הגרסאות, יתווסף מספר בסוף שמו של הקובץ המקומי. קובץ בשרת diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index fe5368775ec3..c3d2d087df6d 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -7,7 +7,6 @@ アカウントの作成に失敗しました アカウントアイコン アカウントが見つかりません! - 失敗したアップロードのクリア 編集 全ての通知を削除 ゴミ箱を空にする @@ -38,9 +37,12 @@ ダッシュボードから一つのウィジェットを表示 %s の中を検索 すべて + テキストを入力 失敗 完了 不明 + 入力 + 出力 関連付けられたアカウントが見つかりません! アクセスに失敗しました: %1$s このアカウントはまだこのデバイスに追加されていません @@ -155,7 +157,6 @@ 本当に %1$s を中身も一緒に削除しますか? 本当に選択したアイテムとその内容を削除しますか? ローカルのみ - コンフリクトダイアログの作成に失敗しました %1$sはすでに存在します ローカルファイル 両方のバージョンを選択した場合、ローカルファイルはファイル名に数字が追加されます。 diff --git a/app/src/main/res/values-ka-rGE/strings.xml b/app/src/main/res/values-ka-rGE/strings.xml deleted file mode 100644 index 01b6281dccd1..000000000000 --- a/app/src/main/res/values-ka-rGE/strings.xml +++ /dev/null @@ -1,506 +0,0 @@ - - - %1$s Android აპლიკაცია - ჩვენს შესახებ - ვერსია %1$s - ანგარიშის პიქტოგრამა - ანგარიში ვერ იქნა ნაპოვნი! - წარუმატებლად დასრულებული ატვირთების გასუფთავება - ცვლილება - გაგზავნა/გაზიარება - ბადისებური ხედი - ჩამონათვლისებური ხედი - ახალი დირექტორია - გახსნა - ძიება - დეტალები - გაგზავნა - პარამეტრები - განლაგება - აქტიური მომხმარებელი - აქტოვობა ჯერ არაა - გაგზავნა - გააგზავნეთ ბმული… - აქტივობა - ხელახალი გაზიარების დაშვება - ყველა - ანგარიში ამ მოწყობილობაზე ჯერ არაა დამატებული - მოწყობილობაზე ანგარიში ამ მომხმარებლით და სერვერით უკვე არსებობს - შეყვანილი მომხმარებელი არ ემთხვევა ამ ანგარიშის მომხმარებელს - სერვერის ამოუცნობი ვერსია - კავშირი დამყარდა - სერვერის მისამართი https://... - სერვერისთვის არასწორი მისამსამართის ფორმატი - სერვერი ვერ მოიძებნა - ქსელთან კავშირი არ არის - დაცული კავშირი არაა ხელმისაწვდომი. - არასწორი სერვერის კონფიგურაცია - წარუმატებელი ავტორიზაცია - წვდომა ავტორიზაციის სერვერის მიერ აიკრძალა - დაცული კავშირი გადამისამართდა დაუცველი მარშრუტით. - დაცული კავშირი დამყარდა - SSL ინიციალიზაცია ვერ მოხერხდა - SSL სერვერის იდენტობა ვერ დამოწმდა - კავშირის შემოწმება - სერვერმა პასუხი დააგვიანია - არასწორი მომხმარებელი ან პაროლი - წარმოიშვა უცნობი შეცდომა! - ჰოსტი ვერ მოიძებნა - %1$s არ უჭერს მხარს მრავალი ანგარიშის არსებობას - კავშირი ვერ დამყარდა - ატვირთვა მხოლოდ შეუზღუდავ Wi-Fi-ზე - /ავტომატური_ატვირთვა - შექმენით ახალი სპეციფიური დირექტორია - დააყენეთ სპეციფიური დირექტორია - ავატარი - დახურვა - გამორთვა - კალენდარი - სერტიპიკატის ჩატვირთვასთან პრობლემაა. - ჩექბოქსი - ტექსტი %1$s-იდან დაკოპირდა - ბუფერში გადასატანად ტექსტი მიღებული არაა - ბუფერში გადატანისას წარმოშვა მოულოდნელი შეცდომა - უკან - გაუქმება - სინქრონიზაციის შეჩერება - აირჩიეთ ანგარიში - დადასტურება - კოპირება - გაუქმება - შეცდომა - არასაკმარისი მეხსიერება - უცნობი შეცდომა - იტვირთება… - შემდეგი - არა - კარგი - მოლოდინშია - გაუქმება - გადარქმევა - შენახვა - გაგზავნა - გაზიარება - გამოტოვება - კი - დევ. ვერსიის შემოწმება - იმყოფება სისხლდენის ზღვარზე და მოიცავს ყველა დამდეგ ფუნქციას. შეიძლება გამოჩნდეს შეცდომები, ასეთი შემთხვევისას გთხოვთ გვამცნობოთ რეპორტით. - ფურუმი - დაეხმარეთ სხვებს - აქტიურად შეიტანეთ წვლილი - აპლიკაცია - გადათარგმნეთ - დეველოპმენტ რელიზის პირდაპირი ჩამოტვირთვა - მიიღეთ დეველოპმენტ რელიზი F-Droid აპლიკაციიდან - მიიღეთ რელიზ კანდიდატი F-Droid აპლიკაციიდან - მიიღეთ რელიზ კანდიდატი Google Play-დან - რელიზის კანდიდატი - ეს რელიზის კანდიდატი (რკ) დამდეგი რელიზის კადრია და მოსალოდნელია მისი სტაბილურობა. ამის დამოწმებაში დაგვეხმარება ინდივიდუალური მოწყობილობის შემოწმება. შემოწმებისთვის დარეგისტრირდით Play-ზე ან გადახედეთ \"ვერსიის\" სექციას F-Droid-ში. - იპოვეთ შეცდომა? უცნაურობა? - დაგვეხმარეთ შემოწმებით - დაამატეთ მოხსენიება GitHub-ზე - ნამდვილად გსურთ %1$s-ის გაუქმება? - დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერების გაუქმება? - ნამდვილად გსურთ %1$s-ის და შემცველობის გაუქმება? - დარწმუნებული ხართ, რომ გსურთ არჩეული ჩანაწერებისა და მათი შემცველობის გაუქმება? - მხოლოდ ლოკალური - მომხმარებლის პიქტოგრამა კონტაქტების სიისთვის - უფლებები არაა მოცემული, არაფერი არ იქნა იმპორტირებული. - კონტაქტები - ახალი ბექაფი - ბექაფი დაინიშნა და მალე დაიწყება - იმპორტი დაინიშნა და მალე დაიწყება - ფაილი ვერ იქნა ნაპოვნი - თქვენი ბოლო ბექაფი ვერ იქნა ნაპოვნი! - კოპირებულია კლიპბორდში - ამ ფაილისა თუ დირექტორიის კოპირებისას წარმოიშვა შეცდომა - დირექტორიის კოპირება მასში არსებულ დირექტორიაში შეუძლებელია - ფაილი დანიშნულ დირექტორიაში უკვე არსებობს - ბმულის კოპირება - ამჟამად კოპირება/გადატანა დაშიფრულ დირექტორიაში არაა მხარდაჭერილი. - შექმნა - დირექტორია ვერ შეიქმნა - ანგარიშის გაუქმება - ყველას წაშლა - ხელმისაწვდომია ახალი ვერსია - ახალი ვერსია არაა ხელმისაწვდომი. - დახურვა - თქვენს ტელეფონს ამ ალგორითმის დაიჯესტის მხარდაჭერა არ აქვს. - გამორთვა - დათხოვნა - დასრულებულია - ვერ ჩამოიტვირთა %1$s - ჩამოტვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ჩამოტვირთვა ვერ განხორციელდა - ფაილი სერვერზე ხელმისაწვდომი აღარაა - %1$d%% ჩამოტვირთვა %2$s - %1$s ჩამოტვირთლია - ჩამოტვირთულია - ჯერ ვერ ჩამოიტვირთა - მხაზველი დასათაურების ფონური სურათი - აქტივობები - ყველა ფაილი - რჩეულები - სახლი - შეტყობინებები - მოწყობილობაზე - ახლად შეცვლილი - გაზიარებული - გაუქმებული ფაილები - ატვირთვები - გასვლა - გამოყენებულია %1$s სულ %2$s-იდან  - ავტო-ატვირთვა - უფრო მეტი - ჩანაწერები - საუბარი - დააყენეთ როგორც დაშიფრული - შიფრაციის დაყენება - დახურვა - პირადი გასაღების დეშიფრაციისთვის გთხოვთ შეიყვანოთ პაროლი. - ეს დირექტორია არაა ცარიელი - 12 სიტყვა ერთად წარმოქმნის ძალიან ძლიერ პაროლს, იძლევა თქვენი დაშიფრული ფაილების ჩვენებისა და მოხმარების უფლებას. გთხოვთ ჩაიწეროთ და შეინახოთ დაცულ ადგილას. - შენიშნეთ თქვენი 12 სიტყვიანი შიფრაციის პაროლი - გასაღებების შენახვა - შიფრაციის დაყენება - გასაღებების შენახვა ვერ მოხერხდა, გთხოვთ სცადოთ ახლიდან. - შეცდომა დეშიფრაციისას. არასწორი პაროლი? - %1$s ვერ კოპირდება %2$s ლოკალურ დირექტორიაში - კრიტიკული შეცდომა: ოპერაციების განხორციელება შეუძლებელია - ანგარიშები - შექმნილია - მომხმარებელი - ფონური საქმეები - ჩამოტვირთვა - ატვირთვა - რჩეულებში დამატება - რჩეული - წაშლა - ფაილი - ატვირთეთ რამე ან გაუწიეთ თქვნს მოწყობილობას სინქრონიზაცია. - რჩეულებში ჯერ არაფერი დამატებულია - აქ ფაილები არაა - ამ დირექტორიაში შედეგები არაა - შედეგები არაა - აქ არაფერია. შეგიძლიათ დაამატოთ დირექტორია. - გასული 7 დღის განმავლობაში შეცვლილი ფაილ(ებ)ი ვერ იქნა ნაპოვნი - იქნებ სხვა დირექტორიაშია? - ჯერ არაფერი გაზიარებულა - დირექტორია - იტვირთება… - ამ ფაილთან სამუშაოდ არც ერთი აპლიკაცია არაა დაყანებული. - წამის წინ - მონაცემების დირექტორია უკვე არსებობს. აირჩიეთ ერთ-ერთი შემდეგიდან: - Nextcloud დირექტორია უკვე არსებობს - საჭიროა მეტი სივრცე - წყარო-ფაილის წაკითხვა ვერ მოხერხდა - სამიზნე ფაილში ჩაწერა ვერ მოხერხდა - მიგრაციისას წარმოიქმნა შეცდომა - ინდექსის განახლება ვერ მოხერხდა - დასრულებულია - ჩანაცვლება - წყარო-დირექტორია ვერ იკითხება! - მოხმარება - ფაილი ვერ იქნა ნაპოვნი - ფაილის სინქ ვერ მოხერხდა. ნაჩვენებია ბოლო ხელმისაწვდომი ვარიანტი. - სახელის შეცვლა - დეტალები - ჩამოტვირთვა - ექსპორტი - ატვირთვისას ფაილს სახელი შეეცვალა %1$s-ზე - სინქ - ფაილი არაა არჩეული - ფაილის სახელი ვერ იქნება ცარიელი - აკრძალული ნიშნები: / \\ < > : \" | ? * - ფაილის სახელი შეიცავს მინ. 1 არასწორ ნიშანს - ფაილის სახელი - შექმნა - აქ დირექტორიები არაა - აირჩიეთ - გადატანა - თქვენ არ გაქვთ უფლება, %s - რომ დააკოპიროთ ეს ფაილი - შექმნათ ეს ფაილი - გააუქმოთ ეს ფაილი - რომ გადაიტანოთ ეს ფაილი - შეუცვალოთ ამ ფაილს სახელი - ფაილების ატვირთვა... - გარკვეული ფაილების გადატანა ვერ მოხერხდა - ლოკალური: %1$s - ყველას გადატანა - დისტანციური: %1$s - ყველა ფაილი გადატანილია - წინ - 4 საათი - სახელი - პაროლი - ატვირთვა მხოლოდ დატენვისას - /მყისიერი_ატვირთვა - უჩინარი - ბმული - ატვირთვისა და ცვლილების უფლებების მინიჭება - ფაილის ჩაგდება (მხოლოდ ატვირთვა) - ჩამოწერილი მაკეტი - ამ დირექტორიაში ფაილები არაა. - ლოკალურ ფაილ-სისტემაში ფაილი ვერ მოიძებნა - მომდევნო დირექტორიები არაა. - %1$s Android აპლიკაციის ლოგები. - ლოგინი - განახლება - იტვირთება… - ლოგები - მონაცემების გასუფთავება -  %1$s-ის მონაცემებიდან პარამეტრები, მონაცემთა ბაზა და სერტიფიკატები სამუდამოდ წაიშლება.\n\nგადმოწერილი ფაილები დარჩება ხელუხლებელი.\n\nეს პროცესი გასტანს გარკვეულ დროს. - მოცულობის მენეჯმენტი - მედია ფაილის წაკითხვა ვერ მოხერხდა - მედია ფაილს გააჩნია არასწორი კოდირება - ფაილის დაკვრის მცდელობის დრო ამოიწურა - ჩაშენებული მედია დამკვრელი ვერ უკრავს მედია ფაილს - მედია კოდეკი მხარდაუჭერელია - სწრაფი გადახვევის ღილაკი - %1$s მუსიკის დამკვრელი - დაკვრის ან პაუზის ღილაკი - გადახვევის ღილაკი - %1$s (იკვრება) - ჯერ ახალი - ჯერ ძველი - ა - ჰ - ჰ - ა - ჯერ დიდი - ჯერ პატარა - მეტი - ამ ფაილისა თუ დირექტორიის გადატანისას წარმოიშვა შეცდომა - დირექტორიის გადატანა მასში არსებულ დირექტორიაში შეუძლებელია - ფაილი დანიშნულ დირექტორიაში უკვე არსებობს - სერვერთან დაკავშირებისას წარმოიქმნა შეცდომა - ფოტო - აჩვენებს გადმოწერის პროგრესს - ჩამოტვირთვები - აჩვენებს ფაილების სინქ. პროგრესს და შედეგებს - ფაილების სინქ. - მუსიკის დამკვრელის პროგრესი - მედია დამკვრელი - აჩვენებს ატვირთვის პროგრესს - ატვირთვები - შეტყობინებები არაა - გთხოვთ დაბრუნდეთ მოგვინაებით. - 1 საათი - შეიყვანეთ თქვენი პასკოდი - პასკოდი მოთხოვნილ იქნება აპლიკაციის ყოველი გაშვებისას - გთხოვთ შეიყვანოთ თქვენი პასკოდი - პასკოდები ერთმანეთს არ ემთხვევა - გთხოვთ თქვენი პასკოდი შეიყვანოთ ახლიდან - თქვენი პასკოდის გაუქმება - პასკოდი გაუქმდა - პასკოდი შენახულია - არასწორი პასკოდი - ფაილების ატვირთვისა და გადმოწერისათვის საჭიროა დამატებითი უფლებები. - სურათის დასაყენებლად ვერც ერთი აპლიკაცია ვერ იქნა ნაპოვნი - 389 კბ - ადგილი.txt - 12:23:45 - ეს არის ადგილი - 18/05/2012 12:23 - გაუქმებულია - დარჩა ორიგინალ დირექტორიაში - გადატანილია აპლიკაციის დირექტორიაში - ანგარიშის დამატება - არც Google Play-ა დაყენებული, არც F-Droid-ი - ჩვენს შესახებ - დეტალები - დევ. - ზოგადი - მეტი - კონტაქტების ყოველდღიური ბექაფი - GNU ზოგადი ღია ლიცენზია, ვერსია 2 - დახმარება - ბეჭედი - ქვე-დირექტორიების მოხმარება - ლიცენზია - არც ერთი - ანგარიშების მართვა - დამალული ფაილების ჩვენება - მიიღეთ კოდი - ლოკალური დირექტორია - დისტანციური დირექტორია - ვიზუალური თემა - მუქი - ღია - სურათის წინასწარი ჩვენება - წინასწარი ჩვენებისთვის ლოკალური ფაილი არაა - სურათის ჩვენება შეუძლებელია - ბოდიში - კონფიდენციალურობა - Google Play სერვისების მესაკუთრეობრივი დამოკიდებულებების გამო, ფუშ შეტყობინებები გათიშულია. - ვადაგასული აუტენტიფიკაციის გამო ფუშ შეტყობინებები არაა. გთხოვთ გაითვალისწინოთ თქვენი ანგარიშის თავიდან დამატება. - ფუშ შეტყობინებები ამჟამად ხელმიუწვდომელია. - სცდეთ %1$s თქვენს მოწყობილობაზე! - გიწვევთ გამოიყენოთ %1$s თქვენს მოწყობილობაზე.\nჩამოტვირთეთ აქედან: %2$s - %1$s ან %2$s - გადატვირთვა - გაუქმება ვერ მოხერხდა - გაუქმება - გაუქმდა - შეიყვანეთ ახალი სახელი - ლოკალური ასლის სახელის გადარქმევა ვერ მოხერხდა, სცადეთ სხვა სახელი - გადარქმევა შეუძლებელია, სახელი უკვე დაკავებულია - ხელახალი გაზიარება არაა დაშვებული - ხელახალი გაზიარება არაა ნებადართული - ზომა-შეცვლილი სურათი არაა ხელმისაწვდომი. გადმოვწეროთ მთლიანი სურათი? - ყველას მონიშვნა - გაგზავნა - დააყენეთ როგორც - გამოიყენეთ სურათი როგორც - გაზიარება - გაზიარება - %1$s-ის გაზიარება - %1$s (ჯგუფი) - ბმულის გაზიარება - უნდა შეიყვანოთ პაროლი - გააზიაროთ ეს ფაილი - შეიყვანეთ პაროლი - მიუთითეთ ვადის გასვლის დრო - პაროლის დაყენება - შეუძლია შეცვალოს - %1$s (დისტანციური) - პარამეტრები - ბმულის გაზიარება - გაზიარებული - დაასორტირე - დამალვა - დეტალები - სერვერის იდენტურობა ვერ დამოწმდა - ქვეყანა: - ზოგადი სახელი: - ადგილმდებარეობა: - ორგანიზაცია: - ორგანიზაციის განყოფილება: - შტატი: - თითის ანაბეჭდი: - გაცემულია მიერ: - ხელმოწერა: - ალგორითმი: - გაცემულია: - მოქმედების ვადა: - ვისგან: - ვის: - - ამ შეცდომაზე ინფორმაცია არაა - სერტიფიკატის შენახვა ვერ მოხერხდა - სერტიფიკატის ჩვენება შეუძლებელია. - გსურთ მაინც ენდოთ ამ სერტიფიკატს? - - სერვერის სერტიფიკატი ვადაგასულია - - სერვერის სერტიფიკატი არ არის სანდო - - სერვერის სერტიფიკატის თარიღები მომავალშია - - URL არ ემთხვევა სერტიფიკატში არსებულ ჰოსტნეიმს - საწყისი - ჩამოტვირთვები - გაგიზიარდათ \"%1$s\" - %1$s-სგან გაგიზიარდათ \"%2$s\" - წარმოიშვა კონფლიქტი - დირექტორია %1$s აღარ არსებობს - %1$s სინქ ვერ მოხერხდა - %1$s რესურსის არასწორი პაროლი - Kept-in-sync ფაილების შეცდომა - სინქრონიზაცია დასრუდლა წარუმატებლად - სინქ ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ფაილების შემცველობა სინქრონიზირებულია - როდესაც ერთი ფაილი სინქრონიზირებულია რამოდენიმე ანგარიშთან, 1.3.16 ვერსიაში მონაცემების დაკარგვისგან თავის ასარიდებლად, ამ მოწყობილობით ატვირთული ფაილ(ებ)ი კოპირდება %1$s დირექტორიაში.\n\nამ ცვლილების გამო, აპლიკაციის ძველი ვერსიით ატვირთული ყველა ფაილი დაკოპირდა %2$s დირექტორიაში. თუმცა, შეცდომის გამო სინქრონიზაციისას მოხდა ამ ოპერაციის შეჩერება. შეგიძლიათ ფაილ(ებ)ი დატოვოთ ისე როგორც არის და წაშალოთ ბმული %3$s-სთან, ან გადაიტანოთ ფაილ(ებ)ი %1$s დირექტორიაში და შეინარჩუნოთ ბმული %4$s-ისთვის.\n\nქვემოთ ჩამოწერილია ლოკალური და %5$s-ში არსებული დისტანციური ფაილ(ებ)ი. - რამოდენიმე ლოკალური ფაილი გამორჩენილია - ამოღებულია ფაილის უახლესი ვერსია. - სინქ. სტატუსის ღილაკი - ფაილები - პარამეტრების ღილაკი - დირექტორიების კონფიგურაცია - მყისიერი ატვირთვა სრულიად გამოსწორდა. ავტო-ატვირთვას რეკონფიგურაცია გაუწიეთ მთავარი მენიუდან.\n\nისიამოვნეთ ახალი და გაუმჯობესებული ავტო-ატვირთვით. - %1$s-ისთვის - სახეობა - ტეგები - სერვერთან კავშირის შემოწმება - 30 წუთი - ამ კვირაში - მინიატურა - დღეს - გაუქმებული ფაილები - წაშლილი ფაილები ვერ მოიძებნა - სამუდამოდ წაშლა - შიფრაციის გაუქმება - შეწყვიტოთ ამ ფაილის გაზიარება - წვდომა არასანდო დომენით. დამატებითი ინფორმაციისთვის გთხოვთ იხილოთ დოკუმენტაცია. - განაახლოთ ეს გაზიარება - წარუმატებლად დასრულებული ატვირთების გასუფთავება - წარუმატებლად დასრულებული ატვირთების ხელახლა გაშვება - ფაილის სახელი - ფაილის სახეობა - Google Maps იარლიყის ფაილი(%s) - ინტერნეტ იარლიყის ფაილი(%s) - ტექსტ ფაილის ჩამონაჭერი(.txt) - ატვირთვისთვის შეიყვანეთ ფაილის სახელი და სახეობა - ფაილების ატვირთვა - ელემენტის ატვირთვის ქმედების ღილაკი - წაშლა - არც ერთი ატვირთვა არაა ხელმისაწვდომი. - ატვირთეთ რაიმე ან გაააქტიურეთ ავტო-ატვირთვა. - არასაკმარისი ადგილი აფერხებს არჩეული ფაილების კოპირებას %1$s დირექტორიაში. გსურთ სანაცვლოდ მათი გადატანა? - უცნობი შეცდომა - აირჩიეთ - ატვირთვა - მიღებული მონაცემები არ მოიცავდნენ სწორ ფაილს. - %1$s მომხმარებელს არ აქვს უფლება წაიკითხოს ფაილი. - ფაილის ასლი დროებით დირექტორიაში ვერ შეიქმნა. სცადეთ მისი ახლიდან გაგზავნა. - ატვირთვისთვის არჩეული ფაილი ვერ იქნა ნაპოვნი. გთხოვთ დაამოწმოთ ფაილის არსებობა. - ფაილი ატვირთვისთვის არაა - დირექტორიის სახელი - აირჩიეთ ატვირთვის დირექტორია - %1$s ვერ აიტვირთა - ატვირთვა ვერ მოხერხდა, ახლიდან გაიარეთ ავტორიზაცია - ატვირთვა ვერ განხორციელდა - ატვირთვის ოპციონი: - ფაილის საწყის დირექტორიაში დატოვება - ფაილის საწყისი დირექტორიიდან გაუქმება - ატვირთოთ ამ დირექტორიაში - %1$d%% ატვირთვა %2$s - იტვირთება... - ატვირთულია %1$s - გამოსვლა - დაყენება - ანგარიშები %1$s თქვენს მოწყობილობაზე არაა. გთხოვთ დააყენოთ ანგარიში. - ანგარიში ვერ მოიძებნა - მიმდინარე - ვერ მოხერხებული/მიმდინარე რესტარტი - ატვირთულია - უარყოფილია - ველოდებით ატვირთვას - ატვირთვები - უარყოფილია - კონფლიქტი - კავშირის პრობლემა - შეცდომა უფლებამოსილებებში - ფაილის შეცდომა - დირექტორიის შეცდომა - ლოკალური ფაილი ვერ იქნა ნაპოვნი - შეცდომა უფლებებში - სერვერის არასანდო სერტიფიკატი - აპლიკაცია შეჩერდა - დასრულებულია - უცნობი შეცდომა - ველოდებით ეკონომიური რეჟიმიდან გამოსვლას - გადახდის მოლოდინშია - მომხმარებელი - მისამართი - ელ-ფოსტა - ტელეფონის ნომერი - Twitter-ი - ვებ-საიტი - მოხმარებლის ინფორმაციის მიღებისას წარმოიშვა შეცდომა - პირადი ინფორმაციის კომპლექტი არაა - დაამატეთ სახელი, ფოტო და საკონტაქტო დეტალები თქვენს პროფილის გვერდზე. - მომხმარებლის სახელი - ჩამოტვირთვა - მოწმდება შენახული უფლებამოსილებანი - ფაილის კოპირება პირადი საცავიდან - განახლება - რა არის ახალი სურათი - გამოტოვება - ახალი %1$s-ში - ელ-წერილის გაგზავნა - diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index ed840d85695c..7f19cbf06a14 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -7,7 +7,6 @@ Account creation failed Account icon Account not found! - Clear failed uploads Edit Clear all notifications Empty trash bin @@ -152,7 +151,6 @@ Do you really want to delete %1$s and the contents thereof? Do you really want to delete the selected items and their contents? Local only - Error creating conflict dialog! Conflicting file %1$s Local file If you select both versions, the local file will have a number appended to its name. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 7c7d534b456e..30781f711852 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -7,7 +7,6 @@ 계정 생성에 실패했습니다. 계정 아이콘 계정을 찾을 수 없습니다! - 실패한 업로드 삭제 편집 모든 알림 지우기 휴지통 비우기 @@ -154,7 +153,6 @@ %1$s 및 포함된 모든 내용을 삭제하시겠습니까? 선택한 항목과 포함된 내용을 삭제하시겠습니까? 로컬만 - 다이얼로그의 충돌로 인한 오류 충돌하는 파일 %1$s 로컬 파일 두 버전을 모두 선택하면 기존 파일 이름에 번호가 추가됩니다. diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 11b73771ac0f..39cda0df6250 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -7,7 +7,6 @@ ການສ້າງບັນຊີບໍ່ສຳເລັດ ໄອຄອນບັນຊີ ບໍ່ພົບບັນຊີ! - ການອັບໂຫຼດບໍ່ສຳເລັດ ແກ້ໄຂ ລ້າງການແຈ້ງເຕືອນທັງຫມົດ ລ້າງຖັງຂີ້ເຫຍື່ອ @@ -145,7 +144,6 @@ ທ່ານຕ້ອງການ ລຶບ%1$s ແລະ ເນື້ອຫາບໍ? ທ່ານຕ້ອງການລຶບລາຍການທີ່ເລືອກ ແລະ ເນື້ອຫາແທ້ບໍ? ຊ່ອງເກັບຢ່າງດຽວ - ຄວາມຜິດພາດໃນການສ້າງການສົນທະນາຜິດພາດ! ຟາຍຜິດພາດ%1$s ຖ້າທ່ານເລືອກເອົາທັງສອງເວີຊັ້ນ, ບ່ອນເກັບຟາຍຈະມີຈໍານວນສະສົມ ລາຍການໄອຄອນຜູ້ຕິດຕໍ່ diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index eccaac995f54..24ddd93adb37 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -7,7 +7,6 @@ Paskyros sukūrimas nepavyko Paskyros piktograma Paskyra nerasta! - Išvalyti nepavykusius įkėlimus Taisyti Išvalyti visus pranešimus Išvalyti šiukšlinę @@ -147,7 +146,6 @@ Ar tikrai norite ištrinti %1$s ir jo turinį? Ar tikrai norite ištrinti pažymėtus elementus ir jų turinį? Tik vietiniai - Klaida kuriant konflikto dialogą! Nesuderinamas failas%1$s Vietinis failas Jei pasirinksite abi versijas, vietinis failas prie pavadinimo turės numerį. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index f7debdce83cf..080f09398c09 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -7,7 +7,6 @@ Konta izveidošana neizdevās Konta ikona Konts nav atrasts! - Notīrīt neizdevušās augšupielādes Rediģēt Notīrīt visus paziņojumus Izdzēst miskastes saturu @@ -123,7 +122,6 @@ Vai tiešām vēlaties izdzēst %1$s un tā saturu? Vai tiešām vēlies dzēst izvēlētos objektus un to saturu? Tikai lokālos - Kļūda veidojot konfliktu dialogu! Lietotāja ikona kontaktpersonu sarakstam Nav dota atļauja, importēšana neizdevās Kontakti diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index f9d63cdeaf2c..bea766436061 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -7,7 +7,6 @@ Неуспешно креирање на сметка Икона за сметката Сметката не е пројандена! - Исчисти ги неуспешните прикачувања Уреди Исчисти ги сите известувања Испразни ја корпата со отпадоци @@ -144,7 +143,6 @@ али си сигурен дека сакаш да ја избришеш %1$s и содржината во истата? Дали си сигурен дека сакаш да ја избришеш означената ставкаи содржината во неа? Само локално - Грешка при креирање конфликтен извештај! Датотеки со конфликт %1$s Локална датотека Ако ги одберете и двете верзии, локалната датотека ќе има број додаден на нејзиното име. diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 5274e2b33061..a97cedbda070 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -7,7 +7,6 @@ Oppretting av konto mislyktes Kontoikon Konto ble ikke funnet! - Fjern feilede opplastinger Rediger Slett alle varsel Tøm papirkurv @@ -57,6 +56,8 @@ Kan ikke hente oppgavetyper, vennligst sjekk internettforbindelsen din. Assistent Ukjent + Inndata + Utdata Tilknyttet bruker ikke funnet! Tilgang mislyktes: %1$s Kontoen er ikke lagt til på denne enheten enda @@ -171,7 +172,6 @@ Ønsker du virkelig å slette %1$s og dets innhold? Vil du virkelig fjerne de valgte elementene og dets innhold? Kun lokalt - Feil ved oppretting av konfliktdialog! Konflikt med%1$s Lokal fil Hvis du velger begge versjonene vil den lokale filen få et tall lagt til på slutten av navnet. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 7c997db67bb6..764a6aaa871b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -7,7 +7,6 @@ Account aanmaken mislukt Accountpictogram Account niet gevonden! - Wis mislukte uploads Bewerken Handel alle meldingen af Prullenbak legen @@ -167,7 +166,6 @@ Wil je %1$s en de inhoud ervan werkelijk verwijderen? Wil je de geselecteerde objecten en hun inhoud echt verwijderen? Alleen lokaal - Fout bij het maken van conflictdialoog! Conflicterend bestand%1$s Lokaal bestand Als je beide versies selecteert, zal het lokale bestand een nummer aan de naam toegevoegd krijgen. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 417fdfa52d8b..78f1f38430cd 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -7,7 +7,6 @@ Błąd tworzenia konta Ikona konta Nie znaleziono konta! - Wyczyść nieudane wysyłanie Edytuj Wyczyść wszystkie powiadomienia Opróżnij kosz @@ -153,7 +152,6 @@ Czy na pewno chcesz usunąć %1$s wraz z zawartością? Czy na pewno chcesz usunąć wybrane pozycje i ich zawartość? Tylko lokalnie - Błąd podczas tworzenia dialogu konfliktu! Plik powodujący konflikt %1$s Plik lokalny Jeśli wybierzesz obie wersje, to do nazwy pliku lokalnego zostanie dodany numer. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9049bf3b6872..1de92b5d710f 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -7,7 +7,6 @@ Falha ao criar conta Ícone da conta Conta não encontrada! - Apagar envios com falha Editar Apagar todas as notificações Esvaziar lixeira @@ -57,6 +56,8 @@ Não foi possível buscar os tipos de tarefas. Verifique sua conexão com a Internet. Assistente Desconhecido + Entrada + Saída Conta associada não encontrada! O acesso falhou: %1$s A conta ainda não está adicionada neste dispositivo @@ -171,7 +172,6 @@ Quer realmente excluir %1$s e seu conteúdo? Quer realmente excluir os itens selecionados e seus conteúdos? Somente local - Erro ao criar diálogo de conflito! Arquivo conflitante %1$s Arquivo local Se você selecionar as duas versões, o arquivo local terá um número anexado ao seu nome. diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 93615fe7e646..0393ad0c6ef0 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -7,7 +7,6 @@ Criação de conta falhou Ícone de conta Conta não encontrada! - Limpar envios falhados Editar Limpar todas as notifcações Esvaziar a reciclagem @@ -150,7 +149,6 @@ Deseja realmente apagar %1$s e o seu conteúdo? Quer realmente apagar os itens seleccionados e os seus conteúdos? Apenas localmente - Erro ao criar a janela de conflito! Ficheiro em conflito %1$s Ficheiro local Se selecionou ambas as versões, o ficheiro local terá um número acrescentado ao seu nome. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 57598577b2fe..a75583470fbd 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -7,7 +7,6 @@ Eroare la crearea contului Iconița contului Contul nu a fost găsit! - Elimină încărcările eșuate Editare Elimină toate notificările Golește coșul de gunoi @@ -150,7 +149,6 @@ Sigur vreți să eliminați %1$s și conținutul său? Doriți să ștergeți elementele selectate și conținutul lor? Doar local - Eroare la crearea dialogului pentru conflict! Conflict cu fișierul %1$s Fișier local Dacă selectezi ambele variante, atunci fișierul local va avea un număr adăugat la numele său. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fd87b685db0d..e0d0b13d6bc7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -7,7 +7,6 @@ Ошибка создания учетной записи Значок учётной записи Учётная запись не найдена! - Убрать неудавшиеся загрузки Редактировать Удалить все уведомления Очистить корзину @@ -39,11 +38,26 @@ Искать в %s Все Наберите какой-то текст + Вы уверены, что хотите удалить эту задачу? + Удалить задачу Не удалось + Список задач загружается, пожалуйста подождите + Нет доступных задач. Выберите тип задачи, чтобы создать новую задачу. + Нет доступной задачи для типа задачи %s, вы можете создать новую задачу в правом нижнем углу. + Выполянется Запланированное Выполнено + Произошла ошибка при создании задачи + Задача успешно создана + Произошла ошибка во время удаления задачи Задача удалена + Не удается получить список задач, проверьте ваше подключение к Интернету. + Удалить задачу + Не удается получить список задач, проверьте ваше подключение к Интернету. + Помощник Неизвестно + Ввод + Вывод Связанный аккаунт не найден! Доступ запрещен: %1$s Учётная запись ещё не создана на этом устройстве @@ -158,7 +172,7 @@ Действительно удалить «%1$s» и его содержимое? Действительно удалить выбранные объекты и их содержимое? Только локально - Не удалось создать диалоговое окно разрешения конфликта синхронизации. + Не удается создать диалоговое окно разрешения конфликтов Конфликт версий файла «%1$s» Локальный файл Если вы выберете обе версии, локальный файл будет иметь номер, добавленный к его имени. @@ -242,6 +256,7 @@ Фоновое изображение заголовка События Все файлы + Помощник Избранное Медиа Групповые папки @@ -260,6 +275,7 @@ Автоматическая передача Сквозное шифрование не настроено Невозможно без подключения к интернету + Помощник Больше Заметки Конференции @@ -731,6 +747,7 @@ Поделиться ссылкой (%1$s) Установить срок действия Задать пароль + Перенаправление запрещено во время безопасного удаления файлов Защищено паролем Разрешено редактировать Принимать файлы @@ -754,6 +771,7 @@ поступ по ссылке С вами поделился %1$s Не удалось открыть общий доступ + Не удалось добавить общий ресурс. Этому пользователю или группе уже предоставлен общий доступ к этому файлу или папке. Показать фотографии Показать видео Подключиться через поставщика услуги @@ -796,6 +814,7 @@ Полный доступ Носитель только для чтения Изображения + Собственная продуктивная платформа, которая держит вас под контролем.\n\nFeatures:\n* Простой, современный интерфейс, соответствующий тематике вашего сервера\n* Загрузите файлы на свой сервер Nextcloud\n* Делитесь ими с другими\n* Синхронизируйте ваши любимые файлы и папки\n* Поиск во всех папках на вашем сервере\n* Автоматическая отправка фотографий и видео с вашего устройства\n* Будьте в курсе с уведомлениями\n* Поддержка нескольких учетных записей\n* Безопасный доступ к вашим данным с помощью отпечатка пальцев или PIN-кода\n* Интеграция с DAVx5 (ранее известный как DAVdroid) для легкой настройки синхронизации календаря и контактов\n\nПожалуйста, сообщите обо всех проблемах на https://github.com/nextcloud/android/issues и обсудите это приложение на https://help.nextcloud.com/c/clients/android\n\nНовичок в Nextcloud? Nextcloud - это частный сервер синхронизации и обмена файлами и коммуникационный сервер. Это свободное программное обеспечение, и вы можете разместить его самостоятельно или заплатить компании, чтобы сделать это за вас. Таким образом, вы управляете своими фотографиями, календарем и контактными данными, документами и всем остальным.\n\nПроверьте Nextcloud по адресу https://nextcloud.com Платформа, размещаемая на собственных ресурсах и позволяющая самостоятельно управлять данными.\nЭто разрабатываемая версия официального приложения Nextcloud, содержащая новые возможности, тестирование которых не ещё завершено. Разрабатываемая версия может быть нестабильна и её использование может привести к потере данных. Это приложение предназначено для пользователей, которые хотят познакомиться с новыми возможности и сообщать об обнаруженных ошибках. Не используйте его для работы!\n\nРазрабатываемая версия может быть установлена совместно с официальным приложением Nextcloud, которое также доступно в F-Droid. Платформа, размещаемая на собственных ресурсах и позволяющая самостоятельно управлять данными Платформа, размещаемая на собственных ресурсах и позволяющая самостоятельно управлять данными (разрабатываемая версия) diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 8a6f92e90331..6fc476d640b0 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -7,7 +7,6 @@ Errore in sa creatzione de su contu Icona de su contu Contu no agatadu! - Boga·nche is carrigamentos faddidos Modìfica Boga·nche totu is notìficas Bòida s\'àliga @@ -144,7 +143,6 @@ A beru boles cantzellare %1$s e is cuntenutos suos? A beru boles cantzellare is elementos seberados e is cuntenutos issoro? Locale isceti - Errore creende una bentana de cunflitu! Archìviu in cunflitu %1$s Si seletzionas ambas is versiones, s\'archìviu locale at a tènnere unu nùmeru in agiunta a su nùmene. Icona utente pro lista de cuntatos diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index e396c88bfc47..39c4efe7cb84 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -7,7 +7,6 @@ Vytvorenie účtu sa nepodarilo Ikona účtu Účet sa nenašiel! - Odstrániť zlyhané nahrávania Upraviť Odstrániť všetky upozornenia Vyprázdniť kôš @@ -57,6 +56,8 @@ Nie je možné načítať typy úloh, skontrolujte svoje internetové pripojenie. Asistent Neznámy + Vstup + Výstup Priradený účet sa nenašiel Prístup zamietnutý: %1$s Účet zatiaľ v zariadení neexistuje @@ -171,7 +172,7 @@ Naozaj chcete odstrániť %1$s a jeho obsah? Naozaj chcete odstrániť vybraté položky a ich obsah? Iba lokálne - Chyba pri vytváraní konfliktného dialógového okna! + Dialógové okno riešenia konfliktov nemožno vytvoriť Konfliktný súbor %1$s Lokálny súbor Ak vyberiete obe verzie, miestny súbor bude mať k svojmu názvu pridané číslo. diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 649b9df23bd0..5b72b578d41e 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -7,7 +7,6 @@ Ustvarjanje računa je spodletelo ikona računa Računa ni mogoče najti! - Počisti dnevnik spodletelih pošiljanj Uredi Počisti vsa obvestila Izprazni smeti @@ -153,7 +152,6 @@ Ali res želite izbrisati %1$s skupaj z vsebino? Ali zares želite izbrisati izbrane predmete in vsebino? Le krajevno - Prišlo je do napake med ustvarjanjem pogovornega okna spora! Različice %1$s v sporu. Krajevna datoteka Če izberete obe različici, bo k imenu krajevne datoteke dodana številka. diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 600a73fb3ce1..50f7c9c863e6 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -7,7 +7,6 @@ Krijimi i llogarisë dështoi Ikona e llogarisë Llogaria nuk u gjet! - Pastroni ngarkimet e dështuara Përpuno Pastro të gjithë njoftimet Dërgoje/Shpërndaje @@ -131,7 +130,6 @@ Doni vërtet të hiqet %1$s dhe përmbajtja e tij? Doni vërtet të hiqni artikujt e zgjedhur dhe përmbajtjen e tyre? Vetëm vendorja - Gabim në krijimin e dialogut për konflikte! Nëse zgjidhni të dy versionet, skedari lokal do të ketë një numër të bashkangjitur në emër Ikona e përdoruesit për listën e kontakteve Asnjë leje e dhënë, nuk u importua asgjë. diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index 6343160a1f3e..e8db00c4d220 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -8,7 +8,6 @@ Pravljenje naloga nije uspelo Ikona naloga Nalog nije nađen! - Izbriši neuspela otpremanja Izmeni Očisti sva obaveštenja Slanje/Deljenje @@ -66,6 +65,7 @@ Isključi proveru uštede baterije Sakrij fasciklu Avatar + Odsutan Zatvori Isključi Vaš uređaj možda ima uključenu optimizaciju baterije. Automatsko otpremanje radi samo ako isključite ovu aplikaciju iz optimizacije baterije. @@ -76,6 +76,7 @@ Odaberite lokalnu fasciklu… Odaberite udaljenu fasciklu… Greška pri čišćenju obaveštenja. + Obriši statusnu poruku Tekst prekopiran sa %1$s Nema teksta za kopiranje u ostavu Veza iskopirana @@ -155,7 +156,9 @@ Isključi Odbaci Skloni obaveštenje + Ne uznemiravaj Gotovo + Ne briši Ne mogu da napravim lokalni fajl Preuzmite najnoviju razvojnu verziju Ne mogu da preuzmem %1$s @@ -295,6 +298,7 @@ Udaljeni: %1$s Svi fajlovi su premešteni Prosledi + 4 sata Ime Beleška Lozinka @@ -302,6 +306,7 @@ Hostujte sopstveni server Otpremaj samo na punjenju /InstantUpload + Nevidljiv Veza Izlistani raspored Nema fajlova u ovoj fascikli. @@ -368,6 +373,8 @@ Molimo proverite kasnije. Nema internet konekcije 1 sat + Na mreži + Mrežni status Server je izašao iz garancije, molimo ažurirajte ga! još menija Unesite kod za zaključavanje @@ -472,6 +479,8 @@ Ikona dugma za slanje Postavi kao Postavi sliku kao + Postavi status + Postavi statusnu poruku Podeli Deljenje Ističe na %1$s @@ -530,6 +539,7 @@ – Sertifikat servera nije od poverenja - Datumi važenja sertifikata su u budućnosti – Adresa se ne poklapa sa imenom domaćina u sertifikatu + Poruka stanja Kamera Odaberite lokaciju skladišta Podrazumevano @@ -672,6 +682,7 @@ Slika šta je novo Preskoči Novo u %1$s + Koji je vaš status? Skidam fajlove… Ne mogu da sinhronizujem %1$d fajl (konflikata: %2$d) diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 30d08f507a51..71a19cc8f3b5 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -7,7 +7,6 @@ Прављење налога није успело Икона налога Налог није нађен! - Избриши неуспела отпремања Уреди Очисти сва обавештења Испразни корпу за отпатке @@ -57,6 +56,8 @@ Не могу да се преузму типови задатака, молимо вас да проверите везу са интернетом. Асистент Непознато + Унос + Излаз Придружени налог није нађен! Неуспешан приступ: %1$s Налог још није додат на овај уређај @@ -171,7 +172,7 @@ Заиста желите да обришете %1$s и сав њен садржај? Заиста желите да обришете означене ставке и њихов садржај? Само локално - Грешка при прављењу дијалога сукоба! + Нем оже да се креира дијалог за разрешавање конфилкта Конфликтни фајл %1$s Локални фајл Ако изаберете обе верзије, локални фајл имаће број придодат свом називу. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 703267b6e654..ff83cad00522 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -7,7 +7,6 @@ Skapande av konto misslyckades Kontoikon Kontot hittades inte! - Ta bort misslyckade uppladdningar Redigera Rensa alla aviseringar Töm papperskorgen @@ -57,6 +56,7 @@ Det går inte att hämta uppgiftstyper, kontrollera din internetanslutning. Assistent Okänd + Inmatning Associerat konto kunde inte hittas! Åtkomst misslyckades: %1$s Kontot har ännu inte lagts till på den här enheten @@ -171,7 +171,7 @@ Vill du verkligen ta bort %1$s och dess innehåll? Vill du verkligen radera de valda objekten och dess innehåll? Endast lokalt - Fel vid skapande av konfliktdialog! + Dialogrutan för konfliktlösning kan inte skapas Konfliktande fil %1$s Lokal fil Om du väljer båda versionerna kommer den lokala filen få ett nummer tillagt i filnamnet. diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 6b2168742ee0..1e3da07892b5 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -7,7 +7,6 @@ การสร้างบัญชีล้มเหลว ไอคอนบัญชี ไม่พบบัญชี! - ล้างการอัปโหลดที่ล้มเหลว แก้ไข ล้างการแจ้งเตือน ล้างข้อมูลในถังขยะ @@ -147,7 +146,6 @@ คุณต้องการลบ %1$s และเนื้อหาที่อยู่ในนั้นจริง ๆ หรือไม่? คุณต้องการลบรายการที่เลือกและเนื้อหาที่อยู่ในนั้นจริง ๆ หรือไม่? เฉพาะต้นทางเท่านั้น - ข้อผิดพลาดในการสร้างกล่องโต้ตอบข้อขัดแย้ง! ขัดขัดแย้งไฟล์ %1$s ไฟล์ต้นทาง ถ้าคุณเลือกทั้งสองรุ่น ชื่อของไฟล์ที่อยู่ในเครื่องจะมีตัวเลขเพิ่มเข้าไป diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index ba8ed96063c3..962c6b34d48f 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -7,7 +7,6 @@ Akaunt döretmeklik şowsuz boldy Hasap nyşany Hasap tapylmady - Şowsuz ýüklemeleri arassalaň Redaktirläň ähli bildirişleri arassala Iber / Paýlaş @@ -137,7 +136,6 @@ Hakykatdan hem %1$sonuň mazmuny hem pozmak isleýärsiňizmi? Saýlanan elementleri we olaryň mazmunyny hakykatdanam pozmak isleýärsiňizmi? Diňe ýerli - Jedelli gepleşikleri döretmekdäki ýalňyşlyk! Jedelli faýl%1$s Eger siz iki wersiýasyny hem saýlasaňyz, ýerli faýlyň adyna san goşular. Aragatnaşyk sanawy üçin ulanyjy nyşany diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 43b1e0e7864a..91c82f0b948e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -7,7 +7,6 @@ Hesap oluşturulamadı Hesap simgesi Hesap bulunamadı! - Tamamlanamayan yüklemeler silinsin Düzenle Tüm bildirimleri temizle Çöp kutusunu boşalt @@ -57,6 +56,8 @@ Görev türleri alınamadı. Lütfen İnternet bağlantınızı denetleyin. Yardımcı Bilinmiyor + Giriş + Çıkış İlişkili hesap bulunamadı! Erişilemedi: %1$s Aygıt üzerinde henüz bu hesap açılmamış @@ -171,7 +172,7 @@ %1$s klasörünü ve içindekileri silmek istediğinize emin misiniz? Seçilmiş ögeleri ve içindekileri silmek istediğinize emin misiniz? Yalnızca yerel - Çakışma penceresi oluşturulurken sorun çıktı! + Çakışma çözümleme penceresi oluşturulamadı %1$s dosyası çakışıyor Yerel dosya İki sürümü de saklamayı seçerseniz, yerel dosyanın adına bir numara eklenecek. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b1974fff65ff..1f26463e95b2 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -7,7 +7,6 @@ Не вдалося створити обліковий запис Зображення облікового запису Обліковий запис не знайдено! - Очистити невдалі завантаження Редагувати Очистити всі сповіщення Очистити кошик @@ -57,6 +56,8 @@ Не вдалося отримати типи завдань. Перевірте з\'єднання з мережею. Помічник Невідомо + Введення + Виведення Пов\'язаний обліковий запис не знайдено! Доступ невдалий: %1$s Обліковий запис все ще не створено на цьому пристрої @@ -171,7 +172,7 @@ Ви справді бажаєте вилучити %1$s та його вміст? Дійсно вилучити вибрані об\'єкти та їхній вміст? Лише на пристрої - Помилка у створенні діалогу щодо конфлікту! + Неможливо ініціювати діалог розв\'язання конфлікту Конфліктний файл %1$s Файл на пристрої Якщо ви виберете обидві версії, то до назви локального файлу буде додано порядковий номер. @@ -253,7 +254,7 @@ Спільнота Зображення тла верхньої панелі Події - Усі файли + Усі документи Помічник Із зірочкою Зображення та відео @@ -261,7 +262,7 @@ Старт Сповіщення На пристрої - Приватні файли + Мої документи Нещодавно змінені Спільні Кошик @@ -353,7 +354,7 @@ Поки що ви нічого не позначили зірочкою Тут можна буде знайти файли та каталоги, які ви позначити зірочкою. Не знайдено ані зображень, ані відео - Тут немає файлів + Тут відсутні файли Жодного збігу у цьому каталозі Жодного збігу Тут нічого немає. Ви можете створити каталог. diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index acf96f9d6f57..968796c73bf8 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,7 +7,6 @@ Tạo tài khoản không thành công Biểu tượng tài khoản Không tìm thấy tài khoản! - Dọn dẹp các tải lên bị lỗi Chỉnh sửa Xoá tất cả thông báo Dọn sạch thùng rác @@ -147,7 +146,6 @@ Bạn có thực sự muốn xóa %1$s và nội dung của nó? Bạn có thực sự muốn xóa các mục đã chọn và nội dung của chúng không? Chỉ cục bộ - Lỗi khi tạo hộp thoại xung đột! Tập tin xung đột %1$s Tệp cục bộ Nếu bạn chọn cả hai phiên bản, tệp trên máy sẽ có một số được thêm vào tên của nó. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 230e09218600..ff8fee2903eb 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -7,7 +7,6 @@ 账号创建失败 账号图标 找不到账号! - 清除失败上传任务 编辑 清除所有通知 清空垃圾箱 @@ -47,6 +46,7 @@ 任务已创建 任务已删除 未知 + 输出结果 相关账号未找到! 访问已失败: %1$s 该账号尚未添加到此设备上 @@ -161,7 +161,6 @@ 确定删除%1$s及其相关内容? 确定删除选中项及其内容? 仅本地 - 创建冲突会话时出错! 冲突文件 %1$s 本地文件 如果您选择两个版本,则本地文件的名称后面将附加一个数字。 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 439093a931b7..da4ab7bc480a 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -4,10 +4,9 @@ 關於 版本 %1$s 版本 %1$s,組建 #%2$s - 建立賬戶失敗 - 賬戶圖示 - 沒有找到任何賬戶 - 清除失敗的上傳 + 建立帳戶失敗 + 帳戶圖示 + 沒有找到任何帳戶 編輯 清除所有通知訊息 清空回收桶 @@ -45,6 +44,7 @@ 任務清單加載中,請稍候 無可用的工作項目。選取工作項目類型以建立新工作項目。 沒有可用於 %s 任務項目類型的任務項目,您可以從右下角建立新任務項目。 + 進行中 預定 已完成 建立任務項目時發生錯誤 @@ -56,11 +56,13 @@ 無法擷取任務類型,請檢查您的網際網路連線。 助手 不詳 - 無法找到連結賬戶 + 輸入 + 輸出 + 無法找到連結帳戶 存取失敗:%1$s - 此賬戶尚未加入到這個裝置 - 此裝置上已經存在同樣伺服器與用戶賬戶 - 輸入的用戶與此賬戶的用戶不一致 + 此帳戶尚未加入到這個裝置 + 此裝置上已經存在同樣伺服器與用戶帳戶 + 輸入的用戶與此帳戶的用戶不一致 無法辨識的伺服器版本 連線已建立 您的伺服器並未回傳正確的用戶名稱,請您聯絡相關管理人員。 @@ -84,7 +86,7 @@ 發生未知的HTTP錯誤! 發生了不詳的錯誤! 無法找到主機 - %1$s 不支援多個賬戶 + %1$s 不支援多個帳戶 無法建立連線 以唯讀模式保留在原本的資料夾 僅在未計量的Wi-Fi上傳 @@ -125,7 +127,7 @@ 返回 取消 取消同步 - 選擇賬戶 + 選擇帳戶 確認 複製 刪除 @@ -143,8 +145,8 @@ 傳送 分享 略過 - 切換賬戶 - 切換到賬戶 + 切換帳戶 + 切換到帳戶 測試開發版本 這包含所有最新功能,預期執行上仍可能會有錯誤,若發現錯誤請舉報給我們。 @@ -170,7 +172,7 @@ 你真的想要刪除 %1$s 與裡面的檔案? 您真的想要刪除所選項目和裡面的內容嗎? 只有近端 - 建立抵觸訊息時發生錯誤! + 無法創建衝突解決程序對話框 檔案 %1$s 抵觸 近端檔案 若您同時選擇兩個版本,近端的檔案會在檔名後附加編號。 @@ -208,7 +210,7 @@ 每日備份 要備份的數據 不正確的認證方式 - 刪除賬戶 + 刪除帳戶 刪除條目 刪除連結 取消全選 @@ -308,13 +310,13 @@ 顯示檔案操作時出錯 更改檔案上鎖狀態時出錯 舉報 - 想舉報問題?(需要GitHub賬戶) + 想舉報問題?(需要GitHub帳戶) 檢索檔案時發生錯誤 取得範本時發生錯誤 顯示加密設定對話方塊時發生錯誤! 無法啟動相機 文件掃描開始時出錯 - 賬戶 + 帳戶 時段運行在48小時內 創建於 工作名稱 @@ -388,8 +390,8 @@ 完成 取代 準備遷移… - 回復賬戶設置中… - 正在儲存賬戶設置… + 回復帳戶設置中… + 正在儲存帳戶設置… 您仍然想要變更資料儲存資料夾為 %1$s 嗎?\n\n注意:所有資料都必須再次下載。 來源資料夾無法讀取 更新索引中… @@ -538,7 +540,7 @@ 沒有可用於處理郵件地址的應用程式 沒有項目 沒有應用程式可以處理地圖 - 只可使用一個賬戶 + 只可使用一個帳戶 沒有應用程式可以開啟 PDF 沒有可用於發送所選檔案的應用程式 請選取至少一個要分享的權限。 @@ -600,10 +602,10 @@ 覆寫遠端的版本 重新命名新的版本 當檔案已經存在時我該怎麼做? - 新增賬戶 + 新增帳戶 同步日曆與通訊錄 尚未安裝 F-Droid 或 Google Play - 為目前用戶賬戶設定 DAVx5 協定(以前稱為 DAVdroid)(版本 v1.3.0 以上) + 為目前用戶帳戶設定 DAVx5 協定(以前稱為 DAVdroid)(版本 v1.3.0 以上) 日曆與通訊錄同步設定 關於 詳細資料 @@ -637,7 +639,7 @@ 使用以下工具保護 app 裝置身分驗證方式 通行碼 - 管理賬戶 + 管理帳戶 向朋友推薦 移除近端加密 設置端對端加密 @@ -660,7 +662,7 @@ 私隱政策 新名稱 由於依賴專有的Google Play服務,推送通知已停用。 - 由於登入已過期,因此沒有推送通知。請考慮重新添加您的賬戶。 + 由於登入已過期,因此沒有推送通知。請考慮重新添加您的帳戶。 推送通知目前無法使用。 QR 碼無法讀取! 在您的裝置上試用 %1$s! @@ -673,17 +675,17 @@ 您可以在此客戶端上本地刪除端到端加密 您可以在此客戶端上本地刪除端到端加密。加密檔案將保留在伺服器上,但不會再同步到此電腦。 刪除失敗 - 移除近端賬戶 - 從裝置中刪除賬戶並刪除所有近端檔案 + 移除近端帳戶 + 從裝置中刪除帳戶並刪除所有近端檔案 移除通知失敗 移除 已刪除 輸入新名稱 無法重新命名近端複本,請試用不同名稱 不可重新命名,名稱已存在 - 請求賬戶刪除 + 請求帳戶刪除 請求刪除 - 請求服務提供者永久刪除賬戶 + 請求服務提供者永久刪除帳戶 不允許重新分享 不允許重新分享 沒有可用調整過的圖像,下載完整圖像? @@ -701,7 +703,7 @@ 全在您一指之間 活動、分享… 可快速存取所有資源 - 您所有的賬戶 + 您所有的帳戶 在一個地方 自動上傳 您的照片與視像 @@ -722,7 +724,7 @@ 使用圖片作為 設定狀態 設定狀態訊息 - 在設置端到端加密期間,您將收到一個隨機的 12 字助記碼,您需要在其他設備上打開文件。這將僅存儲在此設備上,並且可以在此屏幕中再次顯示。請記在安全的地方! + 在設置端到端加密期間,您將收到一個隨機的 12 字助記碼,您需要在其他設備上打開文件。這將僅存儲在此設備上,並且可以在此螢幕中再次顯示。請記在安全的地方! 分享 分享並複製連結 分享 @@ -772,7 +774,7 @@ 顯示照片 顯示影片 使用第三方登入 - 允許 %1$s 存取您的 Nextcloud 賬戶 %2$s? + 允許 %1$s 存取您的 Nextcloud 帳戶 %2$s? 排序方式 隱藏 細節 @@ -811,7 +813,7 @@ 完整存取權限 僅媒體唯讀 圖片 - 可自架且始終讓您掌控一切的生產力平台。\n功能:\n* 簡潔、現代的用戶界面,適合您伺服器的佈景主題\n* 上傳檔案到您的 Nextcloud 伺服器\n* 與其他人分享檔案\n* 讓您最愛的檔案與資料夾同步\n* 搜尋您伺服器上所有的資料夾\n* 以通知訊息保持更新\n* 多賬戶支援\n* 使用指紋或 PIN 碼安全存取您的資料\n* 與 DAVx5 整合(先前被稱為 DAVdroid),可輕易設定行事曆與聯絡人同步\n\n請至 https://github.com/nextcloud/android/issues 舉報所有問題,並於 https://help.nextcloud.com/c/clients/android 討論此應用程式\n\n您是 Nextcloud 新手嗎?Nextcloud 是一個私人的檔案同步、共享與通訊的伺服器。它是自由軟體,您可以自行架設,或是付費請專業公司協助您建置。如此,您便可完全掌控您的照片、行事曆與聯絡人資料、您的文件與其他任何東西。\n\n請造訪 https://nextcloud.com 以取得更多資訊 + 可自架且始終讓您掌控一切的生產力平台。\n功能:\n* 簡潔、現代的用戶界面,適合您伺服器的佈景主題\n* 上傳檔案到您的 Nextcloud 伺服器\n* 與其他人分享檔案\n* 讓您最愛的檔案與資料夾同步\n* 搜尋您伺服器上所有的資料夾\n* 以通知訊息保持更新\n* 多帳戶支援\n* 使用指紋或 PIN 碼安全存取您的資料\n* 與 DAVx5 整合(先前被稱為 DAVdroid),可輕易設定行事曆與聯絡人同步\n\n請至 https://github.com/nextcloud/android/issues 舉報所有問題,並於 https://help.nextcloud.com/c/clients/android 討論此應用程式\n\n您是 Nextcloud 新手嗎?Nextcloud 是一個私人的檔案同步、共享與通訊的伺服器。它是自由軟體,您可以自行架設,或是付費請專業公司協助您建置。如此,您便可完全掌控您的照片、行事曆與聯絡人資料、您的文件與其他任何東西。\n\n請造訪 https://nextcloud.com 以取得更多資訊 可自架且始終讓您掌控一切的生產力平台。\n這是官方的開發版本,每天都會提供未經測試的任何新功能的範例,可能造成不穩定或資料遺失。此應用程式適合願意測試且舉報遇到的問題的用戶。不要將其用於您的生產力工作!\n\nF-Droid 同時提供了官方的開發版本與一般版本,可同時安裝。 讓你能完全掌控、自建且具生產力的平台 讓你能完全掌控、自建且具生產力的平台(開發者預覽版) @@ -837,7 +839,7 @@ 同步失敗,需重新登入。 檔案已同步 無法完成資料夾 %1$s 的同步作業 - 於1.3.16版本中,此裝置已上傳的檔案會複製到近端%1$s資料夾,以防止多重賬戶同步單一檔案時造成遺失。透過此應用程式更早的版本上傳的檔案則複製到%2$s資料夾。但是在同步時出現錯誤會阻礙此操作完成,你可以選擇無視此檔案並移除%3$s的連結,或者移動此檔案到%1$s資料夾並保留%4$s的連結,下面列出%5$s中近端與遠端檔案之間的連結。 + 於1.3.16版本中,此裝置已上傳的檔案會複製到近端%1$s資料夾,以防止多重帳戶同步單一檔案時造成遺失。透過此應用程式更早的版本上傳的檔案則複製到%2$s資料夾。但是在同步時出現錯誤會阻礙此操作完成,你可以選擇無視此檔案並移除%3$s的連結,或者移動此檔案到%1$s資料夾並保留%4$s的連結,下面列出%5$s中近端與遠端檔案之間的連結。 有些近端端的檔案已遺失 取得檔案最近的版本 選擇同步項目 @@ -947,8 +949,8 @@ %1$s 已上傳 退出 設定 - 無 %1$s 賬戶,請先建立賬戶。 - 找不到賬戶 + 無 %1$s 帳戶,請先建立帳戶。 + 找不到帳戶 目前 失敗/等待重啟 已上傳 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 84e5699237f2..5ff629642381 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -7,7 +7,6 @@ 帳號建立失敗 帳號圖示 找不到帳號! - 清除失敗的上傳 編輯 清除所有通知訊息 清空回收桶 @@ -57,6 +56,8 @@ 無法擷取工作項目類型,請檢查您的網際網路連線。 助理 未知 + 輸入 + 輸出 找不到相關的帳號! 存取失敗:%1$s 此帳號尚未加入到這個裝置 @@ -171,7 +172,7 @@ 你真的想要刪除 %1$s 與裡面的檔案? 您真的想要刪除所選項目和裡面的內容嗎? 僅本機 - 建立衝突對話框時發生錯誤! + 無法建立衝突解決對話方塊 檔案 %1$s 衝突 本機檔案 若您同時選擇兩個版本,本機的檔案會在檔名後附加編號。 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d28fe7946729..9a721eb3e385 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,6 +15,7 @@ #ffffff #B3FFFFFF #333333 + #F5F5F5 #303034 #E9E8EB @color/secondary_text_color diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2777a201eee..a7ebed20a682 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,9 @@ Failed In Progress + Input + Output + All Assistant @@ -994,7 +997,7 @@ Thumbnail for new file Thumbnail for existing file Invalid URL - Error creating conflict dialog! + Conflict resolver dialog cannot be created QR code could not be read! Note icon Add another link diff --git a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 13981df525a9..49f1b5e5e848 100644 --- a/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/app/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -10,6 +10,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.preferences.SubFolderRule; import com.owncloud.android.datamodel.MediaFolderType; +import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.files.services.NameCollisionPolicy; @@ -164,6 +165,7 @@ private SyncedFolderDisplayItem create(String folderName, boolean enabled) { MediaFolderType.IMAGE, false, SubFolderRule.YEAR_MONTH, - true); + true, + SyncedFolder.NOT_SCANNED_YET); } } diff --git a/build.gradle b/build.gradle index c4a9144bb143..bac1a6c1368a 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="0c886d61f6" + androidLibraryVersion ="5fbc7d3a9bc9fd123ce9593cc500bb6415e797c6" androidPluginVersion = '8.3.2' androidxMediaVersion = '1.3.1' androidxTestVersion = "1.5.0" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 9692aac00dab..cb11a2ec946c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -766,6 +766,11 @@ + + + + + @@ -5167,6 +5172,14 @@ + + + + + + + + diff --git a/scripts/updateLibraryHash.sh b/scripts/updateLibraryHash.sh index 34d3e5536988..4099efc09aa4 100755 --- a/scripts/updateLibraryHash.sh +++ b/scripts/updateLibraryHash.sh @@ -21,5 +21,7 @@ sed -i s"#androidLibraryVersion\ =.*#androidLibraryVersion =\"$latestCommit\"#" git add build.gradle git add gradle/verification-metadata.xml -git commit -s -m "Update library" -gh pr create --head "$(git branch --show-current)" --title "Update library $(date +%F)" --body "Update library to latest commit" +git commit -s -m "Update library to $(date +%F)" + +git push -u origin HEAD +gh pr create --fill diff --git a/src/generic/fastlane/metadata/android/fr-FR/full_description.txt b/src/generic/fastlane/metadata/android/fr-FR/full_description.txt index 24ba78ea975d..1cd11c9e8254 100644 --- a/src/generic/fastlane/metadata/android/fr-FR/full_description.txt +++ b/src/generic/fastlane/metadata/android/fr-FR/full_description.txt @@ -1,19 +1,19 @@ -The self-hosted productivity platform that keeps you in control. +La plateforme de productivité auto-hébergée qui vous redonne le contrôle. -Features: -* Easy, modern interface, suited to the theme of your server -* Upload files to your Nextcloud server -* Share them with others -* Keep your favorite files and folders synced -* Search across all folders on your server -* Auto Upload for photos and videos taken by your device -* Keep up to date with notifications -* Multi-account support -* Secure access to your data with fingerprint or PIN -* Integration with DAVx5 (formerly known as DAVdroid) for easy setup of calendar and contacts synchronization +Fonctionnalités: +* Intuitif, interface moderne, adaptée au thème de votre serveur. +* Envoyez des fichiers sur votre serveur Nextcloud +* Partagez avec d'autres personnes +* Gardez vos fichiers et dossiers préférés synchronisés +* Recherchez dans tous les dossiers de votre serveur +* Téléversement automatique des photos et vidéos prises par votre appareil +* Restez à jour avec les notifications +* Supporte plusieurs comptes simultanément +* Accès sécurisé avec votre empreinte digitale ou un PIN +* Intégration avec DAVx5 (ancien nom : DAVdroid) pour configurer facilement l'agenda&et synchroniser les contacts -Please report all issues at https://github.com/nextcloud/android/issues and discuss this app at https://help.nextcloud.com/c/clients/android +Merci de signaler les problèmes sur https://github.com/nextcloud/android/issues et discutez de cette application sur https://help.nextcloud.com/c/clients/android -New to Nextcloud? Nextcloud is a private file sync and share and communication server. It is libre software, and you can host it yourself or pay a company to do it for you. That way, you are in control of your photos, your calendar and contact data, your documents and everything else. +New sur Nextcloud ? Nextcloud est un serveur privé de synchronisation de fichiers&de partage et de communications. C'est un logiciel libre, et vous pouvez l'héberger vous-même ou payer une entreprise pour le faire à votre place. De cette façon, vous avez le contrôle de vos photos, votre agenda et vos données de contacts, vos documents et tout le reste. -Check out Nextcloud at https://nextcloud.com \ No newline at end of file +Apprenez en plus sur Nextcloud sur https://nextcloud.com \ No newline at end of file