diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 0000000000000000000000000000000000000000..2369228816d4670ce70d83e414bf1a7b8f30a9de --- /dev/null +++ b/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_PATH: "vendor/bundle" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a70c33a47a744184ea6f678483cb420cdbc4fbd0..414afb07ed5b336b87ddad134fdb17f83d42484f 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,3 @@ # These are supported funding model platforms -liberapay: stefan-niedermann -custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA +custom: https://nextcloud.com/include/ diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5b98b9a611eddb93f25aeb84439ad535bd10f590..042b6066e5921c9668ce0dfd2f928134c10d7359 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,7 +8,7 @@ labels: bug Guidelines for submitting issues: * Bug reports which do not fill the complete issue template will be closed. -* Please have a look at our [FAQ](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md) +* Please have a look at our [FAQ](https://github.com/nextcloud/notes-android/blob/main/FAQ.md) * Please search the existing issues first, it's likely that your issue was already reported or even fixed. * This repository is *only* for issues within the Nextcloud Notes Android app --> diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 75c8395a5f45169e2b19e7e50ce67bc36ef1eeb3..60f86e7b86d7138f61471c9bb4983c5691005407 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,12 +1,18 @@ +# synced from @nextcloud/android-config version: 2 updates: -- package-ecosystem: gradle - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 -- package-ecosystem: github-actions - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + time: "03:00" + timezone: Europe/Paris + rebase-strategy: "disabled" + open-pull-requests-limit: 10 + labels: + - 3. to review + - dependencies diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index a994c5c06bf4a23565de18b87edc5331dc3f529c..bdc401a0bf0cec9bd695949d06df20fa22ad0548 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,6 +1,9 @@ name: Android CI -on: [push, pull_request] +on: + pull_request: + push: + branches: [ main, stable-* ] permissions: contents: read diff --git a/.github/workflows/autoApproveDependabot.yml b/.github/workflows/autoApproveDependabot.yml new file mode 100644 index 0000000000000000000000000000000000000000..160c7f8e680e26a24dab49fef281157df0a7ef5f --- /dev/null +++ b/.github/workflows/autoApproveDependabot.yml @@ -0,0 +1,30 @@ +# synced from @nextcloud/android-config +name: Auto approve dependabot + +on: + pull_request_target: + branches: + - main + - master + - stable-* + +permissions: + contents: read + +concurrency: + group: dependabot-approve-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + auto-approve: + name: Auto approve dependabot + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + permissions: + # needed to approve the PR + pull-requests: write + + steps: + - uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4 # v3.2.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/autoApproveSync.yml b/.github/workflows/autoApproveSync.yml new file mode 100644 index 0000000000000000000000000000000000000000..fb714467423a264f89b89ede07084bf16c10bed1 --- /dev/null +++ b/.github/workflows/autoApproveSync.yml @@ -0,0 +1,29 @@ +# synced from @nextcloud/android-config +name: Auto approve sync +on: + pull_request_target: + branches: + - master + - main + types: + - opened + - reopened + - synchronize + - labeled + +concurrency: + group: sync-approve-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + pull-requests: write + +jobs: + auto-approve: + name: Auto approve sync + runs-on: ubuntu-latest + if: ${{ contains(github.event.pull_request.labels.*.name, 'sync') && github.actor == 'nextcloud-android-bot' }} + steps: + - uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4 # v3.2.1 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d06291f0a328e47f619d4eb1f1b52086d8050379..396fa26aacc12308a7461c9bfd7dd5d128ff6f19 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,27 +1,49 @@ -name: CodeQL security scan +# synced from @nextcloud/android-config +name: "CodeQL" on: + push: + branches: [ "master", "main", "stable-*" ] pull_request: + branches: [ "master", "main" ] schedule: - - cron: '0 12 * * *' + - cron: '24 18 * * 3' permissions: contents: read - security-events: write - pull-requests: read jobs: - codeql: - name: CodeQL security scan + analyze: + name: Analyze runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: [ 'java' ] steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - 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@v2 + uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6 + with: + languages: ${{ matrix.language }} + - name: Set up JDK 17 + uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0 with: - languages: java - - name: Build debug APK - run: bash ./gradlew assembleDev --stacktrace + distribution: "temurin" + java-version: 17 + - name: Assemble + run: | + mkdir -p "$HOME/.gradle" + echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties" + ./gradlew assembleDebug - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6 diff --git a/.github/workflows/command-rebase.yml b/.github/workflows/command-rebase.yml new file mode 100644 index 0000000000000000000000000000000000000000..03109861ea5245cf459d5cce998e65ba83fdfe91 --- /dev/null +++ b/.github/workflows/command-rebase.yml @@ -0,0 +1,51 @@ +# 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 + +name: Rebase command + +on: + issue_comment: + types: created + +permissions: + contents: read + +jobs: + rebase: + runs-on: ubuntu-latest + permissions: + contents: none + + # On pull requests and if the comment starts with `/rebase` + if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase') + + steps: + - name: Add reaction on start + uses: peter-evans/create-or-update-comment@ca08ebd5dc95aa0cd97021e9708fcd6b87138c9b # v3.0.1 + with: + token: ${{ secrets.COMMAND_BOT_PAT }} + repository: ${{ github.event.repository.full_name }} + comment-id: ${{ github.event.comment.id }} + reaction-type: "+1" + + - name: Checkout the latest code + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 + with: + fetch-depth: 0 + token: ${{ secrets.COMMAND_BOT_PAT }} + + - name: Automatic Rebase + uses: cirrus-actions/rebase@b87d48154a87a85666003575337e27b8cd65f691 # 1.8 + env: + GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }} + + - name: Add reaction on failure + uses: peter-evans/create-or-update-comment@ca08ebd5dc95aa0cd97021e9708fcd6b87138c9b # v3.0.1 + if: failure() + with: + token: ${{ secrets.COMMAND_BOT_PAT }} + repository: ${{ github.event.repository.full_name }} + comment-id: ${{ github.event.comment.id }} + reaction-type: "-1" diff --git a/.github/workflows/detectNewJavaFiles.yml b/.github/workflows/detectNewJavaFiles.yml new file mode 100644 index 0000000000000000000000000000000000000000..b64964cef7c2d8863b82053dba2806eccd19dd4f --- /dev/null +++ b/.github/workflows/detectNewJavaFiles.yml @@ -0,0 +1,32 @@ +# synced from @nextcloud/android-config +name: "Detect new java files" + +on: + pull_request: + branches: [ master, main, stable-* ] + +permissions: read-all + +jobs: + detectNewJavaFiles: + runs-on: ubuntu-latest + steps: + - id: file_changes + uses: trilom/file-changes-action@a6ca26c14274c33b15e6499323aac178af06ad4b # v1.2.4 + with: + output: ',' + - name: Detect new java files + run: | + if [ -z '${{ steps.file_changes.outputs.files_added }}' ]; then + echo "No new files added" + exit 0 + fi + new_java=$(echo '${{ steps.file_changes.outputs.files_added }}' | tr ',' '\n' | grep '\.java$' | cat) + if [ -n "$new_java" ]; then + # shellcheck disable=SC2016 + printf 'New java files detected:\n```\n%s\n```\n' "$new_java" | tee "$GITHUB_STEP_SUMMARY" + exit 1 + else + echo "No new java files detected" + exit 0 + fi diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000000000000000000000000000000000000..be44c9e6a2138cc293dc6a867d6ab3e4fc35a30f --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,18 @@ +# synced from @nextcloud/android-config +name: "Validate Gradle Wrapper" +on: + pull_request: + branches: [ master, stable-* ] + push: + branches: [ master, stable-* ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: gradle/wrapper-validation-action@8d49e559aae34d3e0eb16cde532684bc9702762b # v1.0.6 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000000000000000000000000000000000..e774cebe8eb8e87c47012a260851b7ad63f54ade --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,38 @@ +# synced from @nextcloud/android-config +name: Scorecard supply-chain security +on: + branch_protection_rule: + schedule: + - cron: '32 23 * * 4' + push: + branches: [ "main", "master" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + + steps: + - name: "Checkout code" + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: false + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6 + with: + sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000000000000000000000000000000000..d996914db8ccc332cf6987730a7fa8878602b74a --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,28 @@ +# synced from @nextcloud/android-config +name: 'Close stale issues' +on: + schedule: + - cron: '0 0 * * *' + +# Declare default permissions as read only. +permissions: read-all + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + with: + days-before-stale: 28 + days-before-close: 14 + days-before-pr-close: -1 + only-labels: 'bug,needs info' + exempt-issue-labels: 'no-stale' + stale-issue-message: >- + This bug report did not receive an update in the last 4 weeks. + Please take a look again and update the issue with new details, + otherwise the issue will be automatically closed in 2 weeks. Thank you! + exempt-all-pr-milestones: true diff --git a/.gitignore b/.gitignore index e93457ad01c1b84b8d3837691b154f5e29d2802b..10595f178c068eb7c72b1030a8daffa1c1683cfe 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ /.idea/ *.iml /projectFilesBackup/ +# fastlane +/vendor/bundle +fastlane/report.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bfbbba567776381e98971839ef03bfd4f99cf4eb..3f61ce459f316b2f17af12aa485c0d4bf997b82c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,21 +50,16 @@ build: - git config --global http.sslverify false # update $UPSTREAM_BRANCH & tags - git fetch origin - - git checkout $UPSTREAM_BRANCH - git remote add upstream $UPSTREAM_URL - git fetch upstream - - git pull upstream $UPSTREAM_DEFAULT_BRANCH - - git push origin $UPSTREAM_BRANCH - - git push origin --tags # checkout to latest tag commit to $TEMP_LATEST_TAG_BRANCH - - git checkout $(git describe --tags --abbrev=0) + - git checkout $(git ls-remote --tags upstream | grep -o 'refs/tags/[0-9]*\.[0-9]*\.[0-9]*' | sort -r | head -n 1 | grep -o '[^\/]*$') - git checkout -b $TEMP_LATEST_TAG_BRANCH # merge $LOCAL_BRANCH with $TEMP_LATEST_TAG_BRANCH & push - git checkout $LOCAL_BRANCH - - git submodule sync - - git submodule update --init --recursive --force - git merge $TEMP_LATEST_TAG_BRANCH - git push origin $LOCAL_BRANCH + - git push origin --tags # remove unwanted local branch & remote - git branch -D $TEMP_LATEST_TAG_BRANCH - git remote remove upstream @@ -74,7 +69,5 @@ update-default-branch: extends: .update-from-upstream variables: LOCAL_BRANCH: main - UPSTREAM_BRANCH: upstream/master - UPSTREAM_DEFAULT_BRANCH: main UPSTREAM_URL: https://github.com/nextcloud/notes-android.git TEMP_LATEST_TAG_BRANCH: latest_upstream_tag_branch \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 107335520e71ac05262be11f8142fe36e3bc4819..cf84171f5c8781140f4c2473408aa44c34eb3dda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Submitting bug reports -If you find a bug, feel free to [open an issue](https://github.com/stefan-niedermann/OwnCloud-Notes/issues/new). But please provide these information in the comment: +If you find a bug, feel free to [open an issue](https://github.com/nextcloud/notes-android/issues/new). But please provide these information in the comment: **Android version:** e. g. 6.0.1 Marshmallow diff --git a/FAQ.md b/FAQ.md index b75c76066107abefed7a7619bd646edb15137d58..0ddbb7bbdc1548ea1d89850702687dc15d4e4011 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,19 +1,19 @@ # Frequently asked questions -- [Why aren't there any buttons to apply formatting?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#why-arent-there-any-buttons-to-apply-formatting) -- [I have experienced an error](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#i-have-experienced-an-error) - - [`NextcloudApiNotRespondingException`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#nextcloudapinotrespondingexception) - - [`UnknownErrorException: Read timed out`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#unknownerrorexception-read-timed-out) - - [`NextcloudHttpRequestFailedException`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#nextcloudhttprequestfailedexception) - - [`IllegalStateException: Duplicate key`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#illegalstateexception-duplicate-key) - - [`NextcloudFilesAppAccountNotFoundException`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#nextcloudfilesappaccountnotfoundexception) - - [`TokenMismatchException`](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#tokenmismatchexception) - - [Workarounds](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#workarounds) -- [How to share notes?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#how-to-share-notes) -- [Why don't you make an option for…?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#why-dont-you-make-an-option-for) -- [Why is there no support for pens?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#why-is-there-no-support-for-pens) -- [Why has my bug report been closed?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#why-has-my-bug-report-been-closed) -- [How can i activate the dark mode for widgets?](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#how-can-i-activate-the-dark-mode-for-widgets) +- [Why aren't there any buttons to apply formatting?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#why-arent-there-any-buttons-to-apply-formatting) +- [I have experienced an error](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#i-have-experienced-an-error) + - [`NextcloudApiNotRespondingException`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#nextcloudapinotrespondingexception) + - [`UnknownErrorException: Read timed out`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#unknownerrorexception-read-timed-out) + - [`NextcloudHttpRequestFailedException`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#nextcloudhttprequestfailedexception) + - [`IllegalStateException: Duplicate key`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#illegalstateexception-duplicate-key) + - [`NextcloudFilesAppAccountNotFoundException`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#nextcloudfilesappaccountnotfoundexception) + - [`TokenMismatchException`](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#tokenmismatchexception) + - [Workarounds](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#workarounds) +- [How to share notes?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#how-to-share-notes) +- [Why don't you make an option for…?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#why-dont-you-make-an-option-for) +- [Why is there no support for pens?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#why-is-there-no-support-for-pens) +- [Why has my bug report been closed?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#why-has-my-bug-report-been-closed) +- [How can i activate the dark mode for widgets?](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#how-can-i-activate-the-dark-mode-for-widgets) ## Why aren't there any buttons to apply formatting @@ -49,7 +49,7 @@ Probably you will experience it when importing an account, because at this momen Further synchronizations are usually not causing this issue, because the Notes app tries to synchronize only *changed* notes after the first import. If your notes are not ten thousands of characters long, it is very unlikely that this causes a connection timeout. -We improved the import of an account in version `3.4.12` to make it more reliable by [fetching notes step by step](https://github.com/stefan-niedermann/nextcloud-notes/issues/761#issuecomment-836989421). +We improved the import of an account in version `3.4.12` to make it more reliable by [fetching notes step by step](https://github.com/nextcloud/notes-android/issues/761#issuecomment-836989421). If you are using an older version, you can as a workaround for the first import try to 1. move all your notes to a different folder on your Nextcloud instance 2. import your account on your smartphone @@ -59,29 +59,29 @@ If you are using an older version, you can as a workaround for the first import #### `HTTP status-code: 301` -This issue can happen in case of a complex inconsistent state between the Notes Android app, the Single Sign On library, the Nextcloud Android app and your Nextcloud instance. Please try to remove your account from *both*, Notes Android *and* Nextcloud Android and readd it again [as described in the `workarounds` section](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#workarounds). If the issue persists, please report especially any changes on your server side environment: Did you change your domain or IP address of your Nextcloud server? Did you change something about your user account or en- / disabled multi factor authentication (2FA / MFA)? Did you remove your account (only) from the Nextcloud Android app? +This issue can happen in case of a complex inconsistent state between the Notes Android app, the Single Sign On library, the Nextcloud Android app and your Nextcloud instance. Please try to remove your account from *both*, Notes Android *and* Nextcloud Android and readd it again [as described in the `workarounds` section](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#workarounds). If the issue persists, please report especially any changes on your server side environment: Did you change your domain or IP address of your Nextcloud server? Did you change something about your user account or en- / disabled multi factor authentication (2FA / MFA)? Did you remove your account (only) from the Nextcloud Android app? #### `HTTP status-code: 302` -As clearly described in the description of the app, [one of the requirements](https://github.com/stefan-niedermann/nextcloud-notes#link-requirements) is to have installed the [`Notes`](https://apps.nextcloud.com/apps/notes) app on your server. This means: +As clearly described in the description of the app, [one of the requirements](https://github.com/nextcloud/notes-android#link-requirements) is to have installed the [`Notes`](https://apps.nextcloud.com/apps/notes) app on your server. This means: - **not** [`Quick notes`](https://apps.nextcloud.com/apps/quicknotes) - **not** [`Carnet`](https://apps.nextcloud.com/apps/carnet) - **not** `SimpleNote`, `NextNotes`, `Joplin` nor any other app. -Only the [`Notes`](https://apps.nextcloud.com/apps/notes) app is supported by the Notes Android app. Granted, detecting a missing installation of the `Notes` app should be more seamlessly - [we are aware of it](https://github.com/stefan-niedermann/nextcloud-notes/issues/1475) and will try to enhance the detection. +Only the [`Notes`](https://apps.nextcloud.com/apps/notes) app is supported by the Notes Android app. Granted, detecting a missing installation of the `Notes` app should be more seamlessly - [we are aware of it](https://github.com/nextcloud/notes-android/issues/1475) and will try to enhance the detection. ### `IllegalStateException: Duplicate key` -This is issue was caused by a bug which was present in the Notes Android app between `3.4.0` and `3.4.10`. It has been fixed in `3.4.11`, though it created a corrupt database state which is not recoverable automatically without data loss. It is therefore required to [clear the storage of the Notes Android app](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#workarounds) and import your account again from scratch. Make sure to backup unsynchronized changes before doing this. +This is issue was caused by a bug which was present in the Notes Android app between `3.4.0` and `3.4.10`. It has been fixed in `3.4.11`, though it created a corrupt database state which is not recoverable automatically without data loss. It is therefore required to [clear the storage of the Notes Android app](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#workarounds) and import your account again from scratch. Make sure to backup unsynchronized changes before doing this. ### `NextcloudFilesAppAccountNotFoundException` -We are not yet sure what exactly causes this issue, but investigate it by [adding more debug logs to recent versions](https://github.com/stefan-niedermann/nextcloud-notes/issues/1256#issuecomment-859505153). In theory this might happen if an already imported account has been deleted in the Nextcloud app. +We are not yet sure what exactly causes this issue, but investigate it by [adding more debug logs to recent versions](https://github.com/nextcloud/notes-android/issues/1256#issuecomment-859505153). In theory this might happen if an already imported account has been deleted in the Nextcloud app. As a workaround you can remove the account (or clear the storage of the app as described below if you can't access the account manager anymore) and import it again. ### `TokenMismatchException` -The reason of this error is not yet clear. It often seems to be connected to changes of the authentication (for example enabling 2FA after some time). Please clear the storage of both, the Notes and the Nextcloud Android apps as described in the [workarounds](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/FAQ.md#wrokarounds) section. +The reason of this error is not yet clear. It often seems to be connected to changes of the authentication (for example enabling 2FA after some time). Please clear the storage of both, the Notes and the Nextcloud Android apps as described in the [workarounds](https://github.com/nextcloud/notes-android/blob/main/FAQ.md#wrokarounds) section. ### Workarounds @@ -106,7 +106,7 @@ Nextcloud / Notes Then set up your account in the Nextcloud Android app again and import the configured account in the Notes Android app. -If the issue persists, [open a bug report in our issue tracker](https://github.com/stefan-niedermann/nextcloud-notes/issues/new?assignees=&labels=bug&template=bug_report.md&title=). +If the issue persists, [open a bug report in our issue tracker](https://github.com/nextcloud/notes-android/issues/new?assignees=&labels=bug&template=bug_report.md&title=). ## How to share notes? @@ -145,7 +145,7 @@ Since i am not aware of any proper free SDK for pens, I recommend you to ask you ### Hardware issues -Given a [free SDK](#licensing-issues) can be found, there is another issue: I don't own a device with a pen. I welcome [Pull Requests](https://github.com/stefan-niedermann/nextcloud-notes/pulls) with contributions to this topic, but i can and will not buy a new device just for this aspect, sorry. +Given a [free SDK](#licensing-issues) can be found, there is another issue: I don't own a device with a pen. I welcome [Pull Requests](https://github.com/nextcloud/notes-android/pulls) with contributions to this topic, but i can and will not buy a new device just for this aspect, sorry. ## Why has my bug report been closed? diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000000000000000000000000000000000..7a118b49be750543e59f7b9c55123e11322b00c6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000000000000000000000000000000000..3a30044fa12c81f5fc2fd354f1339720a8356a87 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,218 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + artifactory (3.0.15) + atomos (0.1.3) + aws-eventstream (1.2.0) + aws-partitions (1.722.0) + aws-sdk-core (3.170.0) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.63.0) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sigv4 (~> 1.1) + aws-sdk-s3 (1.119.1) + aws-sdk-core (~> 3, >= 3.165.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.2) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.6.4) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.99.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.2.6) + fastlane (2.212.1) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (~> 2.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.35.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.0) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) + google-cloud-core (1.6.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.3.0) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) + domain_name (~> 0.5) + httpclient (2.8.3) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.0) + memoist (0.16.2) + mini_magick (4.12.0) + mini_mime (1.1.2) + multi_json (1.15.0) + multipart-post (2.0.0) + nanaimo (0.3.0) + naturally (2.2.1) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) + public_suffix (5.0.1) + rake (13.0.6) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.2.5) + rouge (2.0.7) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + security (0.1.3) + signet (0.17.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) + webrick (1.8.1) + word_wrap (1.0.0) + xcodeproj (1.22.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.4.6 diff --git a/README.md b/README.md index db30ed4891aa97faeabd1228799a8ae0d580b220..785ed3f8e0a6d1d06c632753c7f11343c2d7bbeb 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ in each version of Notes. ## License -Notes is licensed under the [GNU General Public License v3.0](https://gitlab.e.foundation/e/apps/Notes/-/blob/master/LICENSE) \ No newline at end of file +Notes is licensed under the [GNU General Public License v3.0](https://gitlab.e.foundation/e/apps/Notes/-/blob/master/LICENSE) diff --git a/app/.gitignore b/app/.gitignore index 3548d2f2da3e37f42faebc54be19f0bc8b60d9c0..120fe1141250850a2776b30fec1d07d8363b5114 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -3,6 +3,4 @@ /build /play /fdroid -/dev -/mdm -/pfungstadt \ No newline at end of file +/dev \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index e4b884d734a4d603f3e3a0840c1026b0a2a38260..faf37028afe9c967a356a1c2817933b95bc7cd12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.application' +apply plugin: 'org.jetbrains.kotlin.android' android { compileSdkVersion 33 - buildToolsVersion '31.0.0' compileOptions { coreLibraryDesugaringEnabled true @@ -14,8 +14,8 @@ android { applicationId "foundation.e.notes" minSdkVersion 24 targetSdkVersion 33 - versionCode 3007001 - versionName "3.7.1" + versionCode 40010090 + versionName "4.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { @@ -52,57 +52,67 @@ android { } +ext { + glideVersion = '4.15.1' + roomVersion = "2.5.1" +} + dependencies { - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' // Nextcloud SSO implementation 'foundation.e.lib:Android-SingleSignOn:1.0.5-alpha' - implementation ('com.github.stefan-niedermann:android-commons:0.2.7') { + implementation ('com.github.nextcloud.android-common:ui:0.12.0') { + exclude group: 'com.github.nextcloud', module: 'Android-SingleSignOn' + } + implementation ('com.github.stefan-niedermann:android-commons:0.2.9') { exclude group: 'com.github.nextcloud', module: 'Android-SingleSignOn' } - implementation ('com.github.stefan-niedermann.nextcloud-commons:exception:1.6.4') { + implementation ("com.github.stefan-niedermann.nextcloud-commons:exception:$commonsVersion") { exclude group: 'com.github.nextcloud', module: 'Android-SingleSignOn' } - implementation('com.github.stefan-niedermann.nextcloud-commons:markdown:1.6.4') { + implementation("com.github.stefan-niedermann.nextcloud-commons:markdown:$commonsVersion") { exclude group: 'org.jetbrains', module: 'annotations-java5' exclude group: 'com.github.nextcloud', module: 'Android-SingleSignOn' } // Glide - implementation 'com.github.bumptech.glide:glide:4.14.2' - annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2' + implementation "com.github.bumptech.glide:glide:$glideVersion" + annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" // Android X - implementation 'androidx.appcompat:appcompat:1.5.1' - implementation 'androidx.core:core-splashscreen:1.0.0' - implementation 'androidx.fragment:fragment:1.5.4' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.core:core-splashscreen:1.0.1' + implementation 'androidx.fragment:fragment:1.5.7' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1' implementation 'androidx.preference:preference:1.2.0' implementation 'androidx.recyclerview:recyclerview-selection:1.1.0' - implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'androidx.recyclerview:recyclerview:1.3.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' - implementation 'androidx.work:work-runtime:2.7.1' - implementation 'com.google.android.material:material:1.7.0' + implementation 'androidx.work:work-runtime:2.8.1' + implementation 'com.google.android.material:material:1.9.0' // Database - implementation 'androidx.room:room-runtime:2.4.3' - annotationProcessor 'androidx.room:room-compiler:2.4.3' + implementation "androidx.room:room-runtime:${roomVersion}" + annotationProcessor "androidx.room:room-compiler:${roomVersion}" // Retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' // Gson - implementation 'com.google.code.gson:gson:2.10' + implementation 'com.google.code.gson:gson:2.10.1' // ReactiveX implementation 'io.reactivex.rxjava2:rxjava:2.2.21' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' // Testing testImplementation 'androidx.test:core:1.5.0' - testImplementation 'androidx.arch.core:core-testing:2.1.0' + testImplementation 'androidx.arch.core:core-testing:2.2.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.9.0' - testImplementation 'org.robolectric:robolectric:4.9' + testImplementation 'org.mockito:mockito-core:5.3.1' + testImplementation 'org.robolectric:robolectric:4.10.3' implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'foundation.e:elib:0.0.1-alpha11' diff --git a/app/src/main/java/it/niedermann/owncloud/notes/FormattingHelpActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/FormattingHelpActivity.java index 91ef3724af42b6dbf808df91eee8ee131c1a6adc..50a6a968ed9a562fd3c11a6a61611d05fa5c97d4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/FormattingHelpActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/FormattingHelpActivity.java @@ -1,6 +1,7 @@ package it.niedermann.owncloud.notes; -import android.content.SharedPreferences; +import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; + import android.graphics.Typeface; import android.os.Bundle; import android.text.method.LinkMovementMethod; @@ -10,12 +11,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; -import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandedActivity; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ActivityFormattingHelpBinding; -import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; - public class FormattingHelpActivity extends BrandedActivity { private ActivityFormattingHelpBinding binding; @@ -222,7 +221,6 @@ public class FormattingHelpActivity extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); + public void applyBrand(int color) { } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/NotesApplication.java b/app/src/main/java/it/niedermann/owncloud/notes/NotesApplication.java index b8e35da5058f2fc09c5073ba77a11f55393af5c9..8543b1b4aedb8fbfc9184aeda6144e60765bf3ec 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/NotesApplication.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/NotesApplication.java @@ -1,17 +1,16 @@ package it.niedermann.owncloud.notes; +import static androidx.preference.PreferenceManager.getDefaultSharedPreferences; + import android.app.Application; import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import trikita.log.Log; +import android.webkit.WebView; import androidx.appcompat.app.AppCompatDelegate; import androidx.preference.PreferenceManager; import it.niedermann.owncloud.notes.preferences.DarkModeSetting; - -import static androidx.preference.PreferenceManager.getDefaultSharedPreferences; +import trikita.log.Log; public class NotesApplication extends Application { private static final String TAG = NotesApplication.class.getSimpleName(); @@ -31,6 +30,9 @@ public class NotesApplication extends Application { setLogLevel(); super.onCreate(); + if (BuildConfig.DEBUG) { + WebView.setWebContentsDebuggingEnabled(true); + } } public static void setAppTheme(DarkModeSetting setting) { @@ -50,19 +52,6 @@ public class NotesApplication extends Application { return DarkModeSetting.valueOf(mode); } - public static boolean isDarkThemeActive(Context context, DarkModeSetting setting) { - if (setting == DarkModeSetting.SYSTEM_DEFAULT) { - return isDarkThemeActive(context); - } else { - return setting == DarkModeSetting.DARK; - } - } - - public static boolean isDarkThemeActive(Context context) { - final int uiMode = context.getResources().getConfiguration().uiMode; - return (uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - } - public static void setLockedPreference(boolean lockedPreference) { Log.i(TAG, "New locked preference: " + lockedPreference); NotesApplication.lockedPreference = lockedPreference; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/about/AboutActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/about/AboutActivity.java index ed21d14e3f0a2eb03da9112451cb102a577ace2b..bb01ff05686acdbbf82e26c5519fb8f2c9585f9b 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/about/AboutActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/about/AboutActivity.java @@ -22,7 +22,12 @@ public class AboutActivity extends BrandedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); + public void applyBrand(int color) { + } + + @Override + public boolean onSupportNavigateUp() { + finish(); // close this activity as oppose to navigating up + return true; } } \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountpicker/AccountPickerDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/accountpicker/AccountPickerDialogFragment.java index 8caa454025a759d867b0c38301cc1b59d4163db9..12272889a6697748385d8c17e4f9ab5a2297a709 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/accountpicker/AccountPickerDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountpicker/AccountPickerDialogFragment.java @@ -109,7 +109,7 @@ public class AccountPickerDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int color) { // Nothing to do... } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java index 20c1eed4ff9e3c6da92c586ed4bea7523c4c53b2..bf74192f40ba8a70101fe6eb832c44e33dd6892b 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java @@ -1,7 +1,5 @@ package it.niedermann.owncloud.notes.accountswitcher; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLayerDrawable; - import android.app.Dialog; import android.content.Context; import android.content.Intent; @@ -18,6 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandedDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.DialogAccountSwitcherBinding; import it.niedermann.owncloud.notes.manageaccounts.ManageAccountsActivity; import it.niedermann.owncloud.notes.persistence.NotesRepository; @@ -118,7 +117,8 @@ public class AccountSwitcherDialog extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, mainColor); + public void applyBrand(int color) { + final var util = BrandingUtil.of(color, requireContext()); + util.notes.colorLayerDrawable((LayerDrawable) binding.check.getDrawable(), R.id.area, color); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java index 7ef9138dea8ec8bcfcb8c3cdff6ac80936fc8991..29a33868bd13a0ce78d7acf48bda0aaa5ab955e8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/Branded.java @@ -5,5 +5,5 @@ import androidx.annotation.UiThread; public interface Branded { @UiThread - void applyBrand(@ColorInt int mainColor, @ColorInt int textColor); + void applyBrand(@ColorInt int color); } \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java index aa91251d633e5796b492659aed2d7c5adbd2085e..113db2a92f904acc854fb7ebccaa40cd539e3709 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java @@ -1,25 +1,15 @@ package it.niedermann.owncloud.notes.branding; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; +import static it.niedermann.owncloud.notes.branding.BrandingUtil.readBrandMainColorLiveData; + import android.util.TypedValue; import android.view.Menu; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.content.ContextCompat; - -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.floatingactionbutton.FloatingActionButton; import it.niedermann.owncloud.notes.R; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.readBrandColors; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; - public abstract class BrandedActivity extends AppCompatActivity implements Branded { @ColorInt @@ -33,17 +23,17 @@ public abstract class BrandedActivity extends AppCompatActivity implements Brand getTheme().resolveAttribute(R.attr.colorAccent, typedValue, true); colorAccent = typedValue.data; - readBrandColors(this).observe(this, (pair) -> applyBrand(pair.first, pair.second)); + readBrandMainColorLiveData(this).observe(this, this::applyBrand); } @Override public boolean onCreateOptionsMenu(Menu menu) { + final var utils = BrandingUtil.of(colorAccent, this); + for (int i = 0; i < menu.size(); i++) { - tintMenuIcon(menu.getItem(i), colorAccent); + utils.platform.colorToolbarMenuIcon(this, menu.getItem(i)); } - return super.onCreateOptionsMenu(menu); - } - public void applyBrandToPrimaryToolbar(@NonNull AppBarLayout appBarLayout, @NonNull Toolbar toolbar) { + return super.onCreateOptionsMenu(menu); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java index b930c63a9d0c6e76638f8e07b9517b1063e2a69d..bf232b7da171524242e6560a4485dc9c32cf72f2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedDialogFragment.java @@ -13,8 +13,7 @@ public abstract class BrandedDialogFragment extends DialogFragment implements Br super.onStart(); @Nullable final var context = requireContext(); - @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); - @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); - applyBrand(mainColor, textColor); + @ColorInt final int color = BrandingUtil.readBrandMainColor(context); + applyBrand(color); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java index 993ee37784d8ceb4698a34e38b383e618b7b640e..ac197ef8fbc2445d7f9b9bdafa378895f9a3c523 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java @@ -1,19 +1,15 @@ package it.niedermann.owncloud.notes.branding; -import android.content.Context; import android.util.TypedValue; import android.view.Menu; import android.view.MenuInflater; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import it.niedermann.owncloud.notes.R; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; - public abstract class BrandedFragment extends Fragment implements Branded { @ColorInt @@ -32,16 +28,19 @@ public abstract class BrandedFragment extends Fragment implements Branded { context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true); colorPrimary = typedValue.data; - @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); - @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); - applyBrand(mainColor, textColor); + @ColorInt final int color = BrandingUtil.readBrandMainColor(context); + applyBrand(color); } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); + final var utils = BrandingUtil.of(colorAccent, requireContext()); + for (int i = 0; i < menu.size(); i++) { - tintMenuIcon(menu.getItem(i), colorAccent); + if (menu.getItem(i).getIcon() != null) { + utils.platform.colorToolbarMenuIcon(requireContext(), menu.getItem(i)); + } } } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java index 620ec4b669c614a2698af63d865b5f3ba8021e9d..3925bec48880589fd2611d1e663224c86f9cfba8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedPreferenceCategory.java @@ -2,15 +2,13 @@ package it.niedermann.owncloud.notes.branding; import android.content.Context; import android.util.AttributeSet; -import android.view.View; import android.widget.TextView; -import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceViewHolder; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; public class BrandedPreferenceCategory extends PreferenceCategory { @@ -36,9 +34,10 @@ public class BrandedPreferenceCategory extends PreferenceCategory { final var view = holder.itemView.findViewById(android.R.id.title); @Nullable final var context = getContext(); - if (context != null && view instanceof TextView) { - @ColorInt final int mainColor = getSecondaryForegroundColorDependingOnTheme(context, BrandingUtil.readBrandMainColor(context)); - ((TextView) view).setTextColor(mainColor); + if (view instanceof TextView) { + final var util = BrandingUtil.of(BrandingUtil.readBrandMainColor(context), context); + + util.platform.colorTextView((TextView) view, ColorRole.ON_PRIMARY_CONTAINER); } } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java index 1c0ebf11e71fb4a5b47c192153aa1ce2fcfac251..1789dc90a7c1f51306022fbbf47d3bd8a3107187 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSnackbar.java @@ -1,11 +1,7 @@ package it.niedermann.owncloud.notes.branding; -import static it.niedermann.owncloud.notes.NotesApplication.isDarkThemeActive; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.getAttribute; import static it.niedermann.owncloud.notes.branding.BrandingUtil.readBrandMainColor; -import static it.niedermann.owncloud.notes.shared.util.NotesColorUtil.contrastRatioIsSufficient; -import android.graphics.Color; import android.view.View; import androidx.annotation.ColorInt; @@ -15,26 +11,15 @@ import androidx.annotation.StringRes; import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; -import it.niedermann.owncloud.notes.R; - public class BrandedSnackbar { @NonNull public static Snackbar make(@NonNull View view, @NonNull CharSequence text, @BaseTransientBottomBar.Duration int duration) { - final var snackbar = Snackbar.make(view, text, duration); - - @ColorInt final int backgroundColor = getAttribute(view.getContext(), R.attr.colorSurfaceInverse); @ColorInt final int color = readBrandMainColor(view.getContext()); + final var snackbar = Snackbar.make(view, text, duration); + final var utils = BrandingUtil.of(color, view.getContext()); - if (contrastRatioIsSufficient(backgroundColor, color)) { - snackbar.setActionTextColor(color); - } else { - if (isDarkThemeActive(view.getContext())) { - snackbar.setActionTextColor(Color.BLACK); - } else { - snackbar.setActionTextColor(Color.WHITE); - } - } + utils.material.themeSnackbar(snackbar); return snackbar; } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java index c31ad1eeb8cbbf20d0f87f5522165b3e62b09e50..83e45b7710157b7d9c77c7c764a1ea79343f9739 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedSwitchPreference.java @@ -17,9 +17,6 @@ public class BrandedSwitchPreference extends SwitchPreference implements Branded @ColorInt private Integer mainColor = null; - @ColorInt - private Integer textColor = null; - @SuppressLint("UseSwitchCompatOrMaterialCode") @Nullable private Switch switchView; @@ -50,9 +47,8 @@ public class BrandedSwitchPreference extends SwitchPreference implements Branded } @Override - public void applyBrand(@ColorInt int mainColor, @ColorInt int textColor) { - this.mainColor = mainColor; - this.textColor = textColor; + public void applyBrand(@ColorInt int color) { + this.mainColor = color; // onBindViewHolder is called after applyBrand, therefore we have to store the given values and apply them later. } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java index 27e55b7dd6d3cf9a3cbae59a4c1b25f6bd487eb5..16df3e157a94df30f4215feca502712b60fe1bed 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java @@ -1,84 +1,64 @@ package it.niedermann.owncloud.notes.branding; -import static it.niedermann.owncloud.notes.NotesApplication.isDarkThemeActive; -import static it.niedermann.owncloud.notes.shared.util.NotesColorUtil.contrastRatioIsSufficient; -import static it.niedermann.owncloud.notes.shared.util.NotesColorUtil.contrastRatioIsSufficientBigAreas; - import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.LayerDrawable; -import trikita.log.Log; -import android.util.TypedValue; -import android.view.MenuItem; -import android.widget.EditText; -import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; -import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.core.util.Pair; import androidx.lifecycle.LiveData; -import androidx.lifecycle.MediatorLiveData; import androidx.preference.PreferenceManager; -import com.google.android.material.textfield.TextInputLayout; +import com.nextcloud.android.common.ui.theme.MaterialSchemes; +import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase; +import com.nextcloud.android.common.ui.theme.utils.AndroidViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.AndroidXViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.DialogViewThemeUtils; +import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import it.niedermann.android.sharedpreferences.SharedPreferenceIntLiveData; -import it.niedermann.android.util.ColorUtil; -import it.niedermann.owncloud.notes.NotesApplication; import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.shared.util.NotesColorUtil; +import trikita.log.Log; -public class BrandingUtil { +public class BrandingUtil extends ViewThemeUtilsBase { private static final String TAG = BrandingUtil.class.getSimpleName(); + private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); private static final String pref_key_branding_main = "branding_main"; - private static final String pref_key_branding_text = "branding_text"; - - private BrandingUtil() { + public final AndroidViewThemeUtils platform; + public final MaterialViewThemeUtils material; + public final AndroidXViewThemeUtils androidx; + public final DialogViewThemeUtils dialog; + public final NotesViewThemeUtils notes; + + private BrandingUtil( + final MaterialSchemes schemes, + final com.nextcloud.android.common.ui.color.ColorUtil colorUtil + ) { + super(schemes); + + this.platform = new AndroidViewThemeUtils(schemes, colorUtil); + this.material = new MaterialViewThemeUtils(schemes, colorUtil); + this.androidx = new AndroidXViewThemeUtils(schemes, this.platform); + this.dialog = new DialogViewThemeUtils(schemes); + this.notes = new NotesViewThemeUtils(schemes); } - public static LiveData> readBrandColors(@NonNull Context context) { - return new BrandingLiveData(context); - } - - private static class BrandingLiveData extends MediatorLiveData> { - @ColorInt - Integer lastMainColor = null; - @ColorInt - Integer lastTextColor = null; - - public BrandingLiveData(@NonNull Context context) { - addSource(readBrandMainColorLiveData(context), (nextMainColor) -> { - lastMainColor = nextMainColor; - if (lastTextColor != null) { - postValue(new Pair<>(lastMainColor, lastTextColor)); - } - }); - addSource(readBrandTextColorLiveData(context), (nextTextColor) -> { - lastTextColor = nextTextColor; - if (lastMainColor != null) { - postValue(new Pair<>(lastMainColor, lastTextColor)); - } - }); - } + public static BrandingUtil of(@ColorInt int color, @NonNull Context context) { + return CACHE.computeIfAbsent(color, c -> new BrandingUtil( + MaterialSchemes.Companion.fromColor(c), + new com.nextcloud.android.common.ui.color.ColorUtil(context) + )); } public static LiveData readBrandMainColorLiveData(@NonNull Context context) { final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); Log.v(TAG, "--- Read: shared_preference_theme_main"); - return new SharedPreferenceIntLiveData(sharedPreferences, pref_key_branding_main, context.getApplicationContext().getResources().getColor(R.color.defaultBrand)); - } - - public static LiveData readBrandTextColorLiveData(@NonNull Context context) { - final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - Log.v(TAG, "--- Read: shared_preference_theme_text"); - return new SharedPreferenceIntLiveData(sharedPreferences, pref_key_branding_text, Color.WHITE); + return new SharedPreferenceIntLiveData(sharedPreferences, pref_key_branding_main, ContextCompat.getColor(context, R.color.defaultBrand)); } @ColorInt @@ -86,122 +66,17 @@ public class BrandingUtil { return ContextCompat.getColor(context, R.color.defaultBrand); } - @ColorInt - public static int readBrandTextColor(@NonNull Context context) { - final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - Log.v(TAG, "--- Read: shared_preference_theme_text"); - return sharedPreferences.getInt(pref_key_branding_text, Color.WHITE); - } - - public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor, @ColorInt int textColor) { + public static void saveBrandColor(@NonNull Context context, @ColorInt int color) { final int previousMainColor = readBrandMainColor(context); - final int previousTextColor = readBrandTextColor(context); final var editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - Log.v(TAG, "--- Write: shared_preference_theme_main" + " | " + mainColor); - Log.v(TAG, "--- Write: shared_preference_theme_text" + " | " + textColor); - editor.putInt(pref_key_branding_main, mainColor); - editor.putInt(pref_key_branding_text, textColor); + Log.v(TAG, "--- Write: shared_preference_theme_main" + " | " + color); + editor.putInt(pref_key_branding_main, color); editor.apply(); if (context instanceof BrandedActivity) { - if (mainColor != previousMainColor || textColor != previousTextColor) { + if (color != previousMainColor) { final var activity = (BrandedActivity) context; activity.runOnUiThread(() -> ActivityCompat.recreate(activity)); } } } - - /** - * Since we may collide with dark theme in this area, we have to make sure that the color is visible depending on the background - */ - @ColorInt - public static int getSecondaryForegroundColorDependingOnTheme(@NonNull Context context, @ColorInt int mainColor) { - final int primaryColor = ContextCompat.getColor(context, R.color.primary); - final boolean isDarkTheme = NotesApplication.isDarkThemeActive(context); - if (isDarkTheme && !contrastRatioIsSufficient(mainColor, primaryColor)) { - Log.v(TAG, "Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and dark theme is too low. Falling back to WHITE as brand color."); - return Color.WHITE; - } else if (!isDarkTheme && !contrastRatioIsSufficient(mainColor, primaryColor)) { - Log.v(TAG, "Contrast ratio between brand color " + String.format("#%06X", (0xFFFFFF & mainColor)) + " and light theme is too low. Falling back to BLACK as brand color."); - return Color.BLACK; - } else { - return mainColor; - } - } - - public static void applyBrandToEditText(@ColorInt int mainColor, @ColorInt int textColor, @NonNull EditText editText) { - @ColorInt final int finalMainColor = getSecondaryForegroundColorDependingOnTheme(editText.getContext(), mainColor); - DrawableCompat.setTintList(editText.getBackground(), new ColorStateList( - new int[][]{ - new int[]{android.R.attr.state_active}, - new int[]{android.R.attr.state_activated}, - new int[]{android.R.attr.state_focused}, - new int[]{android.R.attr.state_pressed}, - new int[]{} - }, - new int[]{ - finalMainColor, - finalMainColor, - finalMainColor, - finalMainColor, - editText.getContext().getResources().getColor(R.color.fg_default_low) - } - )); - } - - public static void applyBrandToEditTextInputLayout(@ColorInt int color, @NonNull TextInputLayout til) { - final int colorPrimary = ContextCompat.getColor(til.getContext(), R.color.primary); - final int colorAccent = ContextCompat.getColor(til.getContext(), R.color.accent); - final var colorDanger = ColorStateList.valueOf(ContextCompat.getColor(til.getContext(), R.color.danger)); - til.setBoxStrokeColor(contrastRatioIsSufficientBigAreas(color, colorPrimary) ? color : colorAccent); - til.setHintTextColor(ColorStateList.valueOf(contrastRatioIsSufficient(color, colorPrimary) ? color : colorAccent)); - til.setErrorTextColor(colorDanger); - til.setBoxStrokeErrorColor(colorDanger); - til.setErrorIconTintList(colorDanger); - } - - public static void tintMenuIcon(@NonNull MenuItem menuItem, @ColorInt int color) { - } - - public static void applyBrandToLayerDrawable(@NonNull LayerDrawable check, @IdRes int areaToColor, @ColorInt int mainColor) { - } - - @ColorInt - public static int getAttribute(@NonNull Context context, @AttrRes int id) { - final var typedValue = new TypedValue(); - context.getTheme().resolveAttribute(id, typedValue, true); - return typedValue.data; - } - - @ColorInt - public static int getTextHighlightBackgroundColor(@NonNull Context context, @ColorInt int mainColor, @ColorInt int colorPrimary, @ColorInt int colorAccent) { - if (isDarkThemeActive(context)) { // Dark background - if (ColorUtil.INSTANCE.isColorDark(mainColor)) { // Dark brand color - if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text - return mainColor; - } else { - return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); - } - } else { // Light brand color - if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text - return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); - } else { - return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); - } - } - } else { // Light background - if (ColorUtil.INSTANCE.isColorDark(mainColor)) { // Dark brand color - if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text - return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); - } else { - return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); - } - } else { // Light brand color - if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text - return mainColor; - } else { - return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); - } - } - } - } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/NotesViewThemeUtils.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/NotesViewThemeUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d3970b338a81752c50ea2a323ad6c2f33b651e7f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/NotesViewThemeUtils.java @@ -0,0 +1,202 @@ +package it.niedermann.owncloud.notes.branding; + +import static com.nextcloud.android.common.ui.util.ColorStateListUtilsKt.buildColorStateList; +import static com.nextcloud.android.common.ui.util.PlatformThemeUtil.isDarkMode; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.LayerDrawable; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.SearchView; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.drawable.DrawableCompat; + +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.MaterialToolbar; +import com.google.android.material.card.MaterialCardView; +import com.nextcloud.android.common.ui.theme.MaterialSchemes; +import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase; +import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils; + +import it.niedermann.android.util.ColorUtil; +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.main.navigation.NavigationItem; +import it.niedermann.owncloud.notes.shared.util.NotesColorUtil; +import kotlin.Pair; + +public class NotesViewThemeUtils extends ViewThemeUtilsBase { + + private static final String TAG = NotesViewThemeUtils.class.getSimpleName(); + + public NotesViewThemeUtils(@NonNull MaterialSchemes schemes) { + super(schemes); + } + + /** + * The Notes app uses custom navigation view items because they have several features which are + * not covered by {@link NavigationItem}. + */ + public void colorNavigationViewItem(@NonNull View view) { + withScheme(view, scheme -> { + view.setBackgroundTintList(buildColorStateList( + new Pair<>(android.R.attr.state_selected, scheme.getSecondaryContainer()), + new Pair<>(-android.R.attr.state_selected, Color.TRANSPARENT) + )); + return view; + }); + } + + /** + * The Notes app uses custom navigation view items because they have several features which are + * not covered by {@link NavigationItem}. + */ + public void colorNavigationViewItemIcon(@NonNull ImageView view) { + withScheme(view, scheme -> { + view.setImageTintList(buildColorStateList( + new Pair<>(android.R.attr.state_selected, scheme.getOnSecondaryContainer()), + new Pair<>(-android.R.attr.state_selected, scheme.getOnSurfaceVariant()) + )); + return view; + }); + } + + /** + * The Notes app uses custom navigation view items because they have several features which are + * not covered by {@link NavigationItem}. + */ + public void colorNavigationViewItemText(@NonNull TextView view) { + withScheme(view, scheme -> { + view.setTextColor(buildColorStateList( + new Pair<>(android.R.attr.state_selected, scheme.getOnSecondaryContainer()), + new Pair<>(-android.R.attr.state_selected, scheme.getOnSurfaceVariant()) + )); + return view; + }); + } + + /** + * @deprecated should be replaced by {@link MaterialViewThemeUtils#themeToolbar(MaterialToolbar)}. + */ + @Deprecated(forRemoval = true) + public void applyBrandToPrimaryToolbar(@NonNull AppBarLayout appBarLayout, + @NonNull Toolbar toolbar, + @ColorInt int color) { + // FIXME Workaround for https://github.com/nextcloud/notes-android/issues/889 + appBarLayout.setBackgroundColor(ContextCompat.getColor(appBarLayout.getContext(), R.color.primary)); + + final var overflowDrawable = toolbar.getOverflowIcon(); + if (overflowDrawable != null) { + overflowDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); + toolbar.setOverflowIcon(overflowDrawable); + } + + final var navigationDrawable = toolbar.getNavigationIcon(); + if (navigationDrawable != null) { + navigationDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); + toolbar.setNavigationIcon(navigationDrawable); + } + } + + /** + * Colorizes only a specific part of a drawable + */ + public void colorLayerDrawable(@NonNull LayerDrawable check, @IdRes int areaToColor, @ColorInt int mainColor) { + final var drawable = check.findDrawableByLayerId(areaToColor); + if (drawable == null) { + Log.e(TAG, "Could not find areaToColor (" + areaToColor + "). Cannot apply brand."); + } else { + DrawableCompat.setTint(drawable, mainColor); + } + } + + @ColorInt + public int getTextHighlightBackgroundColor(@NonNull Context context, + @ColorInt int mainColor, + @ColorInt int colorPrimary, + @ColorInt int colorAccent) { + if (isDarkMode(context)) { // Dark background + if (ColorUtil.INSTANCE.isColorDark(mainColor)) { // Dark brand color + if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text + return mainColor; + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } else { // Light brand color + if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text + return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } + } else { // Light background + if (ColorUtil.INSTANCE.isColorDark(mainColor)) { // Dark brand color + if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorAccent)) { // But also dark text + return Color.argb(77, Color.red(mainColor), Color.green(mainColor), Color.blue(mainColor)); + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } else { // Light brand color + if (NotesColorUtil.contrastRatioIsSufficient(mainColor, colorPrimary)) { // But also dark text + return mainColor; + } else { + return ContextCompat.getColor(context, R.color.defaultTextHighlightBackground); + } + } + } + } + + /** + * @deprecated Should be replaced with {@link com.google.android.material.search.SearchBar} component. + */ + @Deprecated + public void themeSearchCardView(@NonNull MaterialCardView searchBarWrapper) { + withScheme(searchBarWrapper, scheme -> { + searchBarWrapper.setBackgroundTintList(ColorStateList.valueOf(scheme.getSurface())); + return searchBarWrapper; + }); + } + + /** + * @deprecated Should be replaced with {@link com.google.android.material.search.SearchBar} or + * {@link MaterialViewThemeUtils#themeToolbar(MaterialToolbar)} + */ + @Deprecated + public void themeSearchToolbar(@NonNull MaterialToolbar toolbar) { + withScheme(toolbar, scheme -> { + toolbar.setNavigationIconTint(scheme.getOnSurface()); + toolbar.setTitleTextColor(scheme.getOnSurface()); + return toolbar; + }); + } + + /** + * @deprecated Should be replaced with {@link com.google.android.material.search.SearchView} + * @see com.nextcloud.android.common.ui.theme.utils.AndroidXViewThemeUtils#themeToolbarSearchView(SearchView) + */ + @Deprecated + public void themeToolbarSearchView(@NonNull SearchView searchView) { + withScheme(searchView, scheme -> { + // hacky as no default way is provided + final var editText = (SearchView.SearchAutoComplete) searchView + .findViewById(androidx.appcompat.R.id.search_src_text); + final var closeButton = (ImageView) searchView.findViewById(androidx.appcompat.R.id.search_close_btn); + final var searchButton = (ImageView) searchView.findViewById(androidx.appcompat.R.id.search_button); + editText.setHintTextColor(scheme.getOnSurfaceVariant()); + editText.setHighlightColor(scheme.getInverseOnSurface()); + editText.setTextColor(scheme.getOnSurface()); + closeButton.setColorFilter(scheme.getOnSurface()); + searchButton.setColorFilter(scheme.getOnSurface()); + return searchView; + }); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java index 0fced7893d374a51c889bf22698d77c81730b34b..85a24a191da934777b3ee316d00f087849241b40 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java @@ -1,8 +1,6 @@ package it.niedermann.owncloud.notes.edit; import static java.lang.Boolean.TRUE; -import static it.niedermann.owncloud.notes.NotesApplication.isDarkThemeActive; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon; import static it.niedermann.owncloud.notes.edit.EditNoteActivity.ACTION_SHORTCUT; import static it.niedermann.owncloud.notes.shared.util.WidgetUtil.pendingIntentFlagCompat; @@ -37,6 +35,7 @@ import java.util.concurrent.Executors; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.accountpicker.AccountPickerDialogFragment; import it.niedermann.owncloud.notes.branding.BrandedFragment; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.edit.category.CategoryDialogFragment; import it.niedermann.owncloud.notes.edit.category.CategoryDialogFragment.CategoryDialogListener; import it.niedermann.owncloud.notes.edit.title.EditTitleDialogFragment; @@ -72,7 +71,8 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego private Note originalNote; private int originalScrollY; protected NotesRepository repo; - private NoteFragmentListener listener; + @Nullable + protected NoteFragmentListener listener; private boolean titleModified = false; protected boolean isNew = true; @@ -144,6 +144,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego @Nullable protected abstract ScrollView getScrollView(); + protected abstract void scrollToY(int scrollY); @Override @@ -198,7 +199,9 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego private void prepareFavoriteOption(MenuItem item) { item.setIcon(note.getFavorite() ? R.drawable.ic_star_golden_24dp : R.drawable.ic_star_border_24dp); item.setChecked(note.getFavorite()); - tintMenuIcon(item, colorAccent); + + final var utils = BrandingUtil.of(colorAccent, requireContext()); + utils.platform.colorToolbarMenuIcon(requireContext(), item); } /** @@ -239,7 +242,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego .show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName())); return true; } else if (itemId == R.id.menu_share) { - ShareUtil.openShareDialog(requireContext(), note.getTitle(), note.getContent()); + shareNote(); return false; } else if (itemId == MENU_ID_PIN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -262,6 +265,10 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego return super.onOptionsItemSelected(item); } + protected void shareNote() { + ShareUtil.openShareDialog(requireContext(), note.getTitle(), note.getContent()); + } + @CallSuper protected void onNoteLoaded(Note note) { this.originalScrollY = note.getScrollY(); @@ -272,10 +279,21 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego if (scrollY > 0) { note.setScrollY(scrollY); } + onScroll(scrollY, oldScrollY); }); } } + /** + * Scroll callback, to be overridden by subclasses. Default implementation is empty + */ + protected void onScroll(int scrollY, int oldScrollY) { + } + + protected boolean shouldShowToolbar() { + return true; + } + public void onCloseNote() { if (!titleModified && originalNote == null && getContent().isEmpty()) { repo.deleteNoteAndSync(localAccount, note.getId()); @@ -366,8 +384,14 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego } public interface NoteFragmentListener { + enum Mode { + EDIT, PREVIEW, DIRECT_EDIT + } + void close(); void onNoteUpdated(Note note); + + void changeMode(@NonNull Mode mode, boolean reloadNote); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/EditNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/EditNoteActivity.java index 3b0a88166e4d5f4bfbef8a15584af255253018ec..aa4849746164100532f33b7bb17665b63cf6ee45 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/EditNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/EditNoteActivity.java @@ -8,10 +8,12 @@ import android.text.TextUtils; import trikita.log.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.WindowManager; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.preference.PreferenceManager; @@ -19,6 +21,7 @@ import androidx.preference.PreferenceManager; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.nextcloud.android.sso.model.SingleSignOnAccount; import java.io.BufferedReader; import java.io.IOException; @@ -30,9 +33,11 @@ import it.niedermann.android.sharedpreferences.SharedPreferenceBooleanLiveData; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.accountpicker.AccountPickerListener; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ActivityEditBinding; import it.niedermann.owncloud.notes.edit.category.CategoryViewModel; import it.niedermann.owncloud.notes.main.MainActivity; +import it.niedermann.owncloud.notes.persistence.NotesRepository; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.model.NavigationCategory; @@ -56,11 +61,14 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment private ActivityEditBinding binding; private BaseNoteFragment fragment; + private NotesRepository repo; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + repo = NotesRepository.getInstance(getApplicationContext()); + try { if (SingleAccountHelper.getCurrentSingleSignOnAccount(this) == null) { throw new NoCurrentAccountSelectedException(); @@ -117,9 +125,20 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment } private long getAccountId() { - return getIntent().getLongExtra(PARAM_ACCOUNT_ID, 0); + final long idParam = getIntent().getLongExtra(PARAM_ACCOUNT_ID, 0); + if (idParam == 0) { + try { + final SingleSignOnAccount ssoAcc = SingleAccountHelper.getCurrentSingleSignOnAccount(this); + return repo.getAccountByName(ssoAcc.name).getId(); + } catch (NextcloudFilesAppAccountNotFoundException | + NoCurrentAccountSelectedException e) { + Log.w(TAG, "getAccountId: no current account", e); + } + } + return idParam; } + /** * Starts the note fragment for an existing note or a new note. * The actual behavior is triggered by the activity's intent. @@ -144,44 +163,109 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment * @param noteId ID of the existing note. */ private void launchExistingNote(long accountId, long noteId) { - final var prefKeyNoteMode = getString(R.string.pref_key_note_mode); - final var prefKeyLastMode = getString(R.string.pref_key_last_note_mode); - final var prefValueEdit = getString(R.string.pref_value_mode_edit); - final var prefValuePreview = getString(R.string.pref_value_mode_preview); - final var prefValueLast = getString(R.string.pref_value_mode_last); + launchExistingNote(accountId, noteId, null); + } - final var preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - final String mode = preferences.getString(prefKeyNoteMode, prefValueEdit); - final String lastMode = preferences.getString(prefKeyLastMode, prefValueEdit); - boolean editMode = true; - if (prefValuePreview.equals(mode) || (prefValueLast.equals(mode) && prefValuePreview.equals(lastMode))) { - editMode = false; - } - launchExistingNote(accountId, noteId, editMode); + private void launchExistingNote(long accountId, long noteId, @Nullable final String mode) { + launchExistingNote(accountId, noteId, mode, false); } /** * Starts a {@link NoteEditFragment} or {@link NotePreviewFragment} for an existing note. * - * @param noteId ID of the existing note. - * @param edit View-mode of the fragment: - * true for {@link NoteEditFragment}, - * false for {@link NotePreviewFragment}. + * @param noteId ID of the existing note. + * @param mode View-mode of the fragment (pref value or null). If null will be chosen based on + * user preferences. + * @param discardState If true, the state of the fragment will be discarded and a new fragment will be created */ - private void launchExistingNote(long accountId, long noteId, boolean edit) { + private void launchExistingNote(long accountId, long noteId, @Nullable final String mode, final boolean discardState) { // save state of the fragment in order to resume with the same note and originalNote - Fragment.SavedState savedState = null; - if (fragment != null) { - savedState = getSupportFragmentManager().saveFragmentInstanceState(fragment); + runOnUiThread(() -> { + Fragment.SavedState savedState = null; + if (fragment != null && !discardState) { + savedState = getSupportFragmentManager().saveFragmentInstanceState(fragment); + } + fragment = getNoteFragment(accountId, noteId, mode); + if (savedState != null) { + fragment.setInitialSavedState(savedState); + } + replaceFragment(); + }); + } + + private void replaceFragment() { + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); + if (!fragment.shouldShowToolbar()) { + binding.toolbar.setVisibility(View.GONE); + } else { + binding.toolbar.setVisibility(View.VISIBLE); } - fragment = edit - ? NoteEditFragment.newInstance(accountId, noteId) - : NotePreviewFragment.newInstance(accountId, noteId); + } + + + /** + * Returns the preferred mode for the account. If the mode is "remember last" the last mode is returned. + * If the mode is "direct edit" and the account does not support direct edit, the default mode is returned. + */ + private String getPreferenceMode(long accountId) { + + final var prefKeyNoteMode = getString(R.string.pref_key_note_mode); + final var prefKeyLastMode = getString(R.string.pref_key_last_note_mode); + final var defaultMode = getString(R.string.pref_value_mode_edit); + final var prefValueLast = getString(R.string.pref_value_mode_last); + final var prefValueDirectEdit = getString(R.string.pref_value_mode_direct_edit); - if (savedState != null) { - fragment.setInitialSavedState(savedState); + + final var preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + final String modePreference = preferences.getString(prefKeyNoteMode, defaultMode); + + String effectiveMode = modePreference; + if (modePreference.equals(prefValueLast)) { + effectiveMode = preferences.getString(prefKeyLastMode, defaultMode); + } + + if (effectiveMode.equals(prefValueDirectEdit)) { + final Account accountById = repo.getAccountById(accountId); + final var directEditAvailable = accountById != null && accountById.isDirectEditingAvailable(); + if (!directEditAvailable) { + effectiveMode = defaultMode; + } + } + + return effectiveMode; + } + + private BaseNoteFragment getNoteFragment(long accountId, long noteId, final @Nullable String modePref) { + + final var effectiveMode = modePref == null ? getPreferenceMode(accountId) : modePref; + + final var prefValueEdit = getString(R.string.pref_value_mode_edit); + final var prefValueDirectEdit = getString(R.string.pref_value_mode_direct_edit); + final var prefValuePreview = getString(R.string.pref_value_mode_preview); + + if (effectiveMode.equals(prefValueEdit)) { + return NoteEditFragment.newInstance(accountId, noteId); + } else if (effectiveMode.equals(prefValueDirectEdit)) { + return NoteDirectEditFragment.newInstance(accountId, noteId); + } else if (effectiveMode.equals(prefValuePreview)) { + return NotePreviewFragment.newInstance(accountId, noteId); + } else { + throw new IllegalStateException("Unknown note modePref: " + modePref); + } + } + + + @NonNull + private BaseNoteFragment getNewNoteFragment(Note newNote) { + final var mode = getPreferenceMode(getAccountId()); + + final var prefValueDirectEdit = getString(R.string.pref_value_mode_direct_edit); + + if (mode.equals(prefValueDirectEdit)) { + return NoteDirectEditFragment.newInstanceWithNewNote(newNote); + } else { + return NoteEditFragment.newInstanceWithNewNote(newNote); } - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); } /** @@ -218,10 +302,11 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment content = ""; } final var newNote = new Note(null, Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(content, this), content, categoryTitle, favorite, null); - fragment = NoteEditFragment.newInstanceWithNewNote(newNote); - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); + fragment = getNewNoteFragment(newNote); + replaceFragment(); } + private void launchReadonlyNote() { final var intent = getIntent(); final var content = new StringBuilder(); @@ -237,7 +322,7 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment } fragment = NoteReadonlyFragment.newInstance(content.toString()); - getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit(); + replaceFragment(); } @Override @@ -259,10 +344,10 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment close(); return true; } else if (itemId == R.id.menu_preview) { - launchExistingNote(getAccountId(), getNoteId(), false); + changeMode(Mode.PREVIEW, false); return true; } else if (itemId == R.id.menu_edit) { - launchExistingNote(getAccountId(), getNoteId(), true); + changeMode(Mode.EDIT, false); return true; } return super.onOptionsItemSelected(item); @@ -280,8 +365,10 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment final String prefKeyLastMode = getString(R.string.pref_key_last_note_mode); if (fragment instanceof NoteEditFragment) { preferences.edit().putString(prefKeyLastMode, getString(R.string.pref_value_mode_edit)).apply(); - } else { + } else if (fragment instanceof NotePreviewFragment) { preferences.edit().putString(prefKeyLastMode, getString(R.string.pref_value_mode_preview)).apply(); + } else if (fragment instanceof NoteDirectEditFragment) { + preferences.edit().putString(prefKeyLastMode, getString(R.string.pref_value_mode_direct_edit)).apply(); } fragment.onCloseNote(); @@ -307,13 +394,30 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment } } + @Override + public void changeMode(@NonNull Mode mode, boolean reloadNote) { + switch (mode) { + case EDIT: + launchExistingNote(getAccountId(), getNoteId(), getString(R.string.pref_value_mode_edit), reloadNote); + break; + case PREVIEW: + launchExistingNote(getAccountId(), getNoteId(), getString(R.string.pref_value_mode_preview), reloadNote); + break; + case DIRECT_EDIT: + launchExistingNote(getAccountId(), getNoteId(), getString(R.string.pref_value_mode_direct_edit), reloadNote); + break; + default: + throw new IllegalStateException("Unknown mode: " + mode); + } + } + + @Override public void onAccountPicked(@NonNull Account account) { fragment.moveNote(account); } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); + public void applyBrand(int color) { } -} \ No newline at end of file +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..0c9b9772ff0284d6c15b6864ad2fd2ef0e0e9e9f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt @@ -0,0 +1,401 @@ +package it.niedermann.owncloud.notes.edit + +import android.annotation.SuppressLint +import android.net.http.SslError +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.webkit.JavascriptInterface +import android.webkit.SslErrorHandler +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient +import android.widget.ScrollView +import androidx.core.view.isVisible +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.android.sso.helper.SingleAccountHelper +import com.nextcloud.android.sso.model.SingleSignOnAccount +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import it.niedermann.owncloud.notes.BuildConfig +import it.niedermann.owncloud.notes.R +import it.niedermann.owncloud.notes.branding.Branded +import it.niedermann.owncloud.notes.branding.BrandedSnackbar +import it.niedermann.owncloud.notes.branding.BrandingUtil +import it.niedermann.owncloud.notes.databinding.FragmentNoteDirectEditBinding +import it.niedermann.owncloud.notes.persistence.ApiProvider +import it.niedermann.owncloud.notes.persistence.DirectEditingRepository +import it.niedermann.owncloud.notes.persistence.entity.Note +import it.niedermann.owncloud.notes.persistence.sync.NotesAPI +import it.niedermann.owncloud.notes.shared.model.ApiVersion +import it.niedermann.owncloud.notes.shared.model.ISyncCallback +import it.niedermann.owncloud.notes.shared.util.ExtendedFabUtil +import it.niedermann.owncloud.notes.shared.util.rx.DisposableSet +import java.util.concurrent.TimeUnit + +class NoteDirectEditFragment : BaseNoteFragment(), Branded { + private var _binding: FragmentNoteDirectEditBinding? = null + private val binding: FragmentNoteDirectEditBinding + get() = _binding!! + + private val disposables: DisposableSet = DisposableSet() + private var switchToEditPending = false + + val account: SingleSignOnAccount by lazy { + SingleAccountHelper.getCurrentSingleSignOnAccount( + requireContext(), + ) + } + + val notesApi: NotesAPI by lazy { + ApiProvider.getInstance().getNotesAPI(requireContext(), account, ApiVersion.API_VERSION_1_0) + } + + // for hiding / showing the fab + private var scrollStart: Int = 0 + + public override fun getScrollView(): ScrollView? { + return null + } + + override fun scrollToY(y: Int) { + // do nothing + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + Log.d(TAG, "onCreateView() called") + _binding = FragmentNoteDirectEditBinding.inflate(inflater, container, false) + setupFab() + prepareWebView() + return binding.root + } + + @SuppressLint("ClickableViewAccessibility") // touch listener only for UI purposes, no need to handle click + private fun setupFab() { + binding.plainEditingFab.isExtended = false + ExtendedFabUtil.toggleExtendedOnLongClick(binding.plainEditingFab) + // manually detect scroll as we can't get it from the webview (maybe with custom JS?) + binding.noteWebview.setOnTouchListener { _, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + scrollStart = event.y.toInt() + } + MotionEvent.ACTION_UP -> { + val scrollEnd = event.y.toInt() + ExtendedFabUtil.toggleVisibilityOnScroll( + binding.plainEditingFab, + scrollStart, + scrollEnd, + ) + } + } + return@setOnTouchListener false + } + binding.plainEditingFab.setOnClickListener { switchToPlainEdit() } + } + + private fun switchToPlainEdit() { + switchToEditPending = true + binding.noteWebview.evaluateJavascript(JS_CLOSE) { result -> + val resultWithoutQuotes = result.replace("\"", "") + if (resultWithoutQuotes != JS_RESULT_OK) { + Log.w(TAG, "Closing via JS failed: $resultWithoutQuotes") + changeToEditMode() + } + // if result is OK, switch will be handled by JS interface callback + } + } + + override fun onDestroyView() { + super.onDestroyView() + disposables.dispose() + binding.noteWebview.destroy() + _binding = null + } + + override fun onResume() { + super.onResume() + val timeoutDisposable = Single.just(Unit) + .delay(LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .map { + if (!binding.noteWebview.isVisible) { + Log.w(TAG, "Editor not loaded after $LOAD_TIMEOUT_SECONDS seconds") + handleLoadError() + } + }.subscribe() + disposables.add(timeoutDisposable) + } + + override fun onNoteLoaded(note: Note) { + super.onNoteLoaded(note) + Log.d(TAG, "onNoteLoaded() called") + val newNoteParam = arguments?.getSerializable(PARAM_NEWNOTE) as Note? + if (newNoteParam != null || note.remoteId == null) { + createAndLoadNote(note) + } else { + loadNoteInWebView(note) + } + } + + private fun createAndLoadNote(newNote: Note) { + Log.d(TAG, "createAndLoadNote() called") + val noteCreateDisposable = Single + .fromCallable { + notesApi.createNote(newNote).execute().body()!! + } + .map { createdNote -> + repo.updateRemoteId(newNote.id, createdNote.remoteId) + repo.getNoteById(newNote.id) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ createdNote -> + loadNoteInWebView(createdNote) + }, { throwable -> + note = null + handleLoadError() + Log.e(TAG, "createAndLoadNote:", throwable) + }) + disposables.add(noteCreateDisposable) + } + + private fun loadNoteInWebView(note: Note) { + Log.d(TAG, "loadNoteInWebView() called") + val directEditingRepository = + DirectEditingRepository.getInstance(requireContext().applicationContext) + val urlDisposable = directEditingRepository.getDirectEditingUrl(account, note) + .observeOn(AndroidSchedulers.mainThread()).subscribe({ url -> + if (BuildConfig.DEBUG) { + Log.d(TAG, "loadNoteInWebView: url = $url") + } + binding.noteWebview.loadUrl(url) + }, { throwable -> + handleLoadError() + Log.e(TAG, "loadNoteInWebView:", throwable) + }) + disposables.add(urlDisposable) + } + + private fun handleLoadError() { + val snackbar = BrandedSnackbar.make( + binding.plainEditingFab, + getString(R.string.direct_editing_error), + Snackbar.LENGTH_INDEFINITE, + ) + if (note != null) { + snackbar.setAction(R.string.switch_to_plain_editing) { + changeToEditMode() + } + } else { + snackbar.setAction(R.string.action_back) { + close() + } + } + snackbar.show() + } + + override fun shouldShowToolbar(): Boolean = false + + @SuppressLint("SetJavaScriptEnabled") + private fun prepareWebView() { + val webSettings = binding.noteWebview.settings + // enable zoom + webSettings.setSupportZoom(true) + webSettings.builtInZoomControls = true + webSettings.displayZoomControls = false + + // Non-responsive webs are zoomed out when loaded + webSettings.useWideViewPort = true + webSettings.loadWithOverviewMode = true + + // user agent + val userAgent = + getString(R.string.user_agent, getString(R.string.app_name), BuildConfig.VERSION_NAME) + webSettings.userAgentString = userAgent + + // no private data storing + webSettings.savePassword = false + webSettings.saveFormData = false + + // disable local file access + webSettings.allowFileAccess = false + + // enable javascript + webSettings.javaScriptEnabled = true + webSettings.domStorageEnabled = true + + if (BuildConfig.DEBUG) { + // caching disabled in debug mode + binding.noteWebview.settings.cacheMode = WebSettings.LOAD_NO_CACHE + } + + binding.noteWebview.addJavascriptInterface( + DirectEditingMobileInterface(this), + JS_INTERFACE_NAME, + ) + + binding.noteWebview.webViewClient = object : WebViewClient() { + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError?, + ) { + super.onReceivedError(view, request, error) + if (request?.isForMainFrame == true) { + handleLoadError() + } + } + + @SuppressLint("WebViewClientOnReceivedSslError") // only for debug mode + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError?, + ) { + if (BuildConfig.DEBUG) { + handler?.proceed() + } else { + super.onReceivedSslError(view, handler, error) + } + } + } + } + + /** + * Gets the current content of the EditText field in the UI. + * + * @return String of the current content. + */ + override fun getContent(): String { + // no way to get content from webview + return "" + } + + override fun saveNote(callback: ISyncCallback?) { + val acc = repo.getAccountByName(account.name) + repo.scheduleSync(acc, false) + } + + override fun onCloseNote() { + saveNote(null) + } + + override fun applyBrand(color: Int) { + val util = BrandingUtil.of(color, requireContext()) + util.material.themeExtendedFAB(binding.plainEditingFab) + util.platform.colorCircularProgressBar(binding.progress, ColorRole.PRIMARY) + } + + private class DirectEditingMobileInterface(val noteDirectEditFragment: NoteDirectEditFragment) { + @JavascriptInterface + fun close() { + noteDirectEditFragment.close() + } + + @JavascriptInterface + fun share() { + noteDirectEditFragment.share() + } + + @JavascriptInterface + fun loaded() { + noteDirectEditFragment.onLoaded() + } + } + + private fun close() { + if (switchToEditPending) { + Log.d(TAG, "close: switching to plain edit") + changeToEditMode() + } else { + Log.d(TAG, "close: closing") + listener?.close() + } + } + + private fun changeToEditMode() { + toggleLoadingUI(true) + val updateDisposable = Single.just(note.remoteId) + .map { remoteId -> + val newNote = notesApi.getNote(remoteId).singleOrError().blockingGet().response + val localAccount = repo.getAccountByName(account.name) + repo.updateNoteAndSync(localAccount, note, newNote.content, newNote.title, null) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + listener?.changeMode(NoteFragmentListener.Mode.EDIT, true) + }, { throwable -> + Log.e(TAG, "changeToEditMode: ", throwable) + listener?.changeMode(NoteFragmentListener.Mode.EDIT, true) + }) + disposables.add(updateDisposable) + } + + private fun share() { + super.shareNote() + } + + private fun onLoaded() { + Log.d(TAG, "onLoaded: note loaded") + toggleLoadingUI(false) + } + + private fun toggleLoadingUI(loading: Boolean) { + activity?.runOnUiThread { + binding.progress.isVisible = loading + binding.noteWebview.isVisible = !loading + binding.plainEditingFab.isVisible = !loading + } + } + + companion object { + private const val TAG = "NoteDirectEditFragment" + private const val LOAD_TIMEOUT_SECONDS = 10L + private const val JS_INTERFACE_NAME = "DirectEditingMobileInterface" + private const val JS_RESULT_OK = "ok" + + // language=js + private val JS_CLOSE = """ + (function () { + var closeIcons = document.getElementsByClassName("icon-close"); + if (closeIcons.length > 0) { + closeIcons[0].click(); + } else { + return "close button not available"; + } + return "$JS_RESULT_OK"; + })(); + """.trimIndent() + + @JvmStatic + fun newInstance(accountId: Long, noteId: Long): BaseNoteFragment { + val fragment = NoteDirectEditFragment() + val args = Bundle() + args.putLong(PARAM_NOTE_ID, noteId) + args.putLong(PARAM_ACCOUNT_ID, accountId) + fragment.arguments = args + return fragment + } + + @JvmStatic + fun newInstanceWithNewNote(newNote: Note?): BaseNoteFragment { + val fragment = NoteDirectEditFragment() + val args = Bundle() + args.putSerializable(PARAM_NEWNOTE, newNote) + fragment.arguments = args + return fragment + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java index c480a39de30652d33a56ce0b6823874650503c24..c034064e0ba123e78cede3466f202d2a499126ed 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java @@ -1,7 +1,6 @@ package it.niedermann.owncloud.notes.edit; import static androidx.core.view.ViewCompat.isAttachedToWindow; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.getTextHighlightBackgroundColor; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; import android.content.Context; @@ -26,9 +25,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.FragmentNoteEditBinding; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.model.ISyncCallback; @@ -103,6 +104,11 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { return binding.searchPrev; } + @Override + protected @NonNull ExtendedFloatingActionButton getDirectEditingButton() { + return binding.directEditing; + } + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { @@ -242,7 +248,7 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { } @Override - protected void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor) { + protected void colorWithText(@NonNull String newText, @Nullable Integer current, int color) { if (binding != null && isAttachedToWindow(binding.editContent)) { binding.editContent.clearFocus(); binding.editContent.setSearchText(newText, current); @@ -250,10 +256,12 @@ public class NoteEditFragment extends SearchableBaseNoteFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - super.applyBrand(mainColor, textColor); - binding.editContent.setSearchColor(mainColor); - binding.editContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); + public void applyBrand(int color) { + super.applyBrand(color); + + final var util = BrandingUtil.of(color, requireContext()); + binding.editContent.setSearchColor(color); + binding.editContent.setHighlightColor(util.notes.getTextHighlightBackgroundColor(requireContext(), color, colorPrimary, colorAccent)); } public static BaseNoteFragment newInstance(long accountId, long noteId) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java index 5439a1178c545ccc37d124df9d40e574dfc29a6f..16d2af01544507d849aa8edd4b387d4e702e913e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java @@ -1,7 +1,6 @@ package it.niedermann.owncloud.notes.edit; import static androidx.core.view.ViewCompat.isAttachedToWindow; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.getTextHighlightBackgroundColor; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.getFontSizeFromPreferences; import android.content.Intent; @@ -18,17 +17,20 @@ import android.view.ViewGroup; import android.widget.ScrollView; import android.widget.Toast; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener; +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.util.SSOUtil; @@ -79,6 +81,11 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O return binding.searchPrev; } + @Override + protected @NonNull ExtendedFloatingActionButton getDirectEditingButton() { + return binding.directEditing; + } + @Override protected Layout getLayout() { binding.singleNoteContent.onPreDraw(); @@ -138,7 +145,7 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } @Override - protected void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor) { + protected void colorWithText(@NonNull String newText, @Nullable Integer current, @ColorInt int color) { if (binding != null && isAttachedToWindow(binding.singleNoteContent)) { binding.singleNoteContent.clearFocus(); binding.singleNoteContent.setSearchText(newText, current); @@ -177,10 +184,12 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O } @Override - public void applyBrand(int mainColor, int textColor) { - super.applyBrand(mainColor, textColor); - binding.singleNoteContent.setSearchColor(mainColor); - binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); + public void applyBrand(int color) { + super.applyBrand(color); + + final var util = BrandingUtil.of(color, requireContext()); + binding.singleNoteContent.setSearchColor(color); + binding.singleNoteContent.setHighlightColor(util.notes.getTextHighlightBackgroundColor(requireContext(), color, colorPrimary, colorAccent)); } public static BaseNoteFragment newInstance(long accountId, long noteId) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/SearchableBaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/SearchableBaseNoteFragment.java index 91fb0f6bf5f3ed20a444e109db3d7763c75efcee..d7381c5cb40cb4a73911e9e6680bb9e6d8f36c78 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/SearchableBaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/SearchableBaseNoteFragment.java @@ -1,17 +1,12 @@ package it.niedermann.owncloud.notes.edit; -import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.text.Layout; import android.text.TextUtils; -import trikita.log.Log; import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; -import android.widget.LinearLayout; -import android.widget.ScrollView; import androidx.annotation.CallSuper; import androidx.annotation.ColorInt; @@ -19,13 +14,19 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.SearchView; +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; +import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.nextcloud.android.sso.model.SingleSignOnAccount; -import java.util.regex.Matcher; import java.util.regex.Pattern; import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.branding.BrandedActivity; +import it.niedermann.owncloud.notes.persistence.entity.Account; +import it.niedermann.owncloud.notes.shared.util.ExtendedFabUtil; +import trikita.log.Log; public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { @@ -38,16 +39,14 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { private SearchView searchView; private String searchQuery = null; private static final int delay = 50; // If the search string does not change after $delay ms, then the search task starts. + private boolean directEditAvailable = false; @ColorInt - private int mainColor; - @ColorInt - private int textColor; + private int color; @Override public void onStart() { - this.mainColor = getResources().getColor(R.color.defaultBrand); - this.textColor = Color.WHITE; + this.color = getResources().getColor(R.color.defaultBrand); super.onStart(); } @@ -61,6 +60,47 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } } + @Override + protected void onScroll(int scrollY, int oldScrollY) { + super.onScroll(scrollY, oldScrollY); + if (directEditAvailable) { + // only show FAB if search is not active + if (getSearchNextButton() == null || getSearchNextButton().getVisibility() != View.VISIBLE) { + final ExtendedFloatingActionButton directFab = getDirectEditingButton(); + ExtendedFabUtil.toggleVisibilityOnScroll(directFab, scrollY, oldScrollY); + } + } + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + checkDirectEditingAvailable(); + if (directEditAvailable) { + final ExtendedFloatingActionButton directEditingButton = getDirectEditingButton(); + directEditingButton.setExtended(false); + ExtendedFabUtil.toggleExtendedOnLongClick(directEditingButton); + directEditingButton.setOnClickListener(v -> { + if (listener != null) { + listener.changeMode(NoteFragmentListener.Mode.DIRECT_EDIT, false); + } + }); + } else { + getDirectEditingButton().setVisibility(View.GONE); + } + } + + private void checkDirectEditingAvailable() { + try { + final SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()); + final Account localAccount = repo.getAccountByName(ssoAccount.name); + directEditAvailable = localAccount != null && localAccount.isDirectEditingAvailable(); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + Log.w(TAG, "checkDirectEditingAvailable: ", e); + directEditAvailable = false; + } + } + @Override public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); @@ -88,12 +128,12 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { if (currentVisibility != oldVisibility) { if (currentVisibility != View.VISIBLE) { - colorWithText("", null, mainColor, textColor); + colorWithText("", null, color); searchQuery = ""; hideSearchFabs(); } else { jumpToOccurrence(); - colorWithText(searchQuery, null, mainColor, textColor); + colorWithText(searchQuery, null, color); occurrenceCount = countOccurrences(getContent(), searchQuery); showSearchFabs(); } @@ -111,7 +151,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { next.setOnClickListener(v -> { currentOccurrence++; jumpToOccurrence(); - colorWithText(searchView.getQuery().toString(), currentOccurrence, mainColor, textColor); + colorWithText(searchView.getQuery().toString(), currentOccurrence, color); }); } @@ -120,7 +160,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { occurrenceCount = countOccurrences(getContent(), searchView.getQuery().toString()); currentOccurrence--; jumpToOccurrence(); - colorWithText(searchView.getQuery().toString(), currentOccurrence, mainColor, textColor); + colorWithText(searchView.getQuery().toString(), currentOccurrence, color); }); } @@ -132,7 +172,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { public boolean onQueryTextSubmit(@NonNull String query) { currentOccurrence++; jumpToOccurrence(); - colorWithText(query, currentOccurrence, mainColor, textColor); + colorWithText(query, currentOccurrence, color); return true; } @@ -152,7 +192,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } currentOccurrence = 1; jumpToOccurrence(); - colorWithText(searchQuery, currentOccurrence, mainColor, textColor); + colorWithText(searchQuery, currentOccurrence, color); } private void queryWithHandler(@NonNull String newText) { @@ -198,7 +238,7 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { } } - protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current, int mainColor, int textColor); + protected abstract void colorWithText(@NonNull String newText, @Nullable Integer current, @ColorInt int color); protected abstract Layout getLayout(); @@ -206,7 +246,12 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { protected abstract FloatingActionButton getSearchPrevButton(); + @NonNull + protected abstract ExtendedFloatingActionButton getDirectEditingButton(); + + private void showSearchFabs() { + ExtendedFabUtil.setExtendedFabVisibility(getDirectEditingButton(), false); final var next = getSearchNextButton(); final var prev = getSearchPrevButton(); if (prev != null) { @@ -292,8 +337,6 @@ public abstract class SearchableBaseNoteFragment extends BaseNoteFragment { @CallSuper @Override - public void applyBrand(int mainColor, int textColor) { - this.mainColor = mainColor; - this.textColor = textColor; + public void applyBrand(int color) { } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/category/CategoryDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/category/CategoryDialogFragment.java index 3210a19cf71ec8a0deee5fde8b8f8526645f3cf1..7233a54a86dcd82b2936f6cf47fe4763a8b3474a 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/category/CategoryDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/category/CategoryDialogFragment.java @@ -25,6 +25,7 @@ import it.niedermann.owncloud.notes.branding.BrandedDialogFragment; import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.DialogChangeCategoryBinding; import it.niedermann.owncloud.notes.main.navigation.NavigationItem; +import it.niedermann.owncloud.notes.shared.util.KeyboardUtils; /** * This {@link DialogFragment} allows for the selection of a category. @@ -48,8 +49,9 @@ public class CategoryDialogFragment extends BrandedDialogFragment { private LiveData> categoryLiveData; @Override - public void applyBrand(int mainColor, int textColor) { - BrandingUtil.applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); + public void applyBrand(int color) { + final var util = BrandingUtil.of(color, requireContext()); + util.material.colorTextInputLayout(binding.inputWrapper); } /** @@ -169,12 +171,7 @@ public class CategoryDialogFragment extends BrandedDialogFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (editCategory.getText() == null || editCategory.getText().length() == 0) { - editCategory.requestFocus(); - if (getDialog() != null && getDialog().getWindow() != null) { - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } else { - Log.w(TAG, "can not set SOFT_INPUT_STATE_ALWAYAS_VISIBLE because getWindow() == null"); - } + KeyboardUtils.showKeyboardForEditText(editCategory); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/title/EditTitleDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/title/EditTitleDialogFragment.java index 00714c7697a53594337aaa3b3f08a4b03fc51787..74f6ce198471c3edca8639bced489b288b20f8b2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/title/EditTitleDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/title/EditTitleDialogFragment.java @@ -1,7 +1,5 @@ package it.niedermann.owncloud.notes.edit.title; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToEditTextInputLayout; - import android.app.Dialog; import android.content.Context; import android.os.Bundle; @@ -18,7 +16,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandedDialogFragment; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.DialogEditTitleBinding; +import it.niedermann.owncloud.notes.shared.util.KeyboardUtils; public class EditTitleDialogFragment extends BrandedDialogFragment { @@ -69,13 +69,7 @@ public class EditTitleDialogFragment extends BrandedDialogFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - binding.title.requestFocus(); - final var window = requireDialog().getWindow(); - if (window != null) { - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - } else { - Log.w(TAG, "can not enable soft keyboard because " + Window.class.getSimpleName() + " is null."); - } + KeyboardUtils.showKeyboardForEditText(binding.title); } public static DialogFragment newInstance(String title) { @@ -87,8 +81,9 @@ public class EditTitleDialogFragment extends BrandedDialogFragment { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToEditTextInputLayout(mainColor, binding.inputWrapper); + public void applyBrand(int color) { + final var util = BrandingUtil.of(color, requireContext()); + util.material.colorTextInputLayout(binding.inputWrapper); } /** diff --git a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java index 1d2e02e778fd66f3e756225e46a4d3d6e9214a9f..a8ca945856656f47d4e102c2c68690ef07d7289d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java @@ -37,7 +37,6 @@ import it.niedermann.owncloud.notes.persistence.ApiProvider; import it.niedermann.owncloud.notes.persistence.CapabilitiesClient; import it.niedermann.owncloud.notes.persistence.SyncWorker; import it.niedermann.owncloud.notes.persistence.entity.Account; -import it.niedermann.owncloud.notes.shared.model.Capabilities; import it.niedermann.owncloud.notes.shared.model.IResponseCallback; public class ImportAccountActivity extends AppCompatActivity { @@ -118,7 +117,7 @@ public class ImportAccountActivity extends AppCompatActivity { public void onSuccess(Account account) { runOnUiThread(() -> { Log.i(TAG, capabilities.toString()); - BrandingUtil.saveBrandColors(ImportAccountActivity.this, capabilities.getColor(), capabilities.getTextColor()); + BrandingUtil.saveBrandColor(ImportAccountActivity.this, capabilities.getColor()); setResult(RESULT_OK); Intent intent = new Intent(ImportAccountActivity.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); @@ -160,7 +159,7 @@ public class ImportAccountActivity extends AppCompatActivity { binding.status.setText(getString(R.string.error_sync, getString(R.string.error_no_network))); binding.status.setVisibility(View.VISIBLE); } else if (t instanceof UnknownErrorException && t.getMessage() != null && t.getMessage().contains("No address associated with hostname")) { - // https://github.com/stefan-niedermann/nextcloud-notes/issues/1014 + // https://github.com/nextcloud/notes-android/issues/1014 binding.status.setText(R.string.you_have_to_be_connected_to_the_internet_in_order_to_add_an_account); binding.status.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java index 6f7e7c392e9704a6d66a8541db227af739fc1e41..01fe8ef3ea722c1195c89047785550aba1e4bdda 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java @@ -15,11 +15,11 @@ import android.accounts.NetworkErrorException; import android.animation.AnimatorInflater; import android.app.SearchManager; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.View; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -43,6 +43,7 @@ import com.bumptech.glide.request.RequestOptions; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.AccountImportCancelledException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; @@ -58,12 +59,14 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import it.niedermann.android.util.ColorUtil; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.accountpicker.AccountPickerListener; import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherDialog; import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherListener; import it.niedermann.owncloud.notes.branding.BrandedSnackbar; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ActivityNotesListViewBinding; import it.niedermann.owncloud.notes.databinding.DrawerLayoutBinding; import it.niedermann.owncloud.notes.edit.EditNoteActivity; @@ -119,6 +122,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A protected ItemAdapter adapter; private NavigationAdapter adapterCategories; + @Nullable private MenuAdapter menuAdapter; private SelectionTracker tracker; @@ -174,7 +178,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A runOnUiThread(() -> mainViewModel.postCurrentAccount(account)); } catch (NextcloudFilesAppAccountNotFoundException e) { - // Verbose log output for https://github.com/stefan-niedermann/nextcloud-notes/issues/1256 + // Verbose log output for https://github.com/nextcloud/notes-android/issues/1256 runOnUiThread(() -> new MaterialAlertDialogBuilder(this) .setTitle(NextcloudFilesAppAccountNotFoundException.class.getSimpleName()) .setMessage(R.string.backup) @@ -352,7 +356,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A } else { startActivityForResult(menuItem.getIntent(), resultCode); } - }); + }, nextAccount.getColor()); binding.navigationMenu.setAdapter(menuAdapter); } else { @@ -602,17 +606,24 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(activityBinding.appBar, activityBinding.searchToolbar); - - binding.headerView.setBackgroundColor(mainColor); - binding.appName.setTextColor(textColor); - - // TODO We assume, that the background of the spinner is always white - binding.appName.setTextColor(textColor); - - adapter.applyBrand(mainColor, textColor); - adapterCategories.applyBrand(mainColor, textColor); + public void applyBrand(int color) { + final var util = BrandingUtil.of(color, this); + util.platform.colorNavigationView(binding.navigationView); +/* + util.notes.themeSearchCardView(binding.activityNotesListView.searchBarWrapper); + util.notes.themeSearchToolbar(binding.activityNotesListView.searchToolbar); + util.notes.themeToolbarSearchView(binding.activityNotesListView.searchView); +*/ + + binding.headerView.setBackgroundColor(color); + @ColorInt final int headerTextColor = ColorUtil.INSTANCE.getForegroundColorForBackgroundColor(color); + binding.appName.setTextColor(headerTextColor); + + adapter.applyBrand(color); + adapterCategories.applyBrand(color); + if (menuAdapter != null) { + menuAdapter.applyBrand(color); + } invalidateOptionsMenu(); } @@ -677,7 +688,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A } case REQUEST_CODE_SERVER_SETTINGS: { // Recreate activity completely, because theme switching makes problems when only invalidating the views. - // @see https://github.com/stefan-niedermann/nextcloud-notes/issues/529 + // @see https://github.com/nextcloud/notes-android/issues/529 if (RESULT_OK == resultCode) { ActivityCompat.recreate(this); return; @@ -737,7 +748,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A .show()); }); } else if (e instanceof UnknownErrorException && e.getMessage() != null && e.getMessage().contains("No address associated with hostname")) { - // https://github.com/stefan-niedermann/nextcloud-notes/issues/1014 + // https://github.com/nextcloud/notes-android/issues/1014 runOnUiThread(() -> Snackbar.make(coordinatorLayout, R.string.you_have_to_be_connected_to_the_internet_in_order_to_add_an_account, Snackbar.LENGTH_LONG) .setAnchorView(binding.activityNotesListView.fabCreate) .show()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java index 466d9ae65039f2ace98e0ee334afc10a016c30dd..afa53983bb43c858b34d48193c492e309f54c139 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java @@ -120,7 +120,7 @@ public class MainViewModel extends AndroidViewModel { public void postCurrentAccount(@NonNull Account account) { state.set(KEY_CURRENT_ACCOUNT, account); - BrandingUtil.saveBrandColors(getApplication(), account.getColor(), account.getTextColor()); + BrandingUtil.saveBrandColor(getApplication(), account.getColor()); SingleAccountHelper.setCurrentAccount(getApplication(), account.getAccountName()); final var currentAccount = this.currentAccount.getValue(); @@ -411,11 +411,11 @@ public class MainViewModel extends AndroidViewModel { try { final var capabilities = CapabilitiesClient.getCapabilities(getApplication(), ssoAccount, localAccount.getCapabilitiesETag(), ApiProvider.getInstance()); repo.updateCapabilitiesETag(localAccount.getId(), capabilities.getETag()); - repo.updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor()); + repo.updateBrand(localAccount.getId(), capabilities.getColor()); localAccount.setColor(capabilities.getColor()); - localAccount.setTextColor(capabilities.getTextColor()); - BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor()); + BrandingUtil.saveBrandColor(getApplication(), localAccount.getColor()); repo.updateApiVersion(localAccount.getId(), capabilities.getApiVersion()); + repo.updateDirectEditingAvailable(localAccount.getId(), capabilities.isDirectEditingAvailable()); callback.onSuccess(null); } catch (Throwable t) { if (t.getClass() == NextcloudHttpRequestFailedException.class || t instanceof NextcloudHttpRequestFailedException) { @@ -626,7 +626,7 @@ public class MainViewModel extends AndroidViewModel { /** * @return true if {@param exceptions} contains at least one exception which is not caused by flaky infrastructure. - * @see Issue #1303 + * @see Issue #1303 */ public boolean containsNonInfrastructureRelatedItems(@Nullable Collection exceptions) { if (exceptions == null || exceptions.isEmpty()) { @@ -668,4 +668,4 @@ public class MainViewModel extends AndroidViewModel { public void migrateOldNotes(@NonNull Account account) { repo.migrateOldNotesIfPossible(account.getId()); } -} \ No newline at end of file +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java index 7470fa2579b70efdab5574db3b33c8899c5143db..7692404200f28ea9a269518bff8338c12826c3a2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java @@ -1,8 +1,6 @@ package it.niedermann.owncloud.notes.main.items; import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Color; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -59,17 +57,14 @@ public class ItemAdapter extends RecyclerView.Adapter i private final float fontSize; private final boolean monospace; @ColorInt - private int mainColor; - @ColorInt - private int textColor; + private int color; @Nullable private Integer swipedPosition; public ItemAdapter(@NonNull T context, boolean gridView) { this.noteClickListener = context; this.gridView = gridView; - this.mainColor = ContextCompat.getColor(context, R.color.defaultBrand); - this.textColor = Color.WHITE; + this.color = ContextCompat.getColor(context, R.color.defaultBrand); final var sp = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); this.fontSize = getFontSizeFromPreferences(context, sp); this.monospace = sp.getBoolean(context.getString(R.string.pref_key_font), false); @@ -156,7 +151,7 @@ public class ItemAdapter extends RecyclerView.Adapter i case TYPE_NOTE_WITH_EXCERPT: case TYPE_NOTE_WITHOUT_EXCERPT: case TYPE_NOTE_ONLY_TITLE: { - ((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, mainColor, textColor, searchQuery); + ((NoteViewHolder) holder).bind(isSelected, (Note) itemList.get(position), showCategory, color, searchQuery); break; } } @@ -208,9 +203,8 @@ public class ItemAdapter extends RecyclerView.Adapter i } @Override - public void applyBrand(int mainColor, int textColor) { - this.mainColor = mainColor; - this.textColor = textColor; + public void applyBrand(int color) { + this.color = color; notifyDataSetChanged(); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java index f439adc45b7f1385be75a0d42aefb22333f9581f..ecbf47dc91c22059df602946a5da11bf6f805441 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/NoteViewHolder.java @@ -1,10 +1,10 @@ package it.niedermann.owncloud.notes.main.items; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + import android.content.Context; -import android.text.SpannableString; import android.text.TextUtils; -import android.text.style.BackgroundColorSpan; -import android.text.style.ForegroundColorSpan; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -14,22 +14,15 @@ import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatImageView; -import androidx.core.content.ContextCompat; import androidx.recyclerview.selection.ItemDetailsLookup; import androidx.recyclerview.widget.RecyclerView; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.model.DBStatus; import it.niedermann.owncloud.notes.shared.model.NoteClickListener; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; - public abstract class NoteViewHolder extends RecyclerView.ViewHolder { @NonNull private final NoteClickListener noteClickListener; @@ -41,18 +34,23 @@ public abstract class NoteViewHolder extends RecyclerView.ViewHolder { } @CallSuper - public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { + public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, @ColorInt int color, @Nullable CharSequence searchQuery) { itemView.setSelected(isSelected); itemView.setOnClickListener((view) -> noteClickListener.onNoteClick(getLayoutPosition(), view)); } - protected void bindStatus(AppCompatImageView noteStatus, DBStatus status, int mainColor) { + protected void bindStatus(AppCompatImageView noteStatus, DBStatus status, int color) { noteStatus.setVisibility(DBStatus.VOID.equals(status) ? INVISIBLE : VISIBLE); } - protected void bindCategory(@NonNull Context context, @NonNull TextView noteCategory, boolean showCategory, @NonNull String category, int mainColor) { - noteCategory.setVisibility(showCategory && !category.isEmpty() ? View.VISIBLE : View.GONE); - noteCategory.setText(category); + protected void bindCategory(@NonNull Context context, @NonNull TextView noteCategory, boolean showCategory, @NonNull String category, int color) { + if (!showCategory || category.isEmpty()) { + noteCategory.setVisibility(View.GONE); + } else { + noteCategory.setText(category); + + noteCategory.setVisibility(View.VISIBLE); + } } protected void bindFavorite(@NonNull ImageView noteFavorite, boolean isFavorite) { @@ -60,28 +58,13 @@ public abstract class NoteViewHolder extends RecyclerView.ViewHolder { noteFavorite.setOnClickListener(view -> noteClickListener.onNoteFavoriteClick(getLayoutPosition(), view)); } - protected void bindSearchableContent(@NonNull Context context, @NonNull TextView textView, @Nullable CharSequence searchQuery, @NonNull String content, int mainColor) { - CharSequence processedContent = content; - if (!TextUtils.isEmpty(searchQuery)) { - @ColorInt final int searchBackground = ContextCompat.getColor(context, R.color.bg_highlighted); - @ColorInt final int searchForeground = BrandingUtil.getSecondaryForegroundColorDependingOnTheme(context, mainColor); - - // The Pattern.quote method will add \Q to the very beginning of the string and \E to the end of the string - // It implies that the string between \Q and \E is a literal string and thus the reserved keyword in such string will be ignored. - // See https://stackoverflow.com/questions/15409296/what-is-the-use-of-pattern-quote-method - //noinspection ConstantConditions - final Pattern pattern = Pattern.compile("(" + Pattern.quote(searchQuery.toString()) + ")", Pattern.CASE_INSENSITIVE); - SpannableString spannableString = new SpannableString(content); - Matcher matcher = pattern.matcher(spannableString); - - while (matcher.find()) { - spannableString.setSpan(new ForegroundColorSpan(searchForeground), matcher.start(), matcher.end(), 0); - spannableString.setSpan(new BackgroundColorSpan(searchBackground), matcher.start(), matcher.end(), 0); - } + protected void bindSearchableContent(@NonNull Context context, @NonNull TextView textView, @Nullable CharSequence searchQuery, @NonNull String content, int color) { + textView.setText(content); - processedContent = spannableString; + if (!TextUtils.isEmpty(searchQuery)) { + final var util = BrandingUtil.of(color, context); + util.platform.highlightText(textView, content, searchQuery.toString()); } - textView.setText(processedContent); } public abstract void showSwipe(boolean left); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java index 7c4cecfe9b411eb59ad225c9a123e3bf52b7ce2b..84d705e581053187c3e23a8d6280bed6073160e5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolder.java @@ -6,6 +6,7 @@ import android.text.TextUtils; import android.util.TypedValue; import android.view.View; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.Px; @@ -39,14 +40,14 @@ public class NoteViewGridHolder extends NoteViewHolder { throw new UnsupportedOperationException(NoteViewGridHolder.class.getSimpleName() + " does not support swiping"); } - public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(isSelected, note, showCategory, mainColor, textColor, searchQuery); + public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, @ColorInt int color, @Nullable CharSequence searchQuery) { + super.bind(isSelected, note, showCategory, color, searchQuery); @NonNull final Context context = itemView.getContext(); - bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), mainColor); - bindStatus(binding.noteStatus, note.getStatus(), mainColor); + bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), color); + bindStatus(binding.noteStatus, note.getStatus(), color); bindFavorite(binding.noteFavorite, note.getFavorite()); - bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), mainColor); - bindSearchableContent(context, binding.noteExcerpt, searchQuery, note.getExcerpt().replace(EXCERPT_LINE_SEPARATOR, "\n"), mainColor); + bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), color); + bindSearchableContent(context, binding.noteExcerpt, searchQuery, note.getExcerpt().replace(EXCERPT_LINE_SEPARATOR, "\n"), color); binding.noteExcerpt.setVisibility(TextUtils.isEmpty(note.getExcerpt()) ? GONE : VISIBLE); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java index 416d99d2566f1a70e002757a24f472947409095c..e6b6df499364cf212056f2e9fb12190427a0e554 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/grid/NoteViewGridHolderOnlyTitle.java @@ -32,12 +32,12 @@ public class NoteViewGridHolderOnlyTitle extends NoteViewHolder { throw new UnsupportedOperationException(NoteViewGridHolderOnlyTitle.class.getSimpleName() + " does not support swiping"); } - public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(isSelected, note, showCategory, mainColor, textColor, searchQuery); + public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int color, @Nullable CharSequence searchQuery) { + super.bind(isSelected, note, showCategory, color, searchQuery); @NonNull final Context context = itemView.getContext(); - bindStatus(binding.noteStatus, note.getStatus(), mainColor); + bindStatus(binding.noteStatus, note.getStatus(), color); bindFavorite(binding.noteFavorite, note.getFavorite()); - bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), mainColor); + bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), color); } @Nullable diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java index c171a236a1272dc7a66ae31cd7e2719385e35c7e..ba6fc1d2811eb92ec4bf88fe5dd7830a7920ba70 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithExcerpt.java @@ -3,6 +3,7 @@ package it.niedermann.owncloud.notes.main.items.list; import android.content.Context; import android.view.View; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,16 +29,16 @@ public class NoteViewHolderWithExcerpt extends NoteViewHolder { binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); } - public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(isSelected, note, showCategory, mainColor, textColor, searchQuery); + public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, @ColorInt int color, @Nullable CharSequence searchQuery) { + super.bind(isSelected, note, showCategory, color, searchQuery); @NonNull final var context = itemView.getContext(); binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); - bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), mainColor); - bindStatus(binding.noteStatus, note.getStatus(), mainColor); + bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), color); + bindStatus(binding.noteStatus, note.getStatus(), color); bindFavorite(binding.noteFavorite, note.getFavorite()); - bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), mainColor); - bindSearchableContent(context, binding.noteExcerpt, searchQuery, note.getExcerpt(), mainColor); + bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), color); + bindSearchableContent(context, binding.noteExcerpt, searchQuery, note.getExcerpt(), color); } @NonNull diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java index 3ca136e49c5c8b9ab33cb48addefa3270f9620fc..84426dba086008712a322f08cc54af33ff710f36 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NoteViewHolderWithoutExcerpt.java @@ -28,14 +28,14 @@ public class NoteViewHolderWithoutExcerpt extends NoteViewHolder { binding.noteSwipeFrame.setBackgroundResource(left ? R.color.bg_warning : R.color.bg_attention); } - public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int mainColor, int textColor, @Nullable CharSequence searchQuery) { - super.bind(isSelected, note, showCategory, mainColor, textColor, searchQuery); + public void bind(boolean isSelected, @NonNull Note note, boolean showCategory, int color, @Nullable CharSequence searchQuery) { + super.bind(isSelected, note, showCategory, color, searchQuery); @NonNull final Context context = itemView.getContext(); binding.noteSwipeable.setAlpha(DBStatus.LOCAL_DELETED.equals(note.getStatus()) ? 0.5f : 1.0f); - bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), mainColor); - bindStatus(binding.noteStatus, note.getStatus(), mainColor); + bindCategory(context, binding.noteCategory, showCategory, note.getCategory(), color); + bindStatus(binding.noteStatus, note.getStatus(), color); bindFavorite(binding.noteFavorite, note.getFavorite()); - bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), mainColor); + bindSearchableContent(context, binding.noteTitle, searchQuery, note.getTitle(), color); } @NonNull diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuAdapter.java index 0f757e8727bc2cb32af4235fe1e08b84a445be18..2aee7fdeaad9ac2c5450ebefd50693f3f46fb9b3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuAdapter.java @@ -7,17 +7,18 @@ import android.net.Uri; import android.view.LayoutInflater; import android.view.ViewGroup; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.core.util.Consumer; import androidx.recyclerview.widget.RecyclerView; -import com.nextcloud.android.sso.Constants; import com.nextcloud.android.sso.helper.VersionCheckHelper; import com.nextcloud.android.sso.model.FilesAppType; import it.niedermann.owncloud.notes.FormattingHelpActivity; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.about.AboutActivity; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ItemNavigationBinding; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.preferences.PreferencesActivity; @@ -26,10 +27,12 @@ public class MenuAdapter extends RecyclerView.Adapter { @NonNull private final MenuItem[] menuItems; + @ColorInt + private int color; @NonNull private final Consumer onClick; - public MenuAdapter(@NonNull Context context, @NonNull Account account, int settingsRequestCode, @NonNull Consumer onClick) { + public MenuAdapter(@NonNull Context context, @NonNull Account account, int settingsRequestCode, @NonNull Consumer onClick, @ColorInt int color) { this.menuItems = new MenuItem[]{ new MenuItem(new Intent(context, FormattingHelpActivity.class), R.string.action_formatting_help, R.drawable.ic_baseline_help_outline_24), new MenuItem(generateTrashbinIntent(context, account), R.string.action_trashbin, R.drawable.ic_delete_grey600_24dp), @@ -37,9 +40,14 @@ public class MenuAdapter extends RecyclerView.Adapter { new MenuItem(new Intent(context, AboutActivity.class), R.string.simple_about, R.drawable.ic_info_outline_grey600_24dp) }; this.onClick = onClick; + this.color = BrandingUtil.readBrandMainColor(context); setHasStableIds(true); } + public void applyBrand(int color) { + notifyDataSetChanged(); + } + @Override public long getItemId(int position) { return position; @@ -53,7 +61,7 @@ public class MenuAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull MenuViewHolder holder, int position) { - holder.bind(menuItems[position], onClick); + holder.bind(menuItems[position], color, onClick); } public void updateAccount(@NonNull Context context, @NonNull Account account) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuViewHolder.java index dd05e971254c5119cca79c77b513429f6a8ff56a..7ecc5b82858643498c46990e28481adf2df4a1f3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuViewHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/menu/MenuViewHolder.java @@ -2,12 +2,14 @@ package it.niedermann.owncloud.notes.main.menu; import android.content.Context; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.core.util.Consumer; import androidx.recyclerview.widget.RecyclerView; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ItemNavigationBinding; import static android.view.View.GONE; @@ -21,12 +23,20 @@ public class MenuViewHolder extends RecyclerView.ViewHolder { this.binding = binding; } - public void bind(@NonNull MenuItem menuItem, @NonNull Consumer onClick) { + public void bind(@NonNull MenuItem menuItem, @ColorInt int color, @NonNull Consumer onClick) { @NonNull Context context = itemView.getContext(); - binding.navigationItemLabel.setText(context.getString(menuItem.getLabelResource())); - binding.navigationItemLabel.setTextColor(binding.getRoot().getResources().getColor(R.color.color_default_primary_text)); + binding.navigationItemIcon.setImageDrawable(ContextCompat.getDrawable(context, menuItem.getDrawableResource())); + binding.navigationItemLabel.setText(context.getString(menuItem.getLabelResource())); + binding.navigationItemLabel.setTextColor(ContextCompat.getColor(binding.getRoot().getContext(), R.color.color_default_primary_text)); binding.navigationItemCount.setVisibility(GONE); binding.getRoot().setOnClickListener((v) -> onClick.accept(menuItem)); + + final var util = BrandingUtil.of(color, binding.getRoot().getContext()); + + util.notes.colorNavigationViewItem(binding.getRoot()); + util.notes.colorNavigationViewItemIcon(binding.navigationItemIcon); + util.notes.colorNavigationViewItemText(binding.navigationItemCount); + util.notes.colorNavigationViewItemText(binding.navigationItemLabel); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/navigation/NavigationAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/navigation/NavigationAdapter.java index 98e1985e939f850958ccc6dbfce087df3aa3b6da..e4d5d3773849a8e4e43fe634e1786375357666ac 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/navigation/NavigationAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/navigation/NavigationAdapter.java @@ -1,5 +1,7 @@ package it.niedermann.owncloud.notes.main.navigation; +import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.UNCATEGORIZED; + import android.content.Context; import android.text.TextUtils; import android.view.LayoutInflater; @@ -17,14 +19,12 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.main.MainActivity; -import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.UNCATEGORIZED; - public class NavigationAdapter extends RecyclerView.Adapter { @NonNull private final Context context; @ColorInt - private int mainColor; + private int color; @DrawableRes public static final int ICON_FOLDER = R.drawable.ic_folder_24dp; @DrawableRes @@ -38,11 +38,6 @@ public class NavigationAdapter extends RecyclerView.Adapter items = new ArrayList<>(); private String selectedItem = null; @@ -51,10 +46,15 @@ public class NavigationAdapter extends RecyclerView.Adapter navigationClickListener.onItemClick(currentItem)); } - public void bind(@NonNull NavigationItem item, @ColorInt int mainColor, String selectedItem) { + public void bind(@NonNull NavigationItem item, @ColorInt int color, String selectedItem) { currentItem = item; final boolean isSelected = item.id.equals(selectedItem); name.setText(NoteUtil.extendCategory(item.label)); @@ -53,15 +54,24 @@ class NavigationViewHolder extends RecyclerView.ViewHolder { } else { icon.setVisibility(View.GONE); } - final int textColor = isSelected ? mainColor : ContextCompat.getColor(view.getContext(), R.color.color_default_primary_text); + final int textColor = ContextCompat.getColor(view.getContext(), (isSelected ? R.color.accent : R.color.color_default_primary_text)); name.setTextColor(textColor); count.setTextColor(textColor); icon.setSelected(isSelected); +/* + + final var util = BrandingUtil.of(color, itemView.getContext()); + + util.notes.colorNavigationViewItem(view); + util.notes.colorNavigationViewItemIcon(icon); + util.notes.colorNavigationViewItemText(name); + util.notes.colorNavigationViewItemText(count); +*/ view.setSelected(isSelected); - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); + final var params = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); params.leftMargin = item.icon == NavigationAdapter.ICON_SUB_FOLDER || item.icon == NavigationAdapter.ICON_SUB_MULTIPLE ? view.getResources().getDimensionPixelSize(R.dimen.spacer_3x) : 0; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java index a8dfd718a410997997d4b9524de132ccacb76350..027275dc8883ea65948a57ac05528eb85142f3ac 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java @@ -2,10 +2,8 @@ package it.niedermann.owncloud.notes.manageaccounts; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLayerDrawable; import static it.niedermann.owncloud.notes.shared.util.ApiVersionUtil.getPreferredApiVersion; -import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.view.View; @@ -80,7 +78,6 @@ public class ManageAccountViewHolder extends RecyclerView.ViewHolder { }); if (isCurrentAccount) { binding.currentAccountIndicator.setVisibility(VISIBLE); - applyBrandToLayerDrawable((LayerDrawable) binding.currentAccountIndicator.getDrawable(), R.id.area, localAccount.getColor()); } else { binding.currentAccountIndicator.setVisibility(GONE); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java index cc3ca8eeb9f604c0af061f3195b4fd8199a10daf..b451e1ab65d7658c67ca62728e084dba970015bc 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java @@ -1,6 +1,5 @@ package it.niedermann.owncloud.notes.manageaccounts; -import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToEditTextInputLayout; import static it.niedermann.owncloud.notes.branding.BrandingUtil.readBrandMainColorLiveData; import static it.niedermann.owncloud.notes.shared.util.ApiVersionUtil.getPreferredApiVersion; @@ -27,6 +26,7 @@ import java.util.function.Function; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.branding.DeleteAlertDialogBuilder; import it.niedermann.owncloud.notes.databinding.ActivityManageAccountsBinding; import it.niedermann.owncloud.notes.databinding.DialogEditSettingBinding; @@ -136,8 +136,9 @@ public class ManageAccountsActivity extends LockedActivity implements IManageAcc final var mainColor$ = readBrandMainColorLiveData(this); mainColor$.observe(this, color -> { mainColor$.removeObservers(this); - applyBrandToEditTextInputLayout(color, binding.inputWrapper); - binding.progress.setIndicatorColor(color); + final var util = BrandingUtil.of(color, this); + util.material.colorTextInputLayout(binding.inputWrapper); + util.material.colorProgressBar(binding.progress); }); binding.inputWrapper.setHint(title); @@ -207,7 +208,6 @@ public class ManageAccountsActivity extends LockedActivity implements IManageAcc } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); + public void applyBrand(int color) { } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/ApiProvider.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/ApiProvider.java index 0cab114eeb1f2140e0612397dd9ce0c2e67130a8..7dc5c7102ebdcfe9b90260a355a353ea37c8ea67 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/ApiProvider.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/ApiProvider.java @@ -1,7 +1,6 @@ package it.niedermann.owncloud.notes.persistence; import android.content.Context; -import trikita.log.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -20,12 +19,14 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import it.niedermann.owncloud.notes.persistence.sync.CapabilitiesDeserializer; +import it.niedermann.owncloud.notes.persistence.sync.FilesAPI; import it.niedermann.owncloud.notes.persistence.sync.NotesAPI; import it.niedermann.owncloud.notes.persistence.sync.OcsAPI; import it.niedermann.owncloud.notes.shared.model.ApiVersion; import it.niedermann.owncloud.notes.shared.model.Capabilities; import retrofit2.NextcloudRetrofitApiBuilder; import retrofit2.Retrofit; +import trikita.log.Log; /** * Since creating APIs via {@link Retrofit} uses reflection and {@link NextcloudAPI} is supposed to stay alive as long as possible, those artifacts are going to be cached. @@ -39,11 +40,14 @@ public class ApiProvider { private static final ApiProvider INSTANCE = new ApiProvider(); private static final String API_ENDPOINT_OCS = "/ocs/v2.php/cloud/"; + private static final String API_ENDPOINT_FILES = "/ocs/v2.php/apps/files/api/v1/"; private static final Map API_CACHE = new ConcurrentHashMap<>(); private static final Map API_CACHE_OCS = new ConcurrentHashMap<>(); private static final Map API_CACHE_NOTES = new ConcurrentHashMap<>(); + private static final Map API_CACHE_FILES = new ConcurrentHashMap<>(); + public static ApiProvider getInstance() { return INSTANCE; @@ -86,6 +90,15 @@ public class ApiProvider { return ncApi.isConnected(); } + public synchronized FilesAPI getFilesAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount) { + if (API_CACHE_FILES.containsKey(ssoAccount.name)) { + return API_CACHE_FILES.get(ssoAccount.name); + } + final var filesAPI = new NextcloudRetrofitApiBuilder(getNextcloudAPI(context, ssoAccount), API_ENDPOINT_FILES).create(FilesAPI.class); + API_CACHE_FILES.put(ssoAccount.name, filesAPI); + return filesAPI; + } + private synchronized NextcloudAPI getNextcloudAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount) { if (API_CACHE.containsKey(ssoAccount.name)) { return API_CACHE.get(ssoAccount.name); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesWorker.java index 3d8700abd66d98c58cdd5fd9eddcc40d4af5c13b..f9cc023eddfce81ad3670d2bb5d757d955e86ae7 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesWorker.java @@ -49,8 +49,9 @@ public class CapabilitiesWorker extends Worker { Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name); final var capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, account.getCapabilitiesETag(), ApiProvider.getInstance()); repo.updateCapabilitiesETag(account.getId(), capabilities.getETag()); - repo.updateBrand(account.getId(), capabilities.getColor(), capabilities.getTextColor()); + repo.updateBrand(account.getId(), capabilities.getColor()); repo.updateApiVersion(account.getId(), capabilities.getApiVersion()); + repo.updateDirectEditingAvailable(account.getId(), capabilities.isDirectEditingAvailable()); Log.i(TAG, capabilities.toString()); repo.updateDisplayName(account.getId(), CapabilitiesClient.getDisplayName(getApplicationContext(), ssoAccount, ApiProvider.getInstance())); } catch (Throwable e) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt b/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..01cdfb1ac1c4d68b4f5ed8c4c82d80733454f1aa --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt @@ -0,0 +1,69 @@ +package it.niedermann.owncloud.notes.persistence + +import android.app.Application +import android.content.Context +import com.nextcloud.android.sso.model.SingleSignOnAccount +import io.reactivex.Single +import io.reactivex.schedulers.Schedulers +import it.niedermann.owncloud.notes.persistence.entity.Note +import it.niedermann.owncloud.notes.shared.model.ApiVersion +import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingRequestBody + +class DirectEditingRepository private constructor(private val applicationContext: Context) { + + private val apiProvider: ApiProvider by lazy { ApiProvider.getInstance() } + private val notesRepository: NotesRepository by lazy { + NotesRepository.getInstance( + applicationContext, + ) + } + + private fun getNotesPath(account: SingleSignOnAccount): Single { + return Single.fromCallable { + val call = notesRepository.getServerSettings(account, ApiVersion.API_VERSION_1_0) + val response = call.execute() + response.body()?.notesPath ?: throw RuntimeException("No notes path available") + }.subscribeOn(Schedulers.io()) + } + + fun getDirectEditingUrl( + account: SingleSignOnAccount, + note: Note, + ): Single { + return getNotesPath(account) + .flatMap { notesPath -> + val filesAPI = apiProvider.getFilesAPI(applicationContext, account) + Single.fromCallable { + val call = + filesAPI.getDirectEditingUrl( + DirectEditingRequestBody( + path = notesPath, + editorId = SUPPORTED_EDITOR_ID, + fileId = note.remoteId!!, + ), + ) + val response = call.execute() + response.body()?.ocs?.data?.url + ?: throw RuntimeException("No url available") + }.subscribeOn(Schedulers.io()) + } + } + + companion object { + private const val SUPPORTED_EDITOR_ID = "text" + + private var instance: DirectEditingRepository? = null + + /** + * @param applicationContext The application context. Do NOT use a view context to prevent leaks. + */ + @JvmStatic + fun getInstance(applicationContext: Context): DirectEditingRepository { + require(applicationContext is Application) + if (instance == null) { + instance = DirectEditingRepository(applicationContext) + } + return instance!! + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java index db7d7ded55cfdca7eb4ef3fc444b0760098de72b..3ccce1d494829344fae0639a7e6c55420e3346df 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java @@ -35,6 +35,8 @@ import it.niedermann.owncloud.notes.persistence.migration.Migration_19_20; import it.niedermann.owncloud.notes.persistence.migration.Migration_20_21; import it.niedermann.owncloud.notes.persistence.migration.Migration_21_22; import it.niedermann.owncloud.notes.persistence.migration.Migration_22_23; +import it.niedermann.owncloud.notes.persistence.migration.Migration_23_24; +import it.niedermann.owncloud.notes.persistence.migration.Migration_24_25; import it.niedermann.owncloud.notes.persistence.migration.Migration_9_10; import trikita.log.Log; @@ -45,7 +47,7 @@ import trikita.log.Log; CategoryOptions.class, SingleNoteWidgetData.class, NotesListWidgetData.class - }, version = 23 + }, version = 25 ) @TypeConverters({Converters.class}) public abstract class NotesDatabase extends RoomDatabase { @@ -80,7 +82,9 @@ public abstract class NotesDatabase extends RoomDatabase { new Migration_19_20(context), new Migration_20_21(), new Migration_21_22(context), - new Migration_22_23() + new Migration_22_23(), + new Migration_23_24(context), + new Migration_24_25() ) .fallbackToDestructiveMigrationOnDowngrade() .fallbackToDestructiveMigration() @@ -114,6 +118,7 @@ public abstract class NotesDatabase extends RoomDatabase { * So we have to retrieve old entries via raw sql commands. * * Check for more details + * * @return list of old notes entries */ @NonNull diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java index 18adb282a8422e9e46fee7d6b312123ebff803ed..ce64fcc1c3c8e1ea4269c4fea680ff1dabcc7049 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java @@ -257,8 +257,8 @@ public class NotesRepository { return db.getAccountDao().countAccounts$(); } - public void updateBrand(long id, @ColorInt Integer color, @ColorInt Integer textColor) { - db.getAccountDao().updateBrand(id, color, textColor); + public void updateBrand(long id, @ColorInt Integer color) { + db.getAccountDao().updateBrand(id, color); } public void updateETag(long id, String eTag) { @@ -272,6 +272,9 @@ public class NotesRepository { public void updateModified(long id, long modified) { db.getAccountDao().updateModified(id, modified); } + public void updateDirectEditingAvailable(final long id, final boolean available) { + db.getAccountDao().updateDirectEditingAvailable(id, available); + } // Notes @@ -518,7 +521,7 @@ public class NotesRepository { public Note updateNoteAndSync(@NonNull Account localAccount, @NonNull Note oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) { final Note newNote; // Re-read the up to date remoteId from the database because the UI might not have the state after synchronization yet - // https://github.com/stefan-niedermann/nextcloud-notes/issues/1198 + // https://github.com/nextcloud/notes-android/issues/1198 @Nullable final Long remoteId = db.getNoteDao().getRemoteId(oldNote.getId()); if (newContent == null) { newNote = new Note(oldNote.getId(), remoteId, oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.getCategory(), oldNote.getFavorite(), oldNote.getETag(), DBStatus.LOCAL_EDITED, localAccount.getId(), oldNote.getExcerpt(), oldNote.getScrollY()); @@ -616,7 +619,7 @@ public class NotesRepository { .setIntent(intent) .build()); } else { - // Prevent crash https://github.com/stefan-niedermann/nextcloud-notes/issues/613 + // Prevent crash https://github.com/nextcloud/notes-android/issues/613 Log.e(TAG, "shortLabel cannot be empty " + (BuildConfig.DEBUG ? note : note.getTitle())); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index e10b80bfda14b0598b67802277cb22aa5c7bf144..76023118fb08d76a7829258bdf306712c12db677 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -55,7 +55,7 @@ public class SyncWorker extends Worker { /** * Set up sync work to enabled every 15 minutes or just disabled - * https://github.com/stefan-niedermann/nextcloud-notes/issues/1168 + * https://github.com/nextcloud/notes-android/issues/1168 * * @param context the application * @param backgroundSync the toggle result backgroundSync diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java index 7723e1f0dfbab01d59627e30c49583dd2a9cb88a..8b3424adfe8538037cc64b5757794ce474b7b0f9 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java @@ -21,8 +21,8 @@ public interface AccountDao { @Delete void deleteAccount(Account localAccount); - String getAccounts = "SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName FROM Account"; - String getAccountById = "SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName FROM Account WHERE ID = :accountId"; + String getAccounts = "SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName, directEditingAvailable FROM Account"; + String getAccountById = "SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName, directEditingAvailable FROM Account WHERE ID = :accountId"; @Query(getAccounts) LiveData> getAccounts$(); @@ -36,14 +36,14 @@ public interface AccountDao { @Query(getAccountById) Account getAccountById(long accountId); - @Query("SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName FROM Account WHERE ACCOUNTNAME = :accountName") + @Query("SELECT id, url, userName, accountName, eTag, modified, apiVersion, color, textColor, capabilitiesEtag, COALESCE(displayName, userName) as displayName, directEditingAvailable FROM Account WHERE ACCOUNTNAME = :accountName") Account getAccountByName(String accountName); @Query("SELECT COUNT(*) FROM Account") LiveData countAccounts$(); - @Query("UPDATE Account SET COLOR = :color, TEXTCOLOR = :textColor WHERE id = :id") - void updateBrand(long id, @ColorInt Integer color, @ColorInt Integer textColor); + @Query("UPDATE Account SET COLOR = :color WHERE id = :id") + void updateBrand(long id, @ColorInt Integer color); @Query("UPDATE Account SET ETAG = :eTag WHERE ID = :id") void updateETag(long id, String eTag); @@ -59,4 +59,7 @@ public interface AccountDao { @Query("UPDATE Account SET DISPLAYNAME = :displayName WHERE id = :id") void updateDisplayName(long id, @Nullable String displayName); + + @Query("UPDATE Account SET directEditingAvailable = :available WHERE id = :id") + void updateDirectEditingAvailable(long id, boolean available); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java index 016b2fd0c1e8f7f6884a5b11e9cbd332313a5640..05b957f0fd616b68add3b87672004cd846bf042e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java @@ -10,17 +10,9 @@ import androidx.room.Entity; import androidx.room.Index; import androidx.room.PrimaryKey; -import org.json.JSONArray; -import org.json.JSONException; - import java.io.Serializable; import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.NoSuchElementException; -import it.niedermann.owncloud.notes.shared.model.ApiVersion; import it.niedermann.owncloud.notes.shared.model.Capabilities; @Entity( @@ -60,6 +52,7 @@ public class Account implements Serializable { private String capabilitiesETag; @Nullable private String displayName; + private boolean directEditingAvailable; public Account() { // Default constructor @@ -76,8 +69,8 @@ public class Account implements Serializable { public void setCapabilities(@NonNull Capabilities capabilities) { capabilitiesETag = capabilities.getETag(); apiVersion = capabilities.getApiVersion(); + directEditingAvailable = capabilities.isDirectEditingAvailable(); setColor(capabilities.getColor()); - setTextColor(capabilities.getTextColor()); } public long getId() { @@ -176,6 +169,14 @@ public class Account implements Serializable { this.displayName = displayName; } + public boolean isDirectEditingAvailable() { + return directEditingAvailable; + } + + public void setDirectEditingAvailable(boolean directEditingAvailable) { + this.directEditingAvailable = directEditingAvailable; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -196,6 +197,7 @@ public class Account implements Serializable { return false; if (capabilitiesETag != null ? !capabilitiesETag.equals(account.capabilitiesETag) : account.capabilitiesETag != null) return false; + if (directEditingAvailable != account.directEditingAvailable) return false; return true; } @@ -211,6 +213,7 @@ public class Account implements Serializable { result = 31 * result + color; result = 31 * result + textColor; result = 31 * result + (capabilitiesETag != null ? capabilitiesETag.hashCode() : 0); + result = 31 * result + (directEditingAvailable ? 1 : 0); return result; } @@ -228,6 +231,7 @@ public class Account implements Serializable { ", color=" + color + ", textColor=" + textColor + ", capabilitiesETag='" + capabilitiesETag + '\'' + + ", directEditingAvailable='" + directEditingAvailable + '\'' + '}'; } -} \ No newline at end of file +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_13_14.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_13_14.java index aa144eabdfe792a09272237b3faeac650fdfdf6f..f309c3bc5ee9c621b893751583dfc2e43a01b095 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_13_14.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_13_14.java @@ -32,7 +32,7 @@ public class Migration_13_14 extends Migration { /** * Move single note widget preferences to database - * https://github.com/stefan-niedermann/nextcloud-notes/issues/754 + * https://github.com/nextcloud/notes-android/issues/754 */ @Override public void migrate(@NonNull SupportSQLiteDatabase db) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_14_15.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_14_15.java index 70c57294b454ba473eed04645e7aa2795e642546..1f7b80c6f85106c879a09fb881a233264d92ee43 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_14_15.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_14_15.java @@ -22,7 +22,7 @@ public class Migration_14_15 extends Migration { /** * Normalize database (move category from string field to own table) - * https://github.com/stefan-niedermann/nextcloud-notes/issues/814 + * https://github.com/nextcloud/notes-android/issues/814 */ @Override public void migrate(@NonNull SupportSQLiteDatabase db) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_15_16.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_15_16.java index df84f1de119f8d020e7a6d5e6fe7c5910fb51b18..02128280dfc41231be11b03487fb725c73e2a8c3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_15_16.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_15_16.java @@ -33,7 +33,7 @@ public class Migration_15_16 extends Migration { /** * Moves note list widget preferences from {@link SharedPreferences} to database - * https://github.com/stefan-niedermann/nextcloud-notes/issues/832 + * https://github.com/nextcloud/notes-android/issues/832 */ @Override public void migrate(@NonNull SupportSQLiteDatabase db) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_16_17.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_16_17.java index b61262ebc19d399c7b5bf294c1ae790e354a2beb..a772fe845d4bfb97efbf3af80a5d95195ea8a976 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_16_17.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_16_17.java @@ -12,7 +12,7 @@ public class Migration_16_17 extends Migration { /** * Adds a column to store the current scroll position per note - * https://github.com/stefan-niedermann/nextcloud-notes/issues/227 + * https://github.com/nextcloud/notes-android/issues/227 */ @Override public void migrate(@NonNull SupportSQLiteDatabase db) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_21_22.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_21_22.java index c076d38e87a941699fad26aea63175513e5be6a6..c52ad8e38e805be3d594c274b318eb6fba4d5479 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_21_22.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_21_22.java @@ -12,7 +12,7 @@ import it.niedermann.owncloud.notes.persistence.SyncWorker; /** * Enabling backgroundSync, set from {@link String} values to {@link Boolean} values - * https://github.com/stefan-niedermann/nextcloud-notes/issues/1168 + * https://github.com/nextcloud/notes-android/issues/1168 */ public class Migration_21_22 extends Migration { @NonNull diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_22_23.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_22_23.java index 1ba08a3c92f4b753b033080b60710e7080a01c98..2a993a2b79564977b323ba73e092fadec4a86227 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_22_23.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_22_23.java @@ -25,7 +25,7 @@ import it.niedermann.owncloud.notes.shared.model.ApiVersion; /** * Add displayName property to {@link Account}. *

- * See: #1079 Show DisplayName instead of uid attribute for LDAP users + * See: #1079 Show DisplayName instead of uid attribute for LDAP users *

* Sanitizes the stored API versions in the database. */ diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_23_24.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_23_24.java new file mode 100644 index 0000000000000000000000000000000000000000..6aa892311fb573646a8c528b0aae2b479c2bd8b6 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_23_24.java @@ -0,0 +1,28 @@ +package it.niedermann.owncloud.notes.persistence.migration; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.preference.PreferenceManager; +import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; + +/** + * Remove textColor property from {@link android.content.SharedPreferences} and the + * database as it is no longer needed for theming. + */ +public class Migration_23_24 extends Migration { + + @NonNull + private final Context context; + + public Migration_23_24(@NonNull Context context) { + super(23, 24); + this.context = context; + } + + @Override + public void migrate(@NonNull SupportSQLiteDatabase db) { + PreferenceManager.getDefaultSharedPreferences(context).edit().remove("branding_text").apply(); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_24_25.kt b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_24_25.kt new file mode 100644 index 0000000000000000000000000000000000000000..e172d17864c3e9b283892484e05e405b2327e7d3 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_24_25.kt @@ -0,0 +1,13 @@ +package it.niedermann.owncloud.notes.persistence.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +@Suppress("ClassName", "Detekt.ClassNaming", "Detekt.MagicNumber") +class Migration_24_25 : Migration(24, 25) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE Account ADD COLUMN directEditingAvailable INTEGER DEFAULT 0 NOT NULL") + // remove capabilities etag to force refresh + db.execSQL("UPDATE Account SET capabilitiesETag = NULL") + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java index 9b4b328fa3e8dc0863f9bc56c4c2fe5fe83634ab..b637e516465bd7d72760ba359bce736d3d142477 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/migration/Migration_9_10.java @@ -19,7 +19,7 @@ public class Migration_9_10 extends Migration { /** * Adds a column to store excerpt instead of regenerating it each time - * https://github.com/stefan-niedermann/nextcloud-notes/issues/528 + * https://github.com/nextcloud/notes-android/issues/528 */ @Override public void migrate(@NonNull SupportSQLiteDatabase db) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java index d5ae7b494f4e8ea84279b81a017a8dd677a6f157..e3b268ab7c8081eb180da196deee8021add60bd4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java @@ -31,6 +31,9 @@ public class CapabilitiesDeserializer implements JsonDeserializer private static final String CAPABILITIES_THEMING = "theming"; private static final String CAPABILITIES_THEMING_COLOR = "color"; private static final String CAPABILITIES_THEMING_COLOR_TEXT = "color-text"; + private static final String CAPABILITIES_FILES = "files"; + private static final String CAPABILITIES_FILES_DIRECT_EDITING = "directEditing"; + private static final String CAPABILITIES_FILES_DIRECT_EDITING_SUPPORTS_FILE_ID = "supportsFileId"; @Override public Capabilities deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { @@ -61,7 +64,21 @@ public class CapabilitiesDeserializer implements JsonDeserializer } } } + response.setDirectEditingAvailable(hasDirectEditingCapability(capabilities)); } return response; } + + private boolean hasDirectEditingCapability(final JsonObject capabilities) { + if (capabilities.has(CAPABILITIES_FILES)) { + final var files = capabilities.getAsJsonObject(CAPABILITIES_FILES); + if (files.has(CAPABILITIES_FILES_DIRECT_EDITING)) { + final var directEditing = files.getAsJsonObject(CAPABILITIES_FILES_DIRECT_EDITING); + if (directEditing.has(CAPABILITIES_FILES_DIRECT_EDITING_SUPPORTS_FILE_ID)) { + return directEditing.get(CAPABILITIES_FILES_DIRECT_EDITING_SUPPORTS_FILE_ID).getAsBoolean(); + } + } + } + return false; + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/FilesAPI.kt b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/FilesAPI.kt new file mode 100644 index 0000000000000000000000000000000000000000..a14dcdd84ecc478b6feea64251c65cf4639f6c90 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/FilesAPI.kt @@ -0,0 +1,18 @@ +package it.niedermann.owncloud.notes.persistence.sync + +import it.niedermann.owncloud.notes.shared.model.OcsResponse +import it.niedermann.owncloud.notes.shared.model.OcsUrl +import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingInfo +import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingRequestBody +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST + +interface FilesAPI { + @GET("directEditing?format=json") + fun getDirectEditingInfo(): Call> + + @POST("directEditing/open?format=json") + fun getDirectEditingUrl(@Body body: DirectEditingRequestBody): Call> +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesActivity.java index 2dcd99ec1bd1efa9e6514fafe059ef0b2f5a3469..d6653a314c2fab5535a767b59c73451335b443a5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesActivity.java @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.ActivityPreferencesBinding; public class PreferencesActivity extends LockedActivity { @@ -31,7 +32,11 @@ public class PreferencesActivity extends LockedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { - applyBrandToPrimaryToolbar(binding.appBar, binding.toolbar); + public void applyBrand(int color) { +/* + final var util = BrandingUtil.of(color, this); + util.platform.themeStatusBar(this); + util.material.themeToolbar(binding.toolbar); +*/ } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesFragment.java index ae8e8caa45ff12014e14f38493648b0899acaea4..5dce6951d99bf6e39c1ef181b4930dd6d1b90e39 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/preferences/PreferencesFragment.java @@ -102,27 +102,25 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Bra public void onStart() { super.onStart(); final var context = requireContext(); - @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(context); - @ColorInt final int textColor = BrandingUtil.readBrandTextColor(context); - applyBrand(mainColor, textColor); + @ColorInt final int color = BrandingUtil.readBrandMainColor(context); + applyBrand(color); } /** * Change color for backgroundSyncPref as well * https://github.com/stefan-niedermann/nextcloud-deck/issues/531 * - * @param mainColor color of main brand - * @param textColor color of text + * @param color color of main brand */ @Override - public void applyBrand(int mainColor, int textColor) { - fontPref.applyBrand(mainColor, textColor); - lockPref.applyBrand(mainColor, textColor); - wifiOnlyPref.applyBrand(mainColor, textColor); - gridViewPref.applyBrand(mainColor, textColor); - preventScreenCapturePref.applyBrand(mainColor, textColor); - backgroundSyncPref.applyBrand(mainColor, textColor); - keepScreenOnPref.applyBrand(mainColor, textColor); + public void applyBrand(int color) { + fontPref.applyBrand(color); + lockPref.applyBrand(color); + wifiOnlyPref.applyBrand(color); + gridViewPref.applyBrand(color); + preventScreenCapturePref.applyBrand(color); + backgroundSyncPref.applyBrand(color); + keepScreenOnPref.applyBrand(color); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/quicksettings/NewNoteTileService.java b/app/src/main/java/it/niedermann/owncloud/notes/quicksettings/NewNoteTileService.java index d660f182eac62dc9c61185897510f10922c435af..56bb20b534f6bc57b2903b02340cd40dd4113d0e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/quicksettings/NewNoteTileService.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/quicksettings/NewNoteTileService.java @@ -1,8 +1,6 @@ package it.niedermann.owncloud.notes.quicksettings; -import android.annotation.TargetApi; import android.content.Intent; -import android.os.Build; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; @@ -11,7 +9,6 @@ import it.niedermann.owncloud.notes.edit.EditNoteActivity; /** * This {@link TileService} adds a quick settings tile that leads to the new note view. */ -@TargetApi(Build.VERSION_CODES.N) public class NewNoteTileService extends TileService { @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java index 06bd867d34ab3072ccbb9f9d9677b9b7f0a76018..e3d739deee7b9c35db56c537bc186729cce659f9 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java @@ -3,7 +3,6 @@ package it.niedermann.owncloud.notes.shared.model; import android.graphics.Color; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; public class Capabilities { @@ -16,6 +15,8 @@ public class Capabilities { @Nullable private String eTag; + private boolean directEditingAvailable; + public void setApiVersion(String apiVersion) { this.apiVersion = apiVersion; } @@ -49,7 +50,15 @@ public class Capabilities { this.textColor = textColor; } - @NonNull + + public boolean isDirectEditingAvailable() { + return directEditingAvailable; + } + + public void setDirectEditingAvailable(boolean directEditingAvailable) { + this.directEditingAvailable = directEditingAvailable; + } + @Override public String toString() { return "Capabilities{" + @@ -57,6 +66,7 @@ public class Capabilities { ", color=" + color + ", textColor=" + textColor + ", eTag='" + eTag + '\'' + + ", hasDirectEditing=" + directEditingAvailable + '}'; } -} \ No newline at end of file +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/OcsUrl.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/OcsUrl.kt new file mode 100644 index 0000000000000000000000000000000000000000..bc9e2a14f17bee653c2148722160694302c97bcd --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/OcsUrl.kt @@ -0,0 +1,9 @@ +package it.niedermann.owncloud.notes.shared.model + +import com.google.gson.annotations.Expose + +data class OcsUrl( + @Expose + @JvmField + var url: String? = null +) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingCreator.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingCreator.kt new file mode 100644 index 0000000000000000000000000000000000000000..7fb7b5accc71391385fc5fe11c71e272152a3efc --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingCreator.kt @@ -0,0 +1,18 @@ +package it.niedermann.owncloud.notes.shared.model.directediting + +import com.google.gson.annotations.Expose + +data class DirectEditingCreator( + @Expose + val id: String, + @Expose + val editor: String, + @Expose + val name: String, + @Expose + val extension: String, + @Expose + val mimetype: String, + @Expose + val templates: Boolean, +) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingEditor.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingEditor.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e5de2e3d0ea3a5902c780942b513d439cdb597a --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingEditor.kt @@ -0,0 +1,19 @@ +package it.niedermann.owncloud.notes.shared.model.directediting + +import com.google.gson.annotations.Expose + +/** + * Editor for direct editing data model + */ +data class DirectEditingEditor( + @Expose + val id: String, + @Expose + val name: String, + @Expose + val mimetypes: ArrayList, + @Expose + val optionalMimetypes: ArrayList, + @Expose + val secure: Boolean, +) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingInfo.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..47131dd305ff8e689bdb5bd452d92a851c6f5f5d --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingInfo.kt @@ -0,0 +1,10 @@ +package it.niedermann.owncloud.notes.shared.model.directediting + +import com.google.gson.annotations.Expose + +data class DirectEditingInfo( + @Expose + val editors: Map, + @Expose + val creators: Map, +) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingRequestBody.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingRequestBody.kt new file mode 100644 index 0000000000000000000000000000000000000000..9bfea16bfc46a5c56be9a99a62b813a6173c7dbd --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/directediting/DirectEditingRequestBody.kt @@ -0,0 +1,12 @@ +package it.niedermann.owncloud.notes.shared.model.directediting + +import com.google.gson.annotations.Expose + +data class DirectEditingRequestBody( + @Expose + val path: String, + @Expose + val editorId: String, + @Expose + val fileId: Long, +) diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/util/ExtendedFabUtil.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/ExtendedFabUtil.kt new file mode 100644 index 0000000000000000000000000000000000000000..a27714fbe1a5a661c74308f286876e1cfeeeaee0 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/ExtendedFabUtil.kt @@ -0,0 +1,65 @@ +package it.niedermann.owncloud.notes.shared.util + +import android.view.View +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import com.google.android.material.R +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton + +object ExtendedFabUtil { + @JvmStatic + fun setExtendedFabVisibility( + extendedFab: ExtendedFloatingActionButton, + visibility: Boolean, + ) { + if (visibility) { + extendedFab.show() + } else { + if (extendedFab.isExtended) { + extendedFab.hide() + } else { + if (extendedFab.animation == null) { + val animation = AnimationUtils.loadAnimation( + extendedFab.context, + R.anim.abc_shrink_fade_out_from_bottom, + ) + animation.setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationEnd(animation: Animation) { + extendedFab.visibility = View.GONE + } + + override fun onAnimationRepeat(animation: Animation) {} + }) + extendedFab.startAnimation(animation) + } + } + } + } + + @JvmStatic + fun toggleExtendedOnLongClick(extendedFab: ExtendedFloatingActionButton) { + extendedFab.setOnLongClickListener { v: View? -> + if (extendedFab.isExtended) { + extendedFab.shrink() + } else { + extendedFab.extend() + } + true + } + } + + @JvmStatic + fun toggleVisibilityOnScroll( + extendedFab: ExtendedFloatingActionButton, + scrollY: Int, + oldScrollY: Int, + ) { + @Suppress("ConvertTwoComparisonsToRangeCheck") + if (oldScrollY > 0 && scrollY > oldScrollY && extendedFab.isShown) { + setExtendedFabVisibility(extendedFab, false) + } else if (scrollY < oldScrollY && !extendedFab.isShown) { + setExtendedFabVisibility(extendedFab, true) + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/util/KeyboardUtils.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/KeyboardUtils.kt new file mode 100644 index 0000000000000000000000000000000000000000..627dfe91bc4dca4cb2baefa89315a44ab930e6d5 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/KeyboardUtils.kt @@ -0,0 +1,23 @@ +package it.niedermann.owncloud.notes.shared.util + +import android.content.Context +import android.view.inputmethod.InputMethodManager +import android.widget.EditText + +object KeyboardUtils { + private const val SHOW_INPUT_DELAY_MILLIS = 100L + + @JvmStatic + fun showKeyboardForEditText(editText: EditText) { + editText.requestFocus() + // needs 100ms delay to account for focus animations + editText.postDelayed({ + val context = editText.context + if (context != null) { + val inputMethodManager = + context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) + } + }, SHOW_INPUT_DELAY_MILLIS) + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/util/SupportUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/SupportUtil.java index d914c13fa6a3ae0414e4ec43b871de2560af0e63..9c7abd8ca08fcd53a916329cc1870cf30c362587 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/util/SupportUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/SupportUtil.java @@ -33,6 +33,10 @@ public class SupportUtil { public static void setTextWithURL(@NonNull TextView textView, @NonNull Resources resources, @StringRes int containerTextId, @StringRes int linkLabelId, @StringRes int urlId) { final String linkLabel = resources.getString(linkLabelId); + setTextWithURL(textView, resources, containerTextId, linkLabel, urlId); + } + + public static void setTextWithURL(@NonNull TextView textView, @NonNull Resources resources, @StringRes int containerTextId, final String linkLabel, @StringRes int urlId) { final String finalText = resources.getString(containerTextId, linkLabel); final var spannable = new SpannableString(finalText); spannable.setSpan(new URLSpan(resources.getString(urlId)), finalText.indexOf(linkLabel), finalText.indexOf(linkLabel) + linkLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/util/rx/DisposableSet.kt b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/rx/DisposableSet.kt new file mode 100644 index 0000000000000000000000000000000000000000..12982ed64b789d67afeb7e8dbc980eb723e6361c --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/util/rx/DisposableSet.kt @@ -0,0 +1,16 @@ +package it.niedermann.owncloud.notes.shared.util.rx + +import io.reactivex.disposables.Disposable + +class DisposableSet { + private val disposables = mutableSetOf() + + fun add(disposable: Disposable) { + disposables.add(disposable) + } + + fun dispose() { + disposables.forEach { it.dispose() } + disposables.clear() + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java index 2cf36e42191dd8f84c3151dcf80c03e88104e2d4..d1cbe2fcf517a8891cea4fec7afad13c88492e30 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java @@ -143,6 +143,7 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { } @Override - public void applyBrand(int mainColor, int textColor) { + public void applyBrand(int color) { + // Nothing to do... } } diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable-v24/ic_launcher_background.xml similarity index 100% rename from app/src/main/res/drawable/ic_launcher_background.xml rename to app/src/main/res/drawable-v24/ic_launcher_background.xml diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 96540cd3c15d3d49214c6f3505b9ba1628e21be7..5d924aa236d2b6ee0c5388801eca8d5b93a89182 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,4 +1,3 @@ - + + diff --git a/app/src/main/res/drawable/ic_rich_editing.xml b/app/src/main/res/drawable/ic_rich_editing.xml new file mode 100644 index 0000000000000000000000000000000000000000..052c290f71b1b04065bcdbae97889956db4c69e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_rich_editing.xml @@ -0,0 +1,24 @@ + + + + diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index d8dc98fd3d9bcf66959e4bca61ee0729b8d250b0..19ea089d0299c90ad89a657d93186d8347c263bd 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -28,12 +28,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - @@ -42,6 +40,7 @@ + android:layout_height="wrap_content" + android:paddingHorizontal="@dimen/spacer_1x" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_edit.xml b/app/src/main/res/layout/activity_edit.xml index f7796f63839e1bab40f55b073137cc20a0d1cd8c..7f3046a82410d935d396f75d07b9ea09468f63ff 100644 --- a/app/src/main/res/layout/activity_edit.xml +++ b/app/src/main/res/layout/activity_edit.xml @@ -6,22 +6,13 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - + android:layout_height="wrap_content" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + tools:title="Edit Sample note" /> - - - - - + android:layout_height="wrap_content" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:title="@string/action_formatting_help" /> - - - - + android:layout_height="wrap_content" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:title="@string/manage_accounts" /> - + app:contentInsetStartWithNavigation="0dp"> - + - - + diff --git a/app/src/main/res/layout/activity_preferences.xml b/app/src/main/res/layout/activity_preferences.xml index 49e50dc4760ad6d1c201c206833146abfc8332ff..2bf99445392b0c24ebac054d98d644239144c5e2 100644 --- a/app/src/main/res/layout/activity_preferences.xml +++ b/app/src/main/res/layout/activity_preferences.xml @@ -5,22 +5,13 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - + android:layout_height="wrap_content" + app:navigationIcon="@drawable/ic_arrow_back_grey600_24dp" + app:title="@string/action_settings" /> + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_note_edit.xml b/app/src/main/res/layout/fragment_note_edit.xml index 1eafb0c885a7662224bf169516bdf88464017767..02498d7d00e837f6bb4d8c254685712212d8b36d 100644 --- a/app/src/main/res/layout/fragment_note_edit.xml +++ b/app/src/main/res/layout/fragment_note_edit.xml @@ -3,8 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - tools:background="?attr/colorPrimary"> + android:layout_height="match_parent"> - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/fragment_note_preview.xml b/app/src/main/res/layout/fragment_note_preview.xml index 7b9282a26f3c119a3e422a4e2672b648aa9f7a22..c1c19dc04681f63d6647e3fda500561878e4d089 100644 --- a/app/src/main/res/layout/fragment_note_preview.xml +++ b/app/src/main/res/layout/fragment_note_preview.xml @@ -3,8 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - tools:background="?attr/colorPrimary"> + android:layout_height="match_parent"> - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/item_notes_list_note_item_grid.xml b/app/src/main/res/layout/item_notes_list_note_item_grid.xml index 09c79ab15b697a66e6d04d351168806bb0dcb0f7..6421d60c71d6c4bddf17f821c5219c4e15afceeb 100644 --- a/app/src/main/res/layout/item_notes_list_note_item_grid.xml +++ b/app/src/main/res/layout/item_notes_list_note_item_grid.xml @@ -93,7 +93,7 @@ app:chipEndPadding="@dimen/spacer_1x" app:chipMinHeight="0dp" app:chipStartPadding="@dimen/spacer_1x" - app:chipStrokeColor="@color/defaultBrand" + app:chipStrokeColor="@android:color/transparent" app:chipStrokeWidth="1dp" app:ensureMinTouchTargetSize="false" app:textEndPadding="0dp" diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c0d35a64712e444690afa625ef0fca4ab5aaae0b..18ee65be2b46b1628bfcaa50a967d749752ef340 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,16 +1,15 @@ - الملاحظات - ملاحظات نيكست كلاود - جميع الملاحظات - - المفضلات - ملاحظة جديدة + الملاحظات Notes + ملاحظات Notes نيكست كلاود + جميع الملاحظات + المُفضّلة + ملاحظة Note جديدة مرحبًا بك في %1$s الإعدادات - مسح الملاحظات - البحث + ملاحظات محذوفة + بحث طريقة الترتيب إلغاء تبديل @@ -18,13 +17,13 @@ حذف حفظ عن - الرابط - حُذف \'%1$s\' + رابط + محذوف \'%1$s\' أُسترجع \'%1$s\' تراجع غير مصنف حذف - الفئة + التصنيف المفضلة معاينة شارك @@ -42,122 +41,151 @@ الشهر الماضي وضع العرض للملاحظات - المظهر - خط أحادي المسافة + السلوك عند فتح الملاحظات + الثيمة + خط font أحادي المسافة حجم الخط - المزامنة فقط على الوايفاي - بيانات اعتماد الجهاز - مزامنة في خلفية النظام + المزامنة فقط على الواي فاي + قفل التطبيق (بيتا) + حيثيات الدخول للجهاز + مزامنة في الخلفية + منع التقاط صورة للشاشة عرض كمخطط - المزامنة فشلت \'%1$s\' - المزامنة فشلت + حافظ على الشاشة قيد العمل + عند عرض أو تحرير ملاحظة + + إخفاق في المزامنة: \'%1$s\' + إخفاق في المزامنة لا يتوفر اتصال - الخادم في وضع الصيانة - حدث خطأ غير معروف. + الخادوم في وضع الصيانة + حدث خطأ غير مُحدّدٍ الإصدار - استهلكتَ حاليا %1$s - مشرف -  المطورون -  المترجِمون + أنت حاليّاً تستعمل %1$s + مُنسّق + المُطوّرون + الكاتب الأصلي + المترجِمون مجتمع نكست كلاود على %1$s -  المجَرِّبون - الشفرة المصدرية - هذا المشروع مستضاف على جيت هب: %1$s + المجَرِّبون + الكود المصدري + هذا المشروع مستضاف على Github: %1$s المشاكل - يمكنك الإبلاغ عن الأخطاء، ترشيح مزايا أو طلب تحسين على GitHub issue tracker:%1$s + يمكنك الإبلاغ عن الأخطاء، ترشيح مزايا أو طلب تحسينات على GitHub issue tracker:%1$s تَرجِم إنضم إلى فريق نكست كلاود على Transifex وساعد في ترجمة هذا التطبيق: %1$s -  رخصة التطبيق - التطبيق مسجل تحت شهادة جنو العامة الاصدار 3+ + رخصة التطبيق + التطبيق مسجل تحت شهادة GNU العامة الاصدار 3+ عرض الرخصة -  الأيقونات + الأيقونات لرؤية الأيقونة الأصلية %1$s. - جميع الأيقونات الأخرى التي يستخدمها هذا التطبيق %1$s من صنع Google Inc. ومرخصة بموجب ترخيص Apache 2.0. - مادة تصميم الأيقونات - الشكر ل - المساهمة -  الرخصة + جميع الأيقونات الأخرى التي يستخدمها هذا التطبيق %1$s هي من إنتاج Google Inc. ومرخصة بموجب ترخيص Apache 2.0. + أيقونات من طراز Material Design + شكرٌ و تقديرٌ + مساهمة + الرخصة قائمة الملاحظات لا ملاحظات - ملاحظة مفردة - - الملاحظة غير موجودة - - الرجاء تسجيل الدخول قبل إستخدامك لهذه الأداة - اختيار الملاحظة + ملاحظة مفردة + الملاحظة غير موجودة + الرجاء تسجيل الدخول إلى تطبيق الملاحظات Notes قبل إستخدامك لهذه الوَدْجَة Widget + أيقونة النجمة تستخدم لتسجيل عنصر كمُفضّل favorite + + إختيار ملاحظة انشاء ملاحظة جديدة عادي - مزامنة على الوايفاي و بيانات الهاتف - الحماية بكلمة السر + مزامنة على الواي فاي Wi-Fi، و بيانات الهاتف النقّال mobile data + محمي بكلمة مرور خطأ إغلاق - أنسخ + إنسخ استثناء - تثبيت الى الشاشة الرئيسية - هذه الملاحظة مُسحت + تثبيت في الشاشة الرئيسية + هذه الملاحظة تمّ مسحها أضف حسابا - الموسيقى - الأفلام + موسيقى + أفلام فلم - العمل + عمل + مَهَمّة + مَهَام + قائمة مراجعة المهام + وصفة أكل + وصفات أكل مطعم - كلمة السر - الكلمات السرية + مطاعم + طعام + خَبْز + + مفتاح + + مفاتيح + كلمة مرور + كلمات مرور + حيثيّات الدخول credentials لعبة - الألعاب + ألعاب تشغيل + هدية + هدايا - حاضر - تم بالفعل استيراد الحساب - لا ملاحظات حاليا - اضغط علامة + لانشاء ملاحظة جديدة + جائزة + + جوائز + + سبق استيراد الحساب + لا ملاحظات بعدُ + اضغط + لانشاء ملاحظة جديدة المزيد نقل - القراءة فقط + للقراءة فقط لا تصنيف - اضف %1$s - مربع + أضف %1$s + مربع اختيار فتح قفل الملاحظات آه لا، ماذا الان؟🙁 - الرجاء محاولة اجبار اغلاق التطبيق واعادة تشغيله، ربما هنالك خطأ في الاتصال مع تطبيق نيكست كلاود nextcloud + رجاءً، إفرض غلق التطبيق ثم أعد تشغيله، ربما هنالك خطأ في الاتصال مع تطبيق نيكست كلاود. اذا تكررت المشكلة، حاول تنظيف مساحة التخزين في التطبيقين كلاهما: نيكست كلاود nextcloud، نيكست كلاود ملاحظات nextcloud notes لحل هذه المشكلة. - تطبيقك نيكست كلاود (Nexcloud) يبدو انه مهمل (لم يحدث منذ زمن). رجاءا زُر بلي ستور او اف-درويد (f-droid) لتحصل على اخر اصدار. - يبدو أن هناك خطأ ما في تطبيق Nextcloud الخاص بك. يرجى محاولة فرض إيقاف كلا من تطبيق Nextcloud وتطبيق Nextcloud Notes. + يمكنك تنظيف ذاكرة التطبيق بفتح \"معلومات التطبيق\" app info ثم اختيار \"التخزين\" ثم \"محو التخزين\" Storage → Clear storage. +⚠️ تحذير: هذا سيؤدي إلى حذف الملاحظات التي لم تتم مزامنتها بعدُ. + تطبيقك نيكست كلاود يبدو انه لم يتم تحديثه منذ وقت طويل. رجاءً، قم بزيارةمتجر تطبيقات \"إف-درويد\" f-droid للحصول على آخر إصدار منه. + يبدو أن هناك خطأ ما في تطبيقك نكست كلاود. يرجى محاولة فرض إيقاف كلا التطبيقين: Nextcloud و Nextcloud Notes. إذا لم يساعد إيقافهم بالقوة ، فيمكنك محاولة مسح تخزين كلا التطبيقين. - لم يكن هناك استجابة من الخادم الخاص بك في الوقت المحدد. يرجى التأكد من أن الخادم الخاص بك يعمل بشكل جيد. - تحقق من اتصالك بالشبكة. أحياناُ، يمكن أن يساعد إيقاف بيانات الهاتف المحمول أو إيقاف تشغيل Wi-Fi وتشغيله مرة أخرى. - لم تكن استجابة الخادم الخاص بك صحيحة. يرجى التحقق مما إذا كان يمكنك الوصول إلى ملاحظاتك عبر واجهة الويب. - هناك مشكلة في إعداد Nextcloud الخاص بك. يرجى إلقاء نظرة على ملفات سجل الخادم. - يرجى التحقق مما إذا كان خادم Nextcloud الخاص بك ليس في وضع الصيانة حاليًا. - خادم Nextcloud الخاص بك ليس لديه مساحة تخزين فارغة متبقية. يرجى حذف بعض الملفات لمزامنة التغييرات المحلية الخاصة بك في السحابة الخاصة بك. + لم يكن هناك استجابة من خادومك في الوقت المحدد. يرجى التأكد من أنه يعمل بشكل جيد. + تحقق من اتصالك بالشبكة. أحياناُ، يمكن لإيقاف ثم إعادة تشغيل بيانات الهاتف المحمول أو الواي فاي Wi-Fi أن يحل المشكل. + لم تكن استجابة خادومك صحيحة. يرجى التحقق مما إذا كان يمكنك الوصول إلى ملاحظاتك عبر واجهة الويب. + هناك مشكلة في إعداد خادومك. يرجى مراجعة سجل حركات الخادوم log. + يرجى التحقق من أن خادومك ليس في وضع الصيانة حاليًا. + خادومك ليس لديه مساحة تخزين فارغة متبقية. يرجى حذف بعض الملفات لمزامنة تغييراتك المحلية في سحابتك. نحتاج إلى المعلومات الفنية التالية لمساعدتك: - يرجى التأكد من تثبيت وتمكين تطبيق \"Notes\" على خادمك. - استجاب خادمك برمز حالة HTTP 302 ، مما يعني أنك لم تقم بتثبيت تطبيق Notes على الخادم الخاص بك أو أنه تم تكوين شيء ما بشكل خاطئ. يمكن أن يحدث هذا بسبب التجاوزات المخصصة في ملف htaccess أو تطبيقات Nextcloud مثل عميل OID. + يرجى التأكد من تنصيب و تفعيل تطبيق \"Notes\" على خادومك. + استجاب خادومك برمز حالة HTTP 302 ، مما يعني أنك لم تقم بتثبيت تطبيق Notes عليه أو أنه تم تكوين شيء ما بشكل خاطئ. يمكن أن يحدث هذا بسبب بعض التخصيصات في ملف htaccess أو تطبيقات Nextcloud مثل عميل OID. يرجى تعطيل جميع تحسينات البطارية لـ Nextcloud وتطبيق Notes. + تطبيق الملاحظات Notes على الأندرويد يلزمه تطبيق Notes على نكست كلاود بإصدار لا يقل عن 3,18. \"%1$s\" أضيفت - كان النص المشارك فارغًا - إلحاق بالملاحظة - تغيير عنوان الملاحظة - تحرير العنوان + كان النصّ المُشارًك فارغًا + إلحاق بملاحظة + تغيير عنوان ملاحظة + تحرير عنوان الأمان + المظهر و التّصرُّف المزامنة إدارة الحسابات التنسيق - - فتح في وضع التحرير - فتح في وضع المعاينة - تذكر آخر ما أخترته - + + وضعية التحرير العادي plain + معاينة عادية + وضعية التحرير الغني rich + تذكر آخر ما اخترْتُهُ صغير @@ -167,7 +195,7 @@ فاتح - ليلي + داكن إفتراضي النظام @@ -177,62 +205,129 @@ %d محددان %d محددين %d محددين - %d محدد + %d مُحدّد + + + ملاحظات %1$d محذوفة + ملاحظة واحدة محذوفة + ملاحظات %1$d محذوفة + ملاحظات %1$d محذوفة + ملاحظات %1$d محذوفة + ملاحظات %1$d محذوفة + + + ملاحظات %1$d مستعادة + ملاحظة واحدة مستعادة + ملاحظات %1$d مستعادة + ملاحظات %1$d مستعادة + ملاحظات %1$d مستعادة + ملاحظات %1$d مستعادة + + مشاركة محتوي %1$d ملاحظات + مشاركة محتوي %1$d ملاحظة + مشاركة محتوي%1$dملاحظات + مشاركة محتوي %1$d ملاحظات + مشاركة محتوي %1$d ملاحظات + مشاركة محتوي %1$d ملاحظات + + --- `%1$s` \\`%1$s\\` ``` + ```` ```javascript - التنسيق القائم على السياق (المحتوى) - يتمثل أحد أهداف التصميم الرئيسية لتطبيق Notes في توفير أداة خالية من التشتيت. على الرغم من أنك ستتمكن من تنسيق النصوص الخاصة بك باستخدام Markdown. بالنسبة للعديد من الأمثلة المذكورة أدناه ، يمكنك استخدام الاختصارات حتى تتمكن من تنسيق ملاحظاتك دون كتابة الرموز أدناه. - ما عليك سوى تحديد نطاق من النص أو النقر على المؤشر في أي موضع وستحصل على قائمة منبثقة تحتوي على بجوار الإدخالات الافتراضية %1$s, %2$s, %3$s إدخالات مثل %4$s أو %5$s. + تنسيق بحسب السياق + يتمثل أحد أهداف التصميم الرئيسية لتطبيق Notes في توفير أداة خالية من التشتيت. على الرغم من أنك ستتمكن من تنسيق النصوص الخاصة بك باستخدام Markdown. بالنسبة للعديد من الأمثلة المذكورة أدناه ، يمكنك استخدام الاختصارات حتى تتمكن من تنسيق ملاحظاتك دون كتابة الرموز أدناه. + ما عليك سوى تحديد نطاق من النص أو النقر على المؤشر في أي موضع وستحصل على قائمة منبثقة تحتوي بالإضافة إلى مُداخلها الافتراضية %1$s, %2$s, %3$s مداخل مثل %4$s أو %5$s. نص - من السهل جدًا كتابة بعض الكلمات %1$sعريضة%1$s وبعض الكلمات %2$sمائلة%2$s بإستخدام Markdown. يمكنك %3$sشخط%3$s بعض الكلمات كما يمكنك أيضا [وصل بـ Nextcloud](https://nextcloud.com). + من السهل جدًا كتابة بعض الكلمات %1$sثخينة bold%1$s وبعض الكلمات %2$sمائلة italic%2$s بإستخدام تنسيق مارك داون Markdown. يمكنك %3$sشطب strike%3$s بعض الكلمات. [رابط إلى نكست كلاود] (https://nextcloud.com). قوائم - في بعض الأحيان تريد قوائم مرقمة: + أحياناً تريد قوائم مرقمة numbered lists: واحد إثنان ثلاثة - في بعض الأحيان تريد نقاطًا نقطية: - ابدأ سطرًا بشرطة - وإذا كانت لديك نقاط فرعية ، فضع مسافتين قبل الشرطة أو النجمة: - هكذا - وهكذا + في بعض الأحيان تريد بنوداً مُنقّطة bullet points: + ابدأ سطرًا بشَرْطَة + وإذا كانت لديك نقاط فرعية، فضع مسافتين قبل الشَرْطَة أو النجمة: + مثل هذا + و هذا مربعات الاختيار لإنشاء مربع اختيار ، استخدم قائمة متبوعة بأقواس عنصر 1 عنصر 2 - وثائق منظمة - من المفيد أحيانًا أن يكون لديك مستويات مختلفة من العناوين لبناء مستنداتك. تبدأ الأسطر ب %1$s لإنشاء العناوين. تعدد %2$s في صف يشير إلى أحجام عناوين أصغر. + مستندات مُهَيْكَلة + من المفيد أحيانًا أن يكون لديك مستويات مختلفة من العناوين لبناء مستنداتك. إبدإ الأسطر بـ %1$s لإنشاء العناوين. %2$s متعددة في سطر يشير إلى أحجام عناوين أصغر. هذا عنوان من الدرجة الثالثة يمكنك إستخدام واحد %1$s على طول حتى %2$s ستة لأحجام العناوين المختلفة. - إذا كنت ترغب في اقتباس أحد، فاستخدم الحرف %1$s قبل السطر: - الخيال أكثر أهمية من المعرفة. المعرفة محدودة. يطوق الخيال العالم. - - البرت اينشتاين + إذا كنت ترغب في الاقتباس من أحد، فاستخدم الحرف %1$s قبل السطر: + الخيال أكثر أهمية من المعرفة. المعرفة محدودة. الخيال يُطوّق العالم. + - ألبرت أينشتاين الرمز - هناك العديد من الطرق المختلفة لتصميم التعليمات البرمجية باستخدام Markdown. إذا كان لديك كتل تعليمات برمجية مضمنة ، فلفها في backticks ( ` ) : - يدعم Markdown أيضًا شيئًا يسمى سياج الكود ، والذي يسمح بخطوط متعددة بدون مسافة بادئة: - وإذا كنت ترغب في استخدام تمييز بناء الجملة، فقم بتضمين اللغة البرمجية: + هناك العديد من الطرق المختلفة لتصميم التعليمات البرمجية باستخدام تنسيق مارك داون Markdown. إذا كان لديك كتل من الكود، فلُفّها في backticks : + يدعم Markdown أيضًا شيئًا يسمى سياج الكود، والذي يسمح بخطوط متعددة بدون مسافة بادئة: + وإذا كنت ترغب في استخدام تمييز بناء الجملة syntax highlighting، فقم بتضمين اللغة: الجداول + + عمود %1d + + قيمة %1d + + يجب أن تكون الروابط إمّا عناوين URL كاملة تبدأ ببروتوكول و نطاق، أو مسارات مطلقة تبدأ بحرف + %1$s: + من أجل التوافق مع تنسيق Markdown، يرجى استخدام أحرف الهروب escape characters في عنوان URL للصورة. هذا يعني على سبيل المثال، استبدل المسافات بـ%1$s في الـ URL. صور + صورة خيالية + آخر - ترتيب حسب تاريخ التعديل + ترتيب حسب تاريخ آخر تعديل الترتيب حسب الأبجدية إعدادات البطارية - افتح معلومات التطبيق - اعدادات الشبكة + إفتح معلومات التطبيق + إعدادات الشبكة تحديث - لا يوجد لديك أي حسابات مكونة أخرى حتى الآن. - اختر حسابًا - يجب أن تكون متصلًا بالانترنت لإضافة حساب. + لم تتم تهيئة الحساب بعدُ + أنت لم تقم بتهيئة أي حسابات أخرى بعدُ. + إختر حسابًا + نافذة منبثقة للتنسيق القائم على السياق Context based formatting + + سوف تؤدي إزالة الحساب%1$s أيضًا إلى حذف التغييرات غير المتزامنة الغير قابلة للاسترداد %2$d. + سوف تؤدي إزالة الحساب%1$s أيضًا إلى حذف التغيير غير المتزامن الغير قابل للاسترداد. + سوف تؤدي إزالة الحساب%1$s أيضًا إلى حذف التغييرات غير المتزامنة الغير قابلة للاسترداد %2$d. + سوف تؤدي إزالة الحساب %1$s أيضًا إلى حذف التغييرات غير المتزامنة الغير قابلة للاسترداد %2$d. + سوف تؤدي إزالة الحساب%1$s أيضًا إلى حذف التغييرات غير المتزامنة الغير قابلة للاسترداد %2$d. + سوف تؤدي إزالة الحساب %1$s أيضًا إلى حذف التغييرات غير المتزامنة الغير قابلة للاسترداد %2$d. + + حذف %1$s + + يجب أن تكون متصلًا بالانترنت ليُمكنك إضاف حساب. + تعيين مُجلّد + التالي السابق النسخ الاحتياطي + اكتشفنا حالة غير قابلة للاسترداد للتطبيق. يرجى الاحتفاظ بنسخة احتياطية من التغييرات غير المتزامنة ومسح تخزين تطبيق Notes. + مُجلّد لتخزين ملاحظاتك في خادوم نكست كلاود + + .txt + .md + + مُجلّد ملاحظات جديدة: %1$s + إمتداد الملف + إمتداد الملف لملاحظاتك الجديدة في خادوم نكست كلاود + لاحقة الملف الجديدة suffix: %1$s + رمز حالة HTTP status code: %1$d + إستيراد ملاحظات ... + إستيراد الملاحظة %1$d من %2$d… + الحساب تمّ استيراده + خطأ في تحميل التحرير الغني rich editing + بدّل إلى التحرير العادي plain editing + عودة diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f1830550814eb46565e05b5597e8d689bd03d974 --- /dev/null +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -0,0 +1,310 @@ + + + + Notes + Nextcloud Notes + All notes + Favourites + New note + Welcome to %1$s + Settings + Deleted notes + Search + Sorting method + Cancel + Switch + Edit + Remove + Save + About + Link + Deleted %1$s + Restored %1$s + Undo + Uncategorised + Delete + Category + Favourite + Preview + Share + + Search in %1$s + Search all notes + + Choose a category + + Today + Yesterday + This week + Last week + This month + Last month + + Display mode for notes + Notes opening behaviour + Theme + Monospace font + Font size + Sync only on Wi-Fi + App lock (Beta) + Device credentials + Background synchronization + Prevent screen capture + Grid view + Keep screen on + When viewing or editing a note + + Synchronisation failed: %1$s + Synchronization failed + No network connection + Server is in maintenance mode + An unknown error has occurred. + + Version + You are currently using %1$s + Maintainer + Developers + original author + Translators + Nextcloud community on %1$s + Testers + Source code + This project is hosted on GitHub: %1$s + Issues + You can report bugs, enhancement proposals and feature requests at the GitHub issue tracker: %1$s + Translate + Join the Nextcloud team on Transifex and help us to translate this app: %1$s + App licence + This application is licensed under the GNU GENERAL PUBLIC LICENSE v3+. + View licence + Icons + For the original icon see %1$s. + All further icons used by this app are %1$s made by Google Inc. and licensed under the Apache 2.0 License. + Material Design Icons + Credits + Contribution + Licence + + Note list + No notes + Single note + Note not found + Please login to Notes before using this widget + Star icon is used to denote an item as a favourite + + Select note + + Create a new note + + Normal + Sync on Wi-Fi and mobile data + Password protection + + Error + Close + Copy + Exception + Pin to homescreen + This note has been deleted + Add account + + Music + Movies + Movie + Work + ToDo + ToDos + Checklists + Tasks + Recipe + Recipes + Restaurant + Restaurants + Food + Bake + + Key + + Keys + Password + Passwords + Credential + Game + Games + + Play + Gift + Gifts + + Present + + Presents + + Account has already been imported + No notes yet + Press + button to create a new note + More + Move + Read only + No category + Add %1$s + Checkbox + Unlock notes + Oh no - What now? 🙁 + Please try to force close the app and restart it again. There might have been an incorrect connection to the Nextcloud app. + If the issue persists, try to clear the storage of both apps: Nextcloud and Nextcloud Notes to solve this issue. + You can clear the storage by opening the app info and selecting Storage → Clear storage. ⚠️ Warning: This will delete not yet synchronized notes! + Your Nextcloud app seems to be outdated. Please visit the Play Store or F-Droid to get the latest version. + Something seems to be wrong with your Nextcloud app. Please try to force stop both, the Nextcloud app and the Nextcloud Notes app. + If force stopping them does not help, you can try to clear the storage of both apps. + There wasn\'t a response from your server in the given time. Please make sure your instance is running well. + Check your network connection. Sometime toggling the mobile data or Wi-Fi off and on again can help. + The response of your server was not correct. Please check whether you can access your notes via web interface. + There is an issue with your Nextcloud setup. Please have a look into the server log files. + Please check whether your Nextcloud instance is not currently in maintenance mode. + Your Nextcloud instance has no free storage left. Please delete some files to sync your local changes into your cloud. + We need the following technical information to help you: + Please make sure you have installed and enabled the "Notes" app on your server. + Your server did respond with a HTTP 302 status code, which implies, that you do not have installed the Notes app on your server or something is misconfigured. This can be caused by custom overrides in a .htaccess-file or by Nextcloud apps like OID Client. + Please disable all battery optimizations for Nextcloud and the Notes app. + The Notes Android app requires the Nextcloud Android app to be at least version 3.18. + Added "%1$s" + Shared text was empty + Append to note + Change note title + Edit title + Security + Appearance and behavior + Synchronization + Manage accounts + Formatting + + + Plain edit mode + Plain preview + Rich edit mode + Remember my last selection + + + Small + Medium + Large + + + + Light + Dark + System Default + + + + %d selected + %d selected + + + Deleted one note + Deleted %1$d notes + + + Restored one note + Restored %1$d notes + + + Share content of %1$d note + Share content of %1$d notes + + + --- + `%1$s` + \\`%1$s\\` + ``` + ```` + ```javascript + Context based formatting + A major design goal of the Notes app is to provide a distraction free tool. Though you will be able to format your texts with Markdown. For various of the below mentioned examples, you can use shortcuts so you can format your notes without typing in the codes below. + Just select a range of text or tap on your cursor at any position and you will get a popup menu which contains next to the default entries %1$s, %2$s, %3$s entries like %4$s or %5$s. + + Text + It\'s very easy to make some words %1$sbold%1$s and other words %2$sitalic%2$s with Markdown. You can %3$sstrike%3$s some words through and even [link to Nextcloud](https://nextcloud.com). + + Lists + Sometimes you want numbered lists: + One + Two + Three + Sometimes you want bullet points: + Start a line with a dash + And if you have sub points, put two spaces before the dash or star: + Like this + And this + + Checkboxes + To create a checkbox, use a list followed by brackets + Item 1 + Item 2 + + Structured documents + Sometimes it\'s useful to have different levels of headings to structure your documents. Start lines with a %1$s to create headings. Multiple %2$s in a row denote smaller heading sizes. + This is a third-tier heading + You can use one %1$s all the way up to %2$s six for different heading sizes. + If you\'d like to quote someone, use the %1$s character before the line: + Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world. + - Albert Einstein + + Code + There are many different ways to style code with Markdown. If you have inline code blocks, wrap them in backticks: + Markdown also supports something called code fencing, which allows for multiple lines without indentation: + And if you\'d like to use syntax highlighting, include the language: + + Tables + + Column %1d + + Value %1d + + Links have to be either complete URLs starting with a protocol and domain or absolute paths starting with a %1$s character. + In order to conform to the Markdown format, please use escape characters in the image URL. This means for example, replace spaces with %1$s in the URL. + Images + Fancy image + + Other + Sort by modified date + Sort by alphabet + Battery settings + Open App info + Network settings + Update + No account configured yet + You don\'t have configured any other accounts yet. + Choose account + Context based formatting popover + + Removing the account %1$s will also delete irrecoverable one unsynchronized change. + Removing the account %1$s will also delete irrecoverable %2$d unsynchronized changes. + + Remove %1$s + + You have to be connected to the internet in order to add an account. + Set folder + + Next + Previous + Backup + We detected an irrecoverably state of the app. Please backup your unsynchronized changes and clear the storage of the Notes app. + Folder to store your notes in your Nextcloud + + .txt + .md + + New notes folder: %1$s + File extension + File extension for new notes in your Nextcloud + New file suffix: %1$s + HTTP status code: %1$d + Importing notes… + Importing note %1$d of %2$d… + Account imported. + Error while loading rich editing + Switch to plain editing + Back + diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index c38873423b0e00d10dc2c251ecc37805c8cd96f4..3515961f16ed14a5ab5456bf0a9bf5806acec4f4 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -41,6 +41,7 @@ Миналия месец Режим на показване на бележки + Поведение при отваряне на бележките Тема Monospace/Монопространствен/ шрифт Размер на шрифта @@ -63,6 +64,7 @@ В момента използвате%1$s Отговорник Разработчици + оригинален автор Преводачи Nextcloud общност на %1$s Тестери @@ -177,11 +179,11 @@ Управление на профилите Форматиране - - Отваряне в режим за редактиране - Отваряне в режим на визуализация - Запаметяване на последният ми избор - + + Режим на обикновено редактиране + Обикновен преглед + Режим на богато редактиране + Запаметяване на последният ми избор Малък @@ -302,4 +304,7 @@ Импортиране на бележки... Импортиране на бележка%1$d от %2$d... Профилът е импортиран. - + Грешка при зареждане на богато редактиране + Превключване към обикновено редактиране + Назад + diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 7a1a01a2d79ef714e5f4a967b08019a062c8b6a6..56cabbfe865e64fd9f56e415e33f9c2571d14045 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -177,11 +177,8 @@ Gestiona els comptes Format - - Obre en mode d\'edició - Obre en mode de previsualització - Recorda la darrera selecció - + + Recorda la darrera selecció Petita @@ -299,7 +296,8 @@ Extensió de fitxer per a les notes noves al Nextcloud Sufix dels fitxers nous: %1$s Codi d\'estat HTTP: %1$d - S\'estan important les notes... - S\'està important la nota %1$d/%2$d... + S\'estan important les notes… + S\'està important la nota %1$d/%2$d… S\'ha importat el compte. - + Torna + diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index b713fdfd9314f9b9e1d64856af0a0a5ee60f5aff..7dfe70f4ca4a7fbc63d69789e569b6364a60d531 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -41,6 +41,7 @@ Minulý měsíc Režim zobrazení poznámek + Chování Poznámek při otevírání Motiv vzhledu Písmo se všemi znaky stejně širokými Velikost písmen @@ -63,6 +64,7 @@ Nyní používáte %1$s Vývoj spravuje Vývojáři + původní autor Překladatelé Nextcloud komunita na portálu %1$s Testeři @@ -177,11 +179,11 @@ Spravovat účty Formátování - - Otevřít v režimu úprav - Otevřít v náhledovém režimu - Zapamatovat si můj poslední výběr - + + Režim upravování neformátovaného textu + Náhled neformátovaného textu + Režim upravování formátovaného textu + Zapamatovat si můj poslední výběr Malé @@ -312,4 +314,7 @@ Importují se poznámky… Importuje se poznámka %1$d z %2$d… Účet naimportován. - + Chyba při načítání upravování formátovaného textu + Přepnout do upravování neformátovaného textu + Zpět + diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..12ef139d37a096686f9db0a545100eba56a7bcef --- /dev/null +++ b/app/src/main/res/values-da/strings.xml @@ -0,0 +1,237 @@ + + + + Noter + Nextcloud noter + Alle noter + Foretrukne + Ny note + Velkommen til %1$s + Indstillinger + Slettede noter + Søg + Sorteringsmetode + Annullér + Byt rundt + Rediger + Fjern + Gem + Om + Link + %1$s blev slettet + %1$s blev gendannet + Fortryd + Ikke kategoriseret + Slet + Kategori + Foretrukken + Vis eksempel + Del + + Søg i %1$s + Søg i alle noter + + Vælg en kategori + + I dag + I går + Denne uge + Seneste uge + Denne måned + Seneste måned + + Visningstilstand for noter + Ved åbning af note + Tema + Monospace font + Skriftstørrelse + Synkronisér kun på Wi-Fi + Enheds legitimationsoplysninger + Synkronisering i baggrunden + Forhindr skærmoptagelse + Grid visning + Hold skærmen tændt + Mens du læser eller redigerer en note + + Synkronisering fejlede: %1$s + Synkroniseringen mislykkedes + Ingen netværksforbindelse + Serveren er i vedligeholdelsestilstand + En ukendt fejl er opstået + + Version + Du bruger lige nu %1$s + Maintainer + Udviklere + original forfatter + Oversættere + Nextcloud fællesskabet på %1$s + Testere + Kildekode + Dette projekt hostes på GitHub: %1$s + Problemer + Du kan rapportere fejl, foreslå forbedringer og ønske nye funktioner på Github: %1$s + Oversæt + Bliv en del af Nextcloudholdet på Transifex, og hjælp os med at oversætte denne app: %1$s + App licens + Denne applikation er licenseret under GNU GENERAL PUBLIC LICENSE v3+. + Vis licens + Ikoner + For det oprindelige ikon, se %1$s. + Alle andre ikoner som anvendes af app\'en er %1$s lavet med Google Inc. og licensieret under et Apache 2.0-licens. + Material Design ikoner + Credits + Bidrag + Licens + + Noteliste + Ingen noter + Enkel note + Note ikke fundet + Log venligst ind i Notes før du bruger denne widget + Vælg note + + Opret en ny note + + Normal + Synkronisér på Wi-Fi og mobil data + Password protection + + Fejl + Luk + Kopiér + Undtagelse + Fastgør til hjemmeskærm + Denne note er blevet slettet + Tilføj konto + + Musik + Film + Film + Arbejde + Tjeklister + Opgaver + Opskrift + Opskrifter + Mad + Adgangskode + Adgangskoder + Spil + Spil + + Afspil + Gave + Gaver + Kontoen er allerede importeret + Ingen noter endnu + Tryk på + for at oprette en ny note + Mere + Flyt + Skrivebeskyttet + Ingen kategori + Tilføj %1$s + Afkrydsningsfelt + Lås noter op + Åh nej - hvad nu? 🙁 + Forsøg venligst at tvinge app\'en til at lukke og start den påny igen. Der kan have været en ukorrekt forbindelse til Nextcloud-app\'en. + Hvis problemet ikke løses, så forsøg at rydde lageret for begge apps: Nextcloud og Nextcloud Notes, for at løse problemet. + Din Nextcloud-app ser ud til at være forældet. Gå venligst til Play Butik eller F-Droid for at få seneste version. + Det lader til at være noget galt med din Nextcloud-app. Forsøg venligst at gennemtvinge stop af både Nextcloud-app\'en og Nextcloud Notes-app\'en. + Hvis det ikke hjælper at stoppe begge, så kan du forsøge at rydde lagerpladsen for begge apps. + Der var intet svar fra din server på nuværende tidspunkt. Sørg venligst for at sikre, at din instans kører rigtigt. + Tjek din netværksforbindelse. Undertiden kan det hjælpe at slukke og tænde for mobildata eller wifi. + Svaret fra din server var ikke korrekt. Tjek venligst om du kan tilgå dine noter via webgrænsefladen. + Der er et problem med din opsætning af Nextcloud. Kig venligst i serverens logfiler. + Sørg venligst for at din Nextcloud-instans ikke er i vedligeholdelsestilstand. + Din Nextcloud-instans har ikke mere ledig lagerplads. Slet venligst nogle filer for at synkronisere dine lokale ændringer ind i skyen. + Vi har brug for følgende tekniske information for at hjælpe dig: + Tjek venligst, om du har installeret og aktiveret appen \"Noter\" på din server. + Din server svarede med en HTTP 302-statuskode, hvilket betyder, at du endnu ikke har installeret app\'en Noter på din server, eller at der er en forkert konfiguration. Dette kan skyldes brugerdefinerede tilsidesættelser i en .htaccess-fil eller af apps fra Nextcloud som OID Client. + Tilføjet \"%1$s\" + Den delte tekst var tom + Tilføj til note + Skift titel på note + Redigér titel + Sikkerhed + Udseende og adfærd + Synkronisering + Administrér konti + Formatering + + + Husk mit sidste valg + + + Lille + Mellem + Stor + + + + Lys + Mørk + System Standard + + + + %d valgt + %d valgt + + + En note slettet + %1$d noter slettet + + + En note gendannet + %1$d noter gendannet + + + Del indhold i %1$d note + Del indhold i %1$d noter + + + --- + `%1$s` + ```javascript + Kontekstbaseret formatering + Tekst + Lister + En + To + Tre + Afkrydsningsfelter + Kode + Tabeller + + Kolonne %1d + + Værdi %1d + + Billeder + Andet + Sortér efter dato ændret + Sortér alfabetisk + Batteriindstillinger + Åbn App info + Netværksindstillinger + Opdatér + Vælg konto + Fjern %1$s + + Du skal være koblet på en internetforbindelse for at tilføje en konto. + Sæt mappe + + Næste + Tidligere + Backup + Mappe for dine noter i Nextcloud + + .txt + .md + + Mappe for nye noter: %1$s + Fil-endelse + Fil-endelse for nye noter i Nextcloud + Importerer noter... + Tilbage + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9ff8b266ab13aa5d9bf2dde9bc458b58aa2e96d3..e67814c41f3d576f375acf329ef80bea5f65c61c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -36,7 +36,8 @@ Diesen Monat Letzten Monat Anzeigemodus für Notizen - Thema + Verhalten beim Öffnen von Notizen + Design Nichtproportionale Schriftart Schriftgröße Nur über WLAN synchronisieren @@ -50,6 +51,8 @@ Synchronisierung fehlgeschlagen Server befindet sich im Wartungsmodus Es ist ein unbekannter Fehler aufgetreten. + + Ursprünglicher Autor Für das ursprüngliche Symbol siehe %1$s. Alle weiteren Symbole dieser App sind %1$s, erstellt von Google Inc. und unter der Apache 2.0-Lizenz lizenziert. Material Design Icons @@ -164,11 +167,12 @@ Synchronisierung Konten verwalten Formatierung - - Im Bearbeitungsmodus öffnen - Im Vorschaumodus öffnen - Letzte Auswahl merken - + + Einfacher Bearbeitungsmodus + Einfache Vorschau + Rich-Bearbeitungsmodus + Letzte Auswahl merken + Klein Mittel @@ -203,7 +207,8 @@ ```javascript Kontextbasierte Formatierung Ein wichtiges Designziel der Notes-App ist die Bereitstellung eines ablenkungsfreien Werkzeugs. Sie können Ihre Texte mit Markdown formatieren. Für verschiedene der unten genannten Beispiele können Sie Verknüpfungen verwenden, um Ihre Notizen zu formatieren, ohne die folgenden Codes eingeben zu müssen. - Wählen Sie einfach einen Textbereich aus oder tippen Sie an einer beliebigen Stelle auf den Cursor und Sie erhalten ein Popup-Menü, das neben den Standardeinträgen %1$s, %2$s, %3$s auch Einträge wie %4$s oder %5$s enthält. + Wählen Sie einfach einen Textbereich aus oder tippen Sie an einer beliebigen Stelle auf den Cursor und Sie erhalten ein Pop-up-Menü, das neben den Standardeinträgen %1$s, %2$s, %3$s auch Einträge wie %4$s oder %5$s enthält. + Text Es ist sehr einfach, einige Wörter %1$sfett%1$s und andere Wörter %2$skursiv%2$s mit Markdown zu versehen. Sie können einige Wörter %3$sdurchstreichen%3$s und sogar zu Nextcloud [verlinken](https://nextcloud.com). Listen @@ -250,7 +255,7 @@ Bislang kein Konto eingerichtet Sie haben bislang keine weiteren Konten eingerichtet. Konto auswählen - Popup für die kontextbasierte Formatierung + Pop-up für die kontextbasierte Formatierung Beim Entfernen des Kontos %1$s wird auch eine unsynchronisierte Änderung unwiderruflich gelöscht werden. Beim Entfernen des Kontos %1$s werden auch %2$d unsynchronisierte Änderungen unwiderruflich gelöscht werden. @@ -291,4 +296,7 @@ \n***Notiz %1$d (Zuletzt verändert: %2$s):*** \n" # Es gibt einen Konflikt zwischen Ihren bestehenden Notizen. Bitte wählen Sie die richtige aus. - \ No newline at end of file + Fehler beim Laden des Rich-Bearbeitungsmodus + Zum einfachen Bearbeitungsmodus wechseln + Zurück + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index d4e144ca508332302d589f4d86f22e4cd040ea0b..8df521f3646054225e0b8db2251b74af6fe011f5 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -49,6 +49,7 @@ Διαπιστευτήρια συσκευής Συγχρονισμός στο παρασκήνιο Προβολή πλέγματος + Διατήρηση της οθόνης ενεργή Αποτυχία συγχρονισμού: %1$s Αποτυχία συγχρονισμού Δεν υπάρχει σύνδεση στο δίκτυο @@ -56,9 +57,10 @@ Προέκυψε άγνωστο σφάλμα. Έκδοση - Αυτήν την στιγμή χρησιμοποιείτε %1$s + Αυτή τη στιγμή χρησιμοποιείτε %1$s Συντηρητής Προγραμματιστές + αρχικός συγγραφέας Μεταφραστές Η κοινότητα του Nextcloud στο %1$s Δοκιμαστές @@ -107,14 +109,24 @@ Ταινία Εργασία Εργασίες + Συνταγή Συνταγές Εστιατόριο + Εστιατόρια + Φαγητό + + Κλειδί + + Κλειδία Συνθηματικό Συνθηματικά + Διαπιστευτήρια Παιχνίδι Παιχνίδια Αναπαραγωγή + Δώρο + Δώρα Παρόν Ο λογαριασμός έχει εισαχθεί ήδη @@ -155,11 +167,8 @@ Διαχείριση λογαριασμών Διαμόρφωση σε εξέλιξη - - Άνοιγμα σε λειτουργία τροποποίησης - Άνοιγμα σε λειτουργία προεπισκόπισης - Να θυμάσαι την τελευταία μου επιλογή - + + Να θυμάσαι την τελευταία μου επιλογή Μικρό @@ -252,8 +261,27 @@ Δεν έχετε ρυθμίσει ακόμη άλλους λογαριασμούς. Επιλογή λογαριασμού Μορφοποίηση βάσει περιεχομένου popover + + Η αφαίρεση του λογαριασμού %1$sθα διαγράψει μόνιμα μια μη συγχρονισμένη αλλαγή. + Η αφαίρεση του λογαριασμού %1$s θα διαγράψει μόνιμα %2$dμη συγχρονισμένες αλλαγές. + + Αφαίρεση%1$s + Πρέπει να είστε συνδεμένοι στο ιντερνετ για να προσθέσετε λογαριασμό. Επόμενο Προηγούμενο Αντίγραφο ασφαλείας + Φάκελος για την αποθήκευση σημειώσεων στο Nextcloud + + .txt + .md + + Φάκελος νέων σημειώσεων: %1$s + Προέκταση αρχείου + Εισαγωγή σημειώσεων... + Γίνεται εισαγωγή της σημείωσης %1$d από %2$d... + Έγινε εισαγωγή του Λογαριασμού + Σφάλμα κατά την φόρτωση της πλούσιας επεξεργασίας + Αλλαγή σε απλή επεξεργασία + Πίσω diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..3464d81c226e96f1ae058c99e672b48362c74a19 --- /dev/null +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -0,0 +1,315 @@ + + + + Notas + Nextcloud Notes + Todas las notas + Favoritos + Nota nueva + Bienvenido a %1$s + Configuraciones + Notas borradas + Buscar + Método de ordenación + Cancelar + Cambiar + Editar + Eliminar + Guardar + Acerca de + Liga + %1$s eliminado + %1$s restaurado + Deshacer + Sin categoría + Borrar + Categoría + Favorito + Vista previa + Compartir + + Buscar en %1$s + Buscar todas las notas + + Elige una categoría + + Hoy + Ayer + Esta semana + La semana pasada + Este mes + El mes pasado + + Modo de visualización de las notas + Comportamiento de apertura de las notas + Tema + Fuente monoespaciada + Tamaño de fuente + Sincronizar solo con Wi-Fi + Bloqueo de la aplicación (Beta) + Credenciales del dispositivo + Sincronización en segundo plano + Evitar la captura de pantalla + Vista de cuadrícula + Mantener la pantalla encendida + Cuando se visualiza o edita una nota + + Falló la sincronización: %1$s + Error de sincronización + No hay conexión de red + El servidor está en modo de mantenimiento + Ha ocurrido un error desconocido. + + Versión + Actualmente estás usando %1$s + Equipo de mantenimiento + Desarrolladores + autor original + Traductores + Comunidad de Nextcloud en %1$s + Equipo de pruebas + Código fuente + Este proyecto está alojado en GitHub: %1$s + Temas + Puedes informar errores, propuestas de mejora y solicitudes de funciones en el rastreador de problemas de GitHub: %1$s + Traducir + Únete al equipo de Nextcloud en Transifex y ayúdanos a traducir esta aplicación: %1$s + Licencia de la aplicación + Esta aplicación está licenciada bajo la LICENCIA GENERAL PUBLICA GNU v3+ + Ver licencia + Íconos + Para el ícono original, consulta %1$s. + Todos los demás íconos utilizados por esta aplicación son %1$s creados por Google Inc. y con licencia Apache 2.0. + Material Design Icons + Créditos + Contribución + Licencia + + Lista de notas + No hay notas + Única nota + Nota no encontrada + Por favor inica sesión en Notes antes de usar este widget + El icono de estrella se utiliza para indicar un elemento como favorito + + Seleccionar nota + + Crear una nota nueva + + Normal + Sincronizar con Wi-Fi y datos móviles + Protección con contraseña + + Error + Cerrar + Copiar + Excepción + Anclar a la pantalla de inicio + Esta nota ha sido eliminada + Agregar cuenta + + Música + Películas + Película + Trabajo + Pendiente + Pendientes + Listas de verificación + Tareas + Receta + Recetas + Restaurante + Restaurantes + Comida + Hornear + + Llave + + Llaves + Contraseña + Contraseñas + Credencial + Juego + Juegos + + Reproducir + Regalo + Regalos + + Presente + + Regalos + + La cuenta ya ha sido importada + Aún no hay notas + Pulsa el botón + para crear una nueva nota + Más + Mover + Sólo lectura + Sin categoría + Agregar %1$s + Casilla de verificación + Desbloquear notas + ¡Oh no! ¿Y ahora? 🙁 + Por favor, intenta cerrar forzosamente la aplicación y reiniciarla nuevamente. Puede haber habido una conexión incorrecta con la aplicación Nextcloud. + Si el problema persiste, intenta borrar el almacenamiento de ambas aplicaciones: Nextcloud y Nextcloud Notes para solucionar este problema. + Puedes borrar el almacenamiento abriendo la información de la aplicación y seleccionando Almacenamiento → Borrar almacenamiento. ⚠️ Advertencia: ¡Esto eliminará las notas aún no sincronizadas! + Tu aplicación de Nextcloud parece estar desactualizada. Visita Play Store o F-Droid para obtener la última versión. + Algo parece estar mal con tu aplicación de Nextcloud. Intenta detener forzosamente tanto la aplicación de Nextcloud como la aplicación de Nextcloud Notes. + Si detenerlas forzosamente no ayuda, puedes intentar borrar el almacenamiento de ambas aplicaciones. + No hubo respuesta de tu servidor en el tiempo indicado. Asegúrate de que tu instancia esté funcionando correctamente. + Verifica tu conexión de red. A veces, activar y desactivar los datos móviles o Wi-Fi puede ayudar. + La respuesta de tu servidor no fue correcta. Verifica si puedes acceder a tus notas a través de la interfaz web. + Hay un problema con tu configuración de Nextcloud. Por favor, revisa los archivos de registro del servidor. + Por favor, verifica si tu instancia de Nextcloud no está actualmente en modo de mantenimiento. + Tu instancia de Nextcloud no tiene espacio de almacenamiento libre. Por favor, elimina algunos archivos para sincronizar tus cambios locales en tu nube. + Necesitamos la siguiente información técnica para ayudarte: + Asegúrate de haber instalado y habilitado la aplicación \"Notes\" en tu servidor. + Tu servidor respondió con un código de estado HTTP 302, lo que implica que no tienes instalada la aplicación Notes en tu servidor o algo está mal configurado. Esto puede ser causado por anulaciones personalizadas en un archivo .htaccess o por aplicaciones de Nextcloud como OID Client. + Desactiva todas las optimizaciones de batería para Nextcloud y la aplicación de Notas. + La aplicación Notas de Android requiere que la aplicación de Nextcloud para Android sea al menos la versión 3.18. + Añadido \"%1$s\" + El texto compartido estaba vacío + Anexar a la nota + Cambiar el título de la nota + Editar título + Seguridad + Apariencia y comportamiento + Sincronización + Administrar cuentas + Formato + + + Modo de edición simple + Vista previa simple + Modo de edición enriquecida + Recordar mi última selección + + + Pequeño + Medio + Grande + + + + Claro + Oscuro + Configuración predeterminada del sistema + + + + %d seleccionado + %d seleccionados + %d seleccionados + + + Eliminada una nota + Eliminadas %1$d notas + Eliminadas %1$d notas + + + Restaurada una nota + Restauradas %1$d notas + Restauradas %1$d notas + + + Compartir contenido de %1$d nota + Compartir contenido de %1$d notas + Compartir contenido de %1$d notas + + + --- + `%1$s` + \\`%1$s\\` + ``` + ```` + ```javascript + Formato contextual basado en el contexto + Uno de los principales objetivos de diseño de la aplicación Notes es proporcionar una herramienta libre de distracciones. Aunque podrás dar formato a tus textos con Markdown. Para varios de los ejemplos mencionados a continuación, puedes utilizar atajos para dar formato a tus notas sin escribir los códigos a continuación. + Solo selecciona un rango de texto o toca tu cursor en cualquier posición y aparecerá un menú emergente que contiene, además de las entradas predeterminadas %1$s, %2$s, %3$s, entradas como %4$s o %5$s. + + Texto + Es muy fácil hacer que algunas palabras estén %1$s negrita%1$s y otras palabras %2$s cursiva%2$s con Markdown. Puedes %3$stachar%3$s algunas palabras e incluso [enlazar a Nextcloud](https://nextcloud.com). + + Listas + A veces, quieres listas numeradas: + Uno + Dos + Tres + A veces, quieres puntos: + Empieza una línea con un guión + Y si tienes puntos secundarios, coloca dos espacios antes del guión o del asterisco: + Así + Y esto + + Casillas de verificación + Para crear una casilla de verificación, utiliza una lista seguida de corchetes + Elemento 1 + Elemento 2 + + Documentos estructurados + A veces es útil tener diferentes niveles de encabezados para estructurar tus documentos. Comienza las líneas con un %1$s para crear encabezados. Múltiples %2$s seguidos indican tamaños de encabezado más pequeños. + Este es un encabezado de tercer nivel + Puedes utilizar un %1$s desde uno hasta %2$s seis para diferentes tamaños de encabezado. + Si deseas citar a alguien, utiliza el carácter %1$s antes de la línea: + La imaginación es más importante que el conocimiento. El conocimiento está limitado. La imaginación rodea el mundo. + - Albert Einstein + + Código + Hay muchas formas diferentes de estilizar el código con Markdown. Si tienes bloques de código en línea, colócalos entre comillas invertidas: + Markdown también admite algo llamado cercas de código, que permite múltiples líneas sin sangría: + Y si deseas utilizar resaltado de sintaxis, incluye el lenguaje: + + Tablas + + Columna %1d + + Valor %1d + + Los enlaces deben ser URL completas que comiencen con un protocolo y un dominio o rutas absolutas que comiencen con un carácter %1$s. + Para cumplir con el formato Markdown, utiliza caracteres de escape en la URL de la imagen. Esto significa, por ejemplo, reemplazar los espacios con %1$s en la URL. + Imágenes + Imagen sofisticada + + Otro + Ordenar por fecha de modificación + Ordenar por orden alfabético + Configuración de la batería + Abrir información de la aplicación + Configuración de red + Actualizar + Aún no se ha configurado ninguna cuenta + Aún no has configurado ninguna otra cuenta. + Elige la cuenta + Popover de formato contextual + + Eliminar la cuenta %1$s también eliminará un cambio no sincronizado irrecuperable. + Eliminar la cuenta %1$s también eliminará %2$d cambios no sincronizados irrecuperables. + Eliminar la cuenta %1$s también eliminará %2$d cambios no sincronizados irrecuperables. + + Eliminar %1$s + + Debes estar conectado a Internet para agregar una cuenta. + Establecer carpeta + + Siguiente + Previo + Copia de seguridad + Detectamos un estado irreparable de la aplicación. Realiza una copia de seguridad de los cambios no sincronizados y borra el almacenamiento de la aplicación Notes. + Carpeta para almacenar tus notas en tu Nextcloud + + .txt + .md + + Nueva carpeta de notas: %1$s + Extensión de archivo + Extensión de archivo para nuevas notas en tu Nextcloud + Nuevo sufijo de archivo: %1$s + Código de estado HTTP: %1$d + Importando notas... + Importando nota %1$d de %2$d... + Cuenta importada. + Error al cargar la edición enriquecida + Cambiar a edición simple + Atrás + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index fc59992a1973e8f0633351f2cd1750421b5be855..1ec2301ec9b715c3bd9e8c12a9b7e95f99d3ad1d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -36,6 +36,7 @@ Este mes El mes pasado Modo de visualización para notas + Comportamiento de apertura de las notas Tema Tipo de letra monoespaciado Tamaño de fuente @@ -56,6 +57,7 @@ Esta actualmente usando <strong>%1$s</strong> Mantenedor Desarrolladores + autor original Traductores Comunidad de Nextcloud en <a href=\"%1$s\">Transifex</a> Probadores @@ -149,9 +151,9 @@ Su instancia de Nextcloud no tiene espacio libre de almacenamiento. Por favor elimine algunos archivos para sincronizar sus cambios locales con su nube. Necesitamos la siguiente información técnica para ayudarle: Por favor, asegúrate de que has instalado y activado la app «Notas» en tu servidor. - Tu servidor respondió con el código de estado HTTP 302, lo que implica que no está instalada la aplicación Notas en su servidor o que algo está mal configurado. Esto puede estar causado por anulaciones personalizadas en un archivo .htaccess o por aplicaciones de Nextcloud como OID Client. + Su servidor respondió con el código de estado HTTP 302, lo que implica que no está instalada la aplicación Notas en su servidor o que algo está mal configurado. Esto puede estar causado por anulaciones personalizadas en un archivo .htaccess o por aplicaciones de Nextcloud como OID Client. Por favor, desactiva todas las optimizaciones de la batería para Nextcloud y la app de Notas. - La aplicación de Android Notas necesita la versión 3.18 o superior de la aplicación Android Nextcloud + La aplicación de Notas de Android requiere que la la aplicación Nextcloud de Android sea al menos la versión 3.18. Se ha añadido «%1$s» El texto compartido estaba vacío Añadir a la nota @@ -162,11 +164,12 @@ Sincronización Gestionar cuentas Formato - - Abrir en modo edición - Abrir en modo visualización - Recordar mi última selección - + + Modo de edición plano + Vista previa en plano + Modo de edición enriquecido + Recordar mi última selección + Pequeña Media @@ -291,4 +294,7 @@ Notas de información Crear una nota nueva Seleccionar nota - \ No newline at end of file + Error al cargar el modo de edición enriquecido + Cambiar a edición plana + Atrás + diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 5408d534186d9762c50ef5fedaaf1bf669ee0a53..2dc19ed4df18ee572d983ed7abed814ba0c22136 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -17,8 +17,8 @@ Gorde Honi buruz Esteka - %1$s ezabatuta - %1$szaharberrituta + %1$s ezabatu da + %1$sberreskuratu da Desegin Kategoria gabe Ezabatu @@ -28,7 +28,7 @@ Partekatu Bilatu %1$s(e)n - Bilatu nota guztiak + Bilatu ohar guztiak Hautatu kategoria bat Gaur @@ -38,11 +38,12 @@ Erakutsi oharren modua Itxura iluna + Oharrak irekitzeko portaera Sinkronizatu wifi bidez bakarrik Aplikazioen blokeoa (Beta) Gailuaren kredentzialak Atzeko planoko sinkronizazioa - Saihestu pantaila argazkia + Eragotzi pantaila argazkia Sareta ikuspegia Mantendu pantaila piztuta Nota bat ikusi edo editatzean @@ -57,6 +58,7 @@ <strong>%1$s</strong> erabiltzen ari zara Mantentzailea Garatzaileak + jatorrizko egilea Itzultzaileak Nextcloud komunitatea <a href=\"%1$s\">Transifex</a> zerbitzuan Probatzaileak @@ -68,11 +70,11 @@ Elkartu Nexcloud taldera Transifex zerbitzuan eta lagundu aplikazio hau itzultzen: <a href=\"%1$s\">%1$s</a> Aplikazio lizentzia Aplikazio hau GNU GENERAL PUBLIC LICENSE v3+ lizentziapean dago. - Lizentzia ikusi - Irudiak + Ikusi lizentzia + Ikonoak Jatorrizko ikonora jotzeko ikusi %1$s. App honetan erabilitako gainontzeko ikonoen egilea %1$s Google Inc. da eta Apache 2.0 lizentzia daukate. - Material Design ikonoak + Material Design Icons Kredituak Kontribuzioak Lizentzia @@ -80,7 +82,7 @@ Oharrik ez Ohar bakarra Oharra ez da aurkitu - Hasi saioa oharrak aplikazioan trepeta hau erabili aurretik + Hasi saioa Oharrak aplikazioan trepeta hau erabili aurretik Izarraren ikonoa elementu bat gogoko gisa adierazteko erabiltzen da Hautatu oharra @@ -110,22 +112,22 @@ Jatetxea Jatetxeak Janaria - Egosi + Kozinatu Gakoa Gakoak Pasahitza Pasahitzak - Kredentzial + Kredentziala Jokoa Jokoak - Erreproduzitu + Jokatu Oparia Opariak - Aurkeztu + Oparia Opariak @@ -138,11 +140,11 @@ Kategoria gabe Gehitu %1$s Kontrol laukia - Desblokeatzearen oharrak - Oh ez - Orain zer? 🙁 + Desblokeatu oharrak + Oh ez - Eta orain zer? 🙁 Saia zaitez aplikazioa itxi eta berrabiarazten. Agian Nextcloud aplikazioarekin gaizki konektatzea gertatu da. Arazoak jarraitzen badu, saiatu bi aplikazioen biltegiak garbitzen: Nextcloud eta Nextcloud Oharrak aplikazioen biltegiak. - Biltegia garbi dezakezu aplikazioaren informazioa ireki eta hautatuz: Biltegia → Garbitu biltegia. ⚠️Abisua: Honek oraindik sinkronizatuta ez dauden oharrak ezabatuko ditu! + Biltegia garbi dezakezu aplikazioaren informazioa ireki eta hautatuz: Biltegia → Garbitu biltegia. ⚠️ Abisua: Honek oraindik sinkronizatuta ez dauden oharrak ezabatuko ditu! Daukazun Nextcloud aplikazioa zaharkituta dagoela dirudi. Zoaz Play Store edo F-Droidera azken bertsioa lortzeko. Daukazun Nextcloud aplikazioan zerbait gaizki dabilela dirudi. Saia zaitez Nextcloud aplikazioa eta Nextcloud Oharrak, biak, geldiarazten. Geldiarazteak ez badu arazoa konpontzen, bi biltegiak garbitzen saia zaitezke. @@ -153,14 +155,14 @@ Egiazta ezazu zure Nextcloud instantzia ez dagoela mantentze moduan une honetan. Zure Nextcloud instantziak ez du biltegiratze leku libre gehiagorik. Ezabatu fitxategi batzuk, aldaketa lokalak hodeiarekin sinkroniza daitezen. Zuri laguntzeko hurrengo informazio teknikoa behar dugu: - Mesedez egiaztatu \"Notak\" aplikazioa instalatu eta gaitu duzula zure zerbitzarian. - Zure zerbitzariak HTTP 302 egoera kodearekin erantzun du, honek esan nahi du ez duzula Notak aplikazioa ez duzula zure zerbitzarian instalatu edo zerbait txarto konfiguratuta dagoela. Hau .htaccess fitxategi batean arau pertsonalizatuak daudelako edo OID Bezeroa izeneko aplikazioarengatik sortua izan daiteke. + Egiaztatu \"Oharrak\" aplikazioa instalatuta eta gaituta duzula zure zerbitzarian. + Zure zerbitzariak HTTP 302 egoera kodearekin erantzun du, honek esan nahi du ez duzula Oharrak aplikazioa zure zerbitzarian instalatu edo zerbait txarto konfiguratuta dagoela. Hau .htaccess fitxategi batean arau pertsonalizatuak daudelako edo OID Bezeroa izeneko aplikazioarengatik sortua izan daiteke. Desgaitu bateria optimizazio guztiak Nextcloudentzat eta Oharrak aplikazioarentzat. - The Notes Android aplikazioak eskatzen du gutxienez Nextcloud Android aplikazioaren 3.18 bertsioa izatea. + Notes Android aplikazioak behar du Nextcloud Android aplikazioaren 3.18 bertsioa izatea gutxienez. \"%1$s\" gehituta Partekatutako testua hutsik zegoen - Gehitu notara - Aldatu notaren titulua + Gehitu oharrari + Aldatu oharraren titulua Editatu izenburua Segurtasuna Itxura eta portaera @@ -168,11 +170,11 @@ Kudeatu kontuak Formatua - - Ireki edizio moduan - Ireki aurrebista moduan - Gogoratu nire azken hautaketa - + + Edizio modu arrunta + Aurrebista arrunta + Edizio modu aberatsa + Gogoratu nire azken hautaketa Txikia @@ -200,7 +202,7 @@ Partekatu %1$doharraren edukia - Partekatu %1$doharraren edukia + Partekatu %1$doharren edukia --- @@ -210,33 +212,33 @@ ```` ```javascript Testuinguruan oinarritutako formatu ematea - Oharrak aplikazioaren diseinuaren erronka handi bat arreta-galtzerik gabeko tresna bat eskaintzea da. Baina aukera dago Markdown erabiliz testuei formatua emateko. Behean adierazitako adibide ugaritarako, lasterbideak erabil daitezke oharrei formatua emateko beheko kodeak idatzi gabe. + Oharrak aplikazioko diseinuaren erronka handi bat arreta-galtzerik gabeko tresna eskaintzea da. Baina aukera dago Markdown erabiliz testuei formatua emateko. Behean adierazitako adibide anitzetan, lasterbideak erabil daitezke oharrei formatua emateko beheko kodeak idatzi gabe. Testuaren zati bat hautatu edo sakatu kurtsorea edozein tokitan eta popup menua agertuko zaizu, %1$s, %2$s eta %3$s bezalako sarrera lehenetsien ondoan %4$seta%5$s bezalakoak ere badituena. - Testu + Testua Oso erraza da hitz batzuk %1$slodi%1$s jartzea eta beste batzuk %2$setzatea%2$s Markdown erabiliz. Hitz batzuk %3$smarratu%3$s ditzakezu eta baita estekak gehitu ere [Nextcloudera joateko](https://nextcloud.com). Zerrendak - Batzuetan zerrenda zenbatuak nahi dituzu: + Batzuetan zerrenda zenbakidunak nahi dituzu: Bat Bi Hiru Noizean behin bulet-puntuak jarri nahi izaten dira: - Lerro bat marratxo batekin hastea - Eta azpipuntuak izanez gero, utzi bi tarte marratxoa edo izarraren aurretik: + Lerro bat marratxo batekin hasi + Eta azpipuntuak nahi izanez gero, utzi bi tarte marratxoa edo izarraren aurretik: Honela - eta horrela + eta honela Kontrol-laukiak - Egiaztapen-laukitxo bat sortzeko, erabili zerrenda bat eta paréntesis. + Kontrol-lauki bat sortzeko, erabili zerrenda bat eta kortxeteak. 1 elementua 2 elementua Egitura duten dokumentuak Noizean behin erabilgarria izaten da maila uagriko izenburuak idaztea dokumentuei egitura emateko. Hasi lerroak %1$sbatekin izenburuak sortzeko. %2$s batzuk jartzeak tamaina txikiagoko izenburuak sortzen ditu. Hau hirugarren mailako izenburu bat da - %1$seta %2$sbitartean sei izenburu tamaina ezberdin erabil ditzakezu. - Norbait aipatzekotan, erabili %1$s karakterea lerroaren aurretik: + %1$sbatetik %2$ssei arteko izenburu tamaina ezberdinak erabil ditzakezu. + Norbait aipatzeko, erabili %1$s karakterea lerroaren aurretik: Irudimena ezagutza baino garrantzitsuagoa da. Ezagutza mugatua da. Irudimenak munduari buelta ematen dio. - Albert Einstein @@ -251,8 +253,8 @@ %1dbalioa - Estekak protokolo eta domeinuarekin hasten diren URL oso edo %1$s karakterearekin hasten diren bide-izen absolutuak izan behar dira. - Markdown formatua betetzeko, mesedez, erabili ihespen karaktereak irudiaren URLan. Hau da, ordeztu tarteak %1$s karakterearekin URLan. + Estekak protokolo eta domeinuarekin hasten diren URL osoak edo %1$s karakterearekin hasten diren bide-izen absolutuak izan behar dira. + Markdown formatua betetzeko, mesedez, erabili ihespen karaktereak irudiaren URLan. Hau da, adibidez ordeztu tarteak %1$s karakterearekin URLan. Irudiak Irudi dotorea @@ -264,30 +266,30 @@ Sareko ezarpenak Eguneratu Oraindik ez da konturik konfiguratu - Oraindik ez duzu konturik konfiguratu. + Oraindik ez duzu beste konturik konfiguratu. Aukeratu kontua Testuinguruan oinarritutako formateatze leiho gainerakorra %1$skontua kentzeak berreskuraezinak diren sinkronizatu gabeko aldaketak ere ezabatuko ditu. - %1$s kontua kentzeak berreskuraezinak diren%2$d sinkronizatu gabeko aldaketak ere ezabatuko ditu. + %1$s kontua kentzeak berreskuraezinak diren%2$d sinkronizatu gabeko aldaketa ere ezabatuko ditu. %1$s kendu - Kontu bat gehitzeko internetera konektatuta egon behar zara. + Kontu bat gehitzeko internetera konektatuta egon behar duzu. Ezarri karpeta Hurrengoa Aurrekoa Babeskopia - Aplikazioaren egoera berreskuraezina antzeman dugu. Mesedez, babestu sinkronizatu gabeko aldaketak eta garbitu Notes aplikazioaren biltegiratzea. - Zure notak Nextclouden gordetzeko karpeta + Aplikazioaren egoera berreskuraezina antzeman dugu. Mesedez, egin sinkronizatu gabeko aldaketen babeskopia eta garbitu Notes aplikazioaren biltegiratzea. + Zure oharrak Nextclouden gordetzeko karpeta .txt .md - Nota karpeta berria: %1$s + Oharren karpeta berria: %1$s Fitxategi-luzapena - Zure Nextcloudeko nota berrien fitxategi-luzapena + Zure Nextcloudeko ohar berrien fitxategi-luzapena Fitxategi berrien luzapena: %1$s HTTP egoera kodea: %1$d Notak inportatzen… @@ -310,4 +312,7 @@ \n -Ezabatze masiboa \n -MarkDown errendatzea \n -Hizkuntza askotan eskuragarri - \ No newline at end of file + Errore bat gertatu da edizio aberatsa kargatzean + Aldatu edizio arruntera + Atzera + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index e77333c6797cfe1f2089d9a16cb4e0f1c38be7ec..b160e67ff47e0b233ad38e3cb49178582822a83c 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -41,13 +41,19 @@ ماه گذشته حالت نمایش یادداشت‌ها + Notes opening behaviour "تم " قلم Monospace اندازهٔ قلم همگام‌سازی فقط در Wi-Fi + App lock (Beta) اعتبارنامه‌های دستگاه همگام‌سازی پس زمینه + Prevent screen capture نمایش گرید + Keep screen on + When viewing or editing a note + همگام‌سازی انجام نشد:%1$s همگام‌سازی انجام نشد هیچ ارتباطی با شبکه برقرار نیست @@ -58,6 +64,7 @@ شما در حال استفاده %1$s هستید نگاه‌دارنده توسعه دهندگان + original author مترجم انجمن نکست کلود در %1$s آزمایش کنندگان @@ -83,6 +90,8 @@ یادداشت واحد یادداشت یافت نشد لطفاً قبل از استفاده از این ویجت، به یادداشت‌ها وارد شوید + Star icon is used to denote an item as a favorite + انتخاب یادداشت ساختن یادداشت جدید @@ -103,16 +112,34 @@ فیلم‌ها فیلم کار + ToDo + ToDos + Checklists وظایف + Recipe + Recipes رستوران + Restaurants + Food + Bake + + Key + + Keys کلمه عبور کلمات عبور + Credential بازی بازی ها پخش کردن + Gift + Gifts حاضر + + Presents + حساب قبلاً وارد شده است هنوز هیچ یادداشتی وجود ندارد دکمه + را فشار دهید تا یک یادداشت جدید ایجاد کنید @@ -126,6 +153,7 @@ وای نه - حالا چی کار کنیم؟ لطفا از طریق توقف اجباری برنامه را بسته و دوباره اجرا کنید. ممکن است یک اتصال نادرست با نکست‌کلود ایجاد شده باشد. اگر این مشکل استمرار داشت، سعی کنید با پاک کردن فضای ذخیره‌سازی هردو برنامک : Nextcloud و Nextcloud Notes این مورد را برطرف کنید. + You can clear the storage by opening the app info and selecting Storage → Clear storage. ⚠️ Warning: This will delete not yet synchronized notes! ظاهرا برنامک یادداشت منسوخ شده است. لطفا آخرین نسخه را از Play Store یا F-Droid دانلود کنید. به نظر می‌رسد که مشکلی در برنامک نکست‌کلود ایجاد شده است. لطفا سعی کنید با توقف اجباری هر دو برنامک Nextcloud app و Nextcloud Notes را ببندید. "اگر توقف اجباری برنامه‌ها کمکی نکرد، می‌توانید پاک‌کردن فضای ذخیره‌سازی هردو برنامه را پاک کنید. " @@ -133,27 +161,29 @@ اتصال اینترنت خود را بررسی کنید. خاموش کردن اینترنت همراه و وای‌فای و روشن کردن مجدد آنها در برخی از مواقع می‌تواند کارساز باشد. پاسخ درستی از سرور شما دریافت نشد. لطفا دسترسی به یادداشت‌های خود را از طریق وب بررسی کنید. مشکلی در نصب ابر Nextcloud وجود دارد. لطفا لاگ‌های سرور را بررسی کنید. - لطفا بررسی کنید که نسخه Nextcloud شما در حال حاضر در حالت تعمیر و نگهداری نباشد. + لطفا بررسی کنید که نسخه نکست‌کلود شما در حال حاضر در حالت تعمیر و نگهداری نباشد. فضای برنامک نکست‌کلود شما پر شده است.لطفا یعضی از فایل‌های خود در را فضای ابری برای همگام‌سازی تغییرات محلی پاک کنید. ما برای کمک به شما احتیاج به دنبال کردن اطلاعات فنی داریم: لطفا از نصب و فعال بودن برنامک یادداشت بر روی سرور، اطمینان حاصل کنید. پاسخ سرور شما کد حالت HTTP 302 است. که دلالت بر عدم نصب برنامک یادداشت بر روی سرور و یا پیکربندی نامناسب دارد. این مشکل می‌تواند به دلیل تغییرات نامناسب ایجاد شده در فایل .htaccess و یا به دلیل برنامه‌های مرتبط با Nextcloud مثل OID Client رخ دهد. لطفا تمامی فرایند‌های بهینه‌سازی مصرف باتری برای Nextcloud و Notes app را غیر فعال کنید. + The Notes Android app requires the Nextcloud Android app to be at least version 3.18. اضافه ‌شده %1$s متن به اشتراک گذاشته‌شده خالی است به یادداشت اضافه کنید عوض‌کردن عنوان یادداشت ویرایش عنوان امنیت + Appearance and behavior همگام‌سازی مدیریت حساب‌ها قالب‌بندی - - یادداشت را در حالت «ویرایش» باز کنید - یادداشت را در حالت پیش نمایش باز کنید - آخرین انتخاب من را به خاطر بسپار - + + Plain edit mode + Plain preview + Rich edit mode + آخرین انتخاب من را به خاطر بسپار کوچک @@ -171,6 +201,25 @@ %d انتخاب شد %d انتخاب شد + + Deleted one note + Deleted %1$d notes + + + Restored one note + Restored %1$d notes + + + Share content of %1$d note + Share content of %1$d notes + + + --- + `%1$s` + \\`%1$s\\` + ``` + ```` + ```javascript قالب‌بندی مبتنی بر متن هدف اصلی از طراحی برنامک یادداشت، ارائه دادن یک ابزار رایگان یادداشت کارها است. همچنین شما قادر خواهید بود متن‌ها را با Markdown قالب‌بندی کنید. برای انواع مختلفی از مثالهای ذکر شده در زیر ، می توانید از میانبرها استفاده کنید تا بتوانید یادداشت های خود را بدون تایپ کد، قالب بندی کنید. فقط کافی است یک دامنه متن را انتخاب کنید یا روی مکان نما در هر موقعیتی ضربه بزنید و یک منوی بازشونده دریافت می کنید که در کنار ورودی‌های پیش‌فرض %1$s و %2$s و %3$s شامل ورودی‌‌هایی مانند %4$s و %5$s می‌باشد. @@ -208,6 +257,13 @@ اگر شما نیاز به مشخص کردن syntax در یک زبان دارید: جدول‌ها + + Column %1d + + Value %1d + + Links have to be either complete URLs starting with a protocol and domain or absolute paths starting with a %1$s character. + In order to conform to the Markdown format, please use escape characters in the image URL. This means for example, replace spaces with %1$s in the URL. تصاویر تصویر فانتزی @@ -218,11 +274,37 @@ باز کردن اطلاعات برنامه تنظیمات شبکه یه‌روزرسانی + No account configured yet شما تاکنون هیچ حساب کاربری دیگری را ایجاد نکرده‌اید. حساب کاربری را انتخاب کنید بازپخش قالب بندی مبتنی بر متن + + Removing the account %1$s will also delete irrecoverable one unsynchronized change. + Removing the account %1$s will also delete irrecoverable %2$d unsynchronized changes. + + Remove %1$s + برای اضافه کردن حساب باید به اینترنت متصل شوید. + Set folder + بعدی قبلی پشتیبان‌گیری + We detected an irrecoverably state of the app. Please backup your unsynchronized changes and clear the storage of the Notes app. + Folder to store your notes in your Nextcloud + + .txt + .md + + New notes folder: %1$s + File extension + File extension for new notes in your Nextcloud + New file suffix: %1$s + HTTP status code: %1$d + Importing notes… + Importing note %1$d of %2$d… + Account imported. + Error while loading rich editing + Switch to plain editing + بازگشت diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index a9d5c2c208fd7ca95850d9e426dd742df33d4227..81e71dc4cd03780f9db08c0dd4021ed1d9d302d7 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -161,11 +161,9 @@ Synkronointi Tilien hallinta Muotoilu - - Avaa muokkaustilassa - Avaa esikatselutilassa - Muista viimeisin valintani - + + Muista viimeisin valintani + Pieni Normaali @@ -286,4 +284,5 @@ Valtuus Kun katsot tai muokkaat muistiinpanoa Pidä näyttö päällä - \ No newline at end of file + Takaisin + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index da66e8c75351bc548ad8cd5fe27a56451215f3ac..ed062efd34380fc9e1558dbc10ac270c3938f0bd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -36,6 +36,7 @@ Ce mois-ci Mois dernier Mode d\'affichage des notes + Comportement à l\'ouverture de Notes Thème Texte monospace Taille des caractères @@ -56,6 +57,7 @@ Vous utilisez actuellement la <strong>%1$s</strong> Responsable Développeurs + auteur original Traducteurs Pour l’icône originale voir %1$s. Les icônes suivantes utilisées dans cette applications sont %1$s réalisées par Google Inc. et sous licence Apache 2.0. @@ -93,7 +95,7 @@ Fermer Copier Exception - Épingler sur la page d’accueil + Épingler sur l\'écran d\'accueil Cette note a été supprimée Ajouter un compte Musique @@ -165,11 +167,12 @@ Synchronisation Gérer les comptes Formatage - - Ouvrir en mode édition - Ouvrir en mode aperçu - Mémoriser ma dernière sélection - + + Édition simple + Prévisualization simple + Édition texte enrichi + Mémoriser ma dernière sélection + Petit Moyen @@ -292,4 +295,7 @@ Politique de confidentialité Conditions d\'utilisation Auteurs - \ No newline at end of file + Erreur lors du chargement de l\'édition enrichie + Bascule vers l\'édition simple + Retour + diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 6f5af6e48cd7098c88c3f28d2b3b9d2cb1d59866..9fdc4a5da76c6907aa63d27d7f4d061035168162 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1,7 +1,7 @@ Notas - Notas do Nextcloud + Notas de Nextcloud Todas as notas Favoritas Nova nota @@ -30,7 +30,8 @@ Buscar en %1$s Buscar todas as notas - Escolla unha categoría + Escoller unha categoría + Hoxe Onte Esta semana @@ -38,6 +39,7 @@ Este mes O mes pasado Modo de vista das notas + Comportamento de apertura das notas Tema Letra monoespazada Tamaño da letra @@ -46,12 +48,16 @@ Sincronización do traballo en segundo plano Evitar a captura da pantalla Ver como grella + Mantér a pantalla acesa + Ao ver ou editar unha nota + Produciuse un fallo na sincronización Servidor en modo mantemento Produciuse un erro descoñecido. + autor orixinal Para a icona orixinal, vexa %1$s. - Todas as iconas usadas nesta aplicación son %1$s feitas por Google Inc. e licenciadas baixo unha Licenza Apache 2.0. + Todas as iconas usadas nesta aplicación %1$sforon feitas por Google Inc. e licenciadas baixo unha Licenza Apache 2.0. Iconas Material Desig Sincronizar só con WiFi @@ -82,7 +88,8 @@ Nota única Non se atopou a nota Acceda a Notas antes de empregar este trebello - A icona de estrela emprégase para indicar que o elemento é un favorito + A icona de estrela úsase para indicar un elemento como favorito + Seleccionar unha nota Crear unha nota nova @@ -102,18 +109,34 @@ Filmes Filme Traballo + Tarefa pendente + Tarefas pendentes + Listas de verificación Tarefas Receita Receitas Restaurante + Restaurantes + Comida + Enfornar + + Chave + + Chaves Contrasinal Contrasinais + Credencial Xogo Xogos Reproducir + Agasallo + Agasallos - Presentar + Presente + + Presentes + A conta xa foi importada Aínda non hai notas Prema o botón + para crear unha nota nova @@ -125,28 +148,29 @@ Caixa de selección Desbloquear notas Vaites, e agora que? 🙁 - Probe a forzar o peche da apli e reiniciala de novo. Pode haber unha conexión incorrecta coa apli Nextcloud. - Se o problema continúa, tente limpar o almacenamento de ambas as aplis: Nextcloud e Nextcloud Notas para resolver este problema. - Pode limpar o almacenamento abrindo a información da apli e seleccionando Almacenamento → Limpar o almacenamento.⚠️ Advertencia: Isto eliminará as notas que aínda non estean sincronizadas. - A súa apli Nextcloud semella estar desactualizada. Visite Play Store ou F-Droid para obter a última versión. - Semella que algo está mal na súa apli Nextcloud. Tente forzar a parar ambas, a apli Nextcloud e a apli Nextcloud Notes. + Probe a forzar o peche da aplicación e reiniciala de novo. Pode haber unha conexión incorrecta coa aplicación Nextcloud. + Se o problema continúa, tente limpar o almacenamento de ambalas dúas aplicacións: Nextcloud e Nextcloud Notas para resolver este problema. + Pode limpar o almacenamento abrindo a información da aplicación e seleccionando Almacenamento → Limpar o almacenamento.⚠️ Advertencia: Isto eliminará as notas que aínda non estean sincronizadas. + A súa aplicación Nextcloud semella estar desactualizada. Visite a Play Store ou F-Droid para obter a última versión. + Semella que algo foi mal na súa aplicación Nextcloud. Tente forzar a parar ámbalas dúas aplicacións, Nextcloud e Nextcloud Notas. Se detelos pola forza non axuda, pode tentar limpar o almacenamento de ambas as aplicacións. Non houbo resposta do seu servidor no tempo dado. Asegúrese de que a súa instancia funciona correctamente. Comprobe a súa conexión de rede. Ás veces apagar e volver acender os datos móbiles ou a wifi pode axudar. - A resposta do servidor non foi correcta. Comprobe se pode acceder a Notas a través de interface web. - Hai un problema coa configuración do Nextcloud. Bótelle un ollo aos ficheiros de rexistro do servidor. + A resposta do servidor non foi correcta. Comprobe se pode acceder a Notas a través da interface web. + Hai un problema coa configuración de Nextcloud. Bótelle un ollo aos ficheiros de rexistro do servidor. Comprobe que a súa instancia Nextcloud non se atope en modo de mantemento. A súa instancia Nextcloud non dispón de espazo de almacenamento libre. Elimine algúns ficheiros para sincronizar os seus cambios locais na súa nube. Necesitamos a seguinte información técnica para axudarlle: Asegúrese de ter instalada e activada a aplicación «Notas» no seu servidor. - O seu servidor respondeu cun código de estado HTTP 302, o que implica que non ten instalada a aplicación Notas no servidor ou que algo está mal configurado. Isto pode ser causado por substitucións personalizadas nun ficheiro .htaccess ou por aplicacións Nextcloud como Client OID. - Desactive todas as optimizacións de batería para Nextcloud e a apli Notas. + O seu servidor respondeu cun código de estado HTTP 302, o que implica que non ten instalada a aplicación Notas no servidor ou que algo está mal configurado. Isto pode ser causado por substitucións personalizadas nun ficheiro .htaccess ou por aplicacións Nextcloud como Cliente OID. + Desactive todas as optimizacións de batería para Nextcloud e a aplicación Notas. + A aplicación de Notas para Android precisa que a aplicación de Android Nextcloud teña como mínimo a versión 3.18. Engadido «%1$s» O texto compartido estaba baleiro Anexo á nota Cambiar o título da nota Editar o título - Seguridade + Seguranza Aparencia e comportamento Sincronización Xestionar contas @@ -154,11 +178,12 @@ Fixar na pantalla de inicio Esta nota foi eliminada - - Abrir en modo de edición - Abrir en modo de vista previa - Lembrar a miña última selección - + + Modo de edición simple + Vista previa simple + Modo de edición mellorada + Lembrar a miña última selección + Pequena Mediana @@ -199,7 +224,7 @@ Só ten que seleccionar un intervalo de texto ou tocar no cursor en calquera posición e obterá un menú emerxente que contén xunto ás entradas predeterminadas %1$s, %2$s, %3$s entradas como %4$s ou %5$s. Texto - É moi sinxelo facer algunhas palabras en %1$snegriña%1$s e outras palabras en %2$scursiva%2$s con Markdown. Pode %3$sriscar%3$s algunhas palabras e incluso [ligar ao Nextcloud](https://nextcloud.com). + É moi sinxelo facer algunhas palabras en %1$snegriña%1$s e outras palabras en %2$scursiva%2$s con Markdown. Pode %3$sriscar%3$s algunhas palabras e incluso [ligar a Nextcloud](https://nextcloud.com). Listas Ás veces quere listas numeradas: @@ -236,6 +261,8 @@ Valor %1d + As ligazóns teñen que ser URL completos que comezan cun protocolo e dominio ou camiños absolutos que comezan cun carácter %1$s. + Para axustarse ao formato Markdown, utilice caracteres de escape no URL da imaxe. Isto significa, por exemplo, substituír espazos por %1$s no URL. Imaxes Imaxe elegante @@ -243,14 +270,22 @@ Ordenar por data de modificación Ordenar alfabéticamente Axustes da batería - Abrir a info da apli + Abrir a info da aplicación Axustes da rede Actualizar Aínda non hai ningunha conta configurada Aínda non ten configurada ningunha outra conta. - Escolla unha conta + Escoller unha conta Xanela emerxente de formato baseado no contexto - Para engadir unha conta tes que estar conectado a internet. + + Retirar a conta %1$s tamén eliminará de xeito irrecuperábel un cambio non sincronizado. + Retirar a conta %1$s tamén eliminará de xeito irrecuperábel %2$d cambios non sincronizados. + + Retirar %1$s + + Para engadir unha conta ten que estar conectado a Internet. + Estabelecer cartafol + Seguinte Anterior Copia de seguridade @@ -268,4 +303,18 @@ \n-Producir unha redución \n-Traducir a moitas linguas Información de notas - \ No newline at end of file + Detectamos un estado irrecuperábel da aplicación. Faga unha copia de seguranza dos cambios non sincronizados e limpe o almacenamento da aplicación Notas. + Cartafol para almacenar as súas notas no seu Nextcloud + + .txt + .md + + Novo cartafol de notas: %1$s + Extensión de ficheiro + Extensión de ficheiro para novas notas no seu Nextcloud + Novo sufixo de ficheiro: %1$s + Código de estado HTTP: %1$d + Produciuse un erro ao cargar a edición mellorada + Cambiar á edición simple + Atrás + diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..50ed248c678bfc7c84f8e39bb196342cba05e854 --- /dev/null +++ b/app/src/main/res/values-he/strings.xml @@ -0,0 +1,203 @@ + + + + פתקים + פתקים של Nextcloud + כל הפתקים + מועדפים + פתק חדש + ברוך בואך אל %1$s + הגדרות + פתקים שנמחקו + חיפוש + שיטת מיון + ביטול + החלפה + עריכה + הסרה + שמירה + על אודות + קישור + %1$s נמחק + %1$s שוחזר + ביטול + ללא קטגוריה + מחיקה + קטגוריה + סימון כמועדף + תצוגה מקדימה + שיתוף + + חיפוש תחת %1$s + חיפוש בכל הפתקים + + בחירת קטגוריה + + היום + אתמול + השבוע + שבוע שעבר + החודש + חודש שעבר + + מצב תצוגת פתקים + ערכת עיצוב + גופן ברוחב אחיד + גודל גופן + סנכרון רק דרך רשת אלחוטית + פרטי זיהוי להתקן + סנכרון ברקע + תצוגת רשת + הסנכרון נכשל: %1$s + הסנכרון נכשל + אין חיבור לאינטרנט + אירעה שגיאה בלתי ידועה. + + גרסה + אתה משתמש כרגע ב%1$s + מתחזק + מפתחים + מתרגמים + קהילת nextcloud ב %1$s + בודקים + קוד מקור + הפרוייקט מתארח ב GitHub %1$s + תקלו + ניתן לדווח על תקלות, להציע שיפורים ותכונות במערכת מעקב התקלות שלנו שב־GitHub: %1$s + תרגום + ניתן להצטרף לצוות של Nextcloud ב־Transifex ולסייע לנו לתרגם את היישומון הזה: %1$s + רישיון היישומון + היישומון מוגן ברישיון הציבורי הכללי של GNU גרסה 3 ומעלה. + הצגת הרישיון + סמלים + לסמל המקורי יש לגשת אל %1$s. + כל שאר הסמלים בהם נעשה שימוש ביישומון זה הם %1$s שנוצרו על ידי Google בע״מ ומוגשים תחת הרישוי Apache 2.0. + עיצוב צלמיות + תודות + תרומה + רישיון + + רשימת פתקים + אין פתקים + פתק בודד + לא נמצא פתק + נא להיכנס לפתקים לפני השימוש בחלונית הזאת + נא לבחור פתק + + יצירת פתק חדש + + רגיל + עדכון דרך רשת אלחוטית ונתונים סלולריים + Password protection + + שגיאה + סגור + העתק + חריגה + הוסף למסך הבית + פתק זה נמחק + הוספת חשבון + + מוזיקה + סרטים + סרט + עבודה + משימות + מתכון + מסעדה + ססמה + ססמאות + משחק + משחקים + + נגן + + מתנה + החשבון הזה כבר עבר ייבוא + אין פתקים עדיין + לחיצה + כפתור כדי ליצור פתק חדש + עוד + העברה + קריאה בלבד + אין קטגוריה + הוספת %1$s + תיבת סימון + שחרור פתקים + שוד ושבר - מה עכשיו? 🙁 + נראה כי יישומון ה־Nextcloud שלך אינו עדכני. נא לבקר בחנות Play או ב־F-Droid כדי לעדכן לגרסה האחרונה. + כנראה שמשהו השתבש ביישומון ה־Nextcloud שלך. נא לנסות לכפות עצירה על שניהם, יישומון ה־Nextcloud ויישומון הפתקים של Nextcloud. + אם כפיית עצירה לא עוזרת, ניתן לנסות לרוקן את האחסון של שני היישומונים. + לא חזרה תגובה מהשרת שלך בזמן קצוב. נא לוודא שהעותק פועל כראוי. + נא לבדוק שעותק ה־Nextcloud שלך אינו במצב תחזוקה כרגע. + אנו זקוקים לפירוט הטכני הבא כדי לסייע לך: + הטקסט ששותף היה ריק + הוספה לסוף הפתק + עריכת כותרת + אבטחה + סנכרון + ניהול חשבונות + עיצוב + + + שמירת הבחירה האחרונה שלי + + + קטן + בינוני + גדול + + + + בהירה + כהה + ברירת מחדל של המערכת + + + + נבחר %d + נבחרו %d + נבחרו %d + נבחרו %d + + --- + `%1$s` + \\`%1$s\\` + ``` + ```javascript + עיצוב מבוסס הקשר + טקסט + רשימות + לפעמים יעניין אותך להוסיף רשימות ממוספרות: + אחת + שתיים + שלוש + לפעמים יעניין אותך דווקא תבליטים: + התחלת שורה במינוס + ככה + וככה + + תיבות סימון + כדי ליצור תיבת סימון, יש להשתמש ברשימה עם סוגריים מרובעים אחריה + פריט 1 + פריט 2 + + מסמכים עם מבנה פנימ + זו כותרת מסדר שלישי + ניתן להשתמש ב־%1$s עד %2$s שש לגדלי כותרות שונים. + כדי לצטט מישהו, יש להשתמש בתו %1$s לפני השורה: + דמיון חשוב יותר מידע. ידע הוא מוגבל. דמיון חובק את העולם. + - אלברט אינשטיין + + קוד + טבלאות + תמונות + אחר + פתיחת פרטי יישומון + הגדרות רשת + עדכון + נא לבחור חשבון + על המכשיר שלך להיות מחובר לאינטרנט כדי שיתאפשר לך להוסיף חשבון. + הבא + הקודם + חזרה + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 5c89d7a138462a029f07cb209fa1145f5f593849..e3ccfd796b9b8fd34c6e914be1de2d08bfcd6f1d 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -173,11 +173,8 @@ Upravljaj računima Oblikovanje - - Otvori u načinu uređivanja - Otvori u načinu pregleda - Zapamti moj posljednji izbor - + + Zapamti moj posljednji izbor Mali @@ -303,4 +300,5 @@ Uvoz bilješki... Uvoz bilješke %1$d od %2$d... Račun je uvezen. - + Natrag + diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 190fe93d292ef2adaea5ce1c7b0bac5b8de5d0e5..22fd6c1338c47e20dac7f67bed44272d03eb02ae 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -38,6 +38,7 @@ Ebben a hónapban Előző hónap + Jegyzetek megnyitási viselkedése Fix szélességű betűkészlet Betűméret Alkalmazás zár (Béta) @@ -52,6 +53,7 @@ A kiszolgáló karbantartási módban van Ismeretlen hiba történt. + eredeti szerző Az eredeti ikon itt található: %1$s. Az alkalmazás által használt összes többi ikon a Google által készített %1$s része, és azok Apache 2.0 License alatt érhetők el. Material Design ikonok @@ -174,11 +176,11 @@ Új jegyzet létrehozása Világos Sötét - - Megnyitás szerkesztésre - Megnyitás előnézetre - A legutóbbi döntés megjegyzése - + + Egyszerű szerkesztési mód + Egyszerű előnézet + Formázott szöveges mód + A legutóbbi döntés megjegyzése Kicsi @@ -300,4 +302,7 @@ Fiók importálva. Szolgáltatás feltételei Szerzők - \ No newline at end of file + Hiba a formázott szöveges szerkesztő betöltése során + Átváltás az egyszerű szerkesztésre + Vissza + diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml deleted file mode 100644 index 0e883a628f894b4f5ce8d50180170c50bbda7844..0000000000000000000000000000000000000000 --- a/app/src/main/res/values-ia/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - %d selectionate - %d selectionate - - \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 53bcd33dbd6c942bd436995895cbd38f6952703e..54950d6c2b4a649e3bb6c7240b4638b82f222bbd 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -163,11 +163,9 @@ Formattazione Allega alla schermata iniziale Questa nota è stata eliminata - - Apri in modalità modifica - Apri in modalità anteprima - Ricorda la mia ultima selezione - + + Ricorda la mia ultima selezione + Piccola Media @@ -297,4 +295,5 @@ \n***Nota %1$d (Ultima modifica: %2$s):*** \n" # Abbiamo riscontrato un conflitto tra le tue note. Scegli quella corretta. - \ No newline at end of file + Indietro + diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index f81c688616779f347ba3778dba38cf41a245dfe9..24fcbf30b7bc0082d808a163d3c316afdbb7c9ed 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -174,11 +174,8 @@ アカウント管理 フォーマット - - 編集モードで開く - プレビューモードで開く - 最後に使用したモードで開く - + + 最後に使用したモードで開く @@ -294,4 +291,5 @@ ノートを導入しています… %2$d中%1$dのノートをインポート中... アカウントを導入しました。 - + 戻る + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index ab67024efa26ef694040d2c6acb6a9ce676d93d3..b56ab20755285d808cd5403aab4bf7dcd070926d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -176,11 +176,8 @@ 계정 관리 서식 - - 편집 모드로 열기 - 미리 보기 모드로 열기 - 마지막 선택 기억하기 - + + 마지막 선택 기억하기 작게 @@ -291,4 +288,5 @@ 노트 불러오는 중... %2$d의 노트 %1$d 불러오는 중... 계정을 불러왔습니다. - + 뒤로 + diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index d4e8a7e1a24dae20bf852db9209164038364de7b..70ae063ae121a4d1351ebb9d67654c3fbf200380 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -144,11 +144,8 @@ Tvarkyti paskyras Formatavimas - - Atverti redagavimo veiksenoje - Atverti peržiūros veiksenoje - Prisiminti paskutinį pasirinkimą - + + Prisiminti paskutinį pasirinkimą Mažas @@ -224,4 +221,5 @@ Norėdami pridėti paskyrą, turite būti prisijungę prie interneto. Kitas Ankstesnis + Atgal diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml deleted file mode 100644 index 25b885bf8735c6db917785c2404eed7c73c5aa1f..0000000000000000000000000000000000000000 --- a/app/src/main/res/values-lv/strings.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - Piezīmes - Jauna piezīme - Iestatījumi - Atcelt - Rediģēt - Saglabāt - Par - Piezīme ir dzēsta - Piezīme restaurēta - Atsaukt - Dzēst - Izlase - Priekšskatīt - Koplietot - - Šodien - Vakar - Šonedēļ - Šis mēnesis - - - - - - Sinhronizācija neizdevās: %1$s - nav tīkla savienojumu - - - - - Versija - Uzturētājs - Izstrādātāji - Tulkotāji - Testētāji - Izejas kods - Tulkot - Programmas licence - Šī lietojumprogramma tiek licencēta zem GNU GENERAL PUBLIC LICENSE v3+. - Skatīt licenci - Ikonas - Kredīti - Licence - - Viena piezīme - - - Izveidot jaunu pierakstu - - diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 734a66f631c8fbbff01e159c10c49f7a7ba32081..43555cd4e7a2df5d6759dcd3a0081972a4b1cca0 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -2,7 +2,7 @@ Notater - Nextcloud Notes + Nextcloud Notater Alle notater Favoritter Nytt notat @@ -41,23 +41,30 @@ Forrige måned Visningsmodus for notater + Åpningsoppførsel fra Notes Tema Monospace font Skriftstørrelse Synkroniser kun med WiFi + Applås (beta) Enhetens legitimasjon Synkronisering i bakgrunnen + Forhindre skjermopptak Rutenett-visning + Hold skjermen på + Når du viser eller redigerer et notat + Synkronisering mislyktes: %1$s Synkronisering mislyktes Ingen nettverkstilkobling Server i vedlikeholdsmodus - En ukjent feil oppstod. + En ukjent feil oppsto. Versjon Du bruker forøyeblikket %1$s Vedlikeholder Utviklere + original forfatter Oversettere Nextcloud-fellesskapet på %1$s Testere @@ -105,15 +112,34 @@ Filmer Film Arbeid + Gjøremål + Gjøremål + Sjekklister Oppgaver + Oppskrift + Oppskrifter Restaurant - PassordP + Restauranter + Mat + Bake + + Nøkkel + + Nøkler + Passord Passord + Påloggingsdetalj + Spill Spill Spill + Gave + Gaver Gave + + Gavepakker + Kontoen har allerede blitt importert Ingen notater enda Trykk + knappen for å opprette et nytt notat @@ -126,9 +152,10 @@ Lås opp notater Å nei - Hva nå? 🙁 Prøv å tvinge avslutning av appen og start den på nytt. Det kan ha vært en feiltilkobling til Nextcloud-appen. - Hvis problemet vedvarer, prøv å tømme lageret for begge disse appene for å løse problemet: Nextcloud og Nextcloud Notes. + Hvis problemet vedvarer, prøv å tømme lageret for begge disse appene for å løse problemet: Nextcloud og Nextcloud Notater. + Du kan tømme lagringen ved å åpne appinformasjonen og velge Lagring → Tøm lagring. ⚠️ Advarsel: Dette vil slette notater som ikke er synkronisert ennå! Det virker som Nextcloud appen er utdatert. Venligst besøk Play Sore eller F-Droid for å finne siste versjon. - Det virker som noe er galt med Nextcloud appen. Vennligst prøv å tvinge avslutning av Nextcloud appen og Nextcloud Notes appen. + Det virker som noe er galt med Nextcloud-appen. Vennligst prøv å tvinge avslutning av Nextcloud-appen og Nextcloud Notater-appen. Hvis det ikke hjelper med tving avslutt, så kan du prøve å tømme lageret for begge appene. Serveren svarer ikke innen en gitt tid. Vennligst påse at serveren er tilgjengelig. Sjekk nettverkstilkoblingen. Noen ganger hjelper det å skru av og på mobil data eller WI-FI tilkoblingen. @@ -137,24 +164,26 @@ Vennligst sjekk at Nextcloud instansen ikke er i vedlikeholdsmodus for øyeblikket. Nextcloud instansen har ikke mer ledig lagringsplass. Vennligst slett noen filer for å synkronisere de lokale endringen til skyen din. Vi trenger følgende teknisk informasjon for å kunne hjelpe deg: - Vennligst påse at du har installert og aktivert \"Notes\" appen på serveren. - Serveren din svarte med en HTTP 302-statuskode, noe som antyder at du ikke har installert Notes-appen på serveren din, eller at noe er feilkonfigurert. Dette kan være forårsaket av tilpassede overstyringer i en .htaccess-fil eller av Nextcloud-apper som OID Client. - Deaktiver alle batterioptimaliseringer for Nextcloud og Notes-appen. + Vennligst påse at du har installert og aktivert \"Notes\"-appen på serveren. + Serveren din svarte med en HTTP 302-statuskode, noe som antyder at du ikke har installert Notater-appen på serveren din, eller at noe er feilkonfigurert. Dette kan være forårsaket av tilpassede overstyringer i en .htaccess-fil eller av Nextcloud-apper som OID Client. + Deaktiver alle batterioptimaliseringer for Nextcloud og Notater-appen. + Notater-androidappen krever at Nextcloud-androidappen er minst versjon 3.18. Lagt til \"%1$s\" Delt tekst var tom Legg til i notat Endre tittel på notat Rediger tittel Sikkerhet + Utseende og oppførsel Synkronisering Håndter kontoer Formatering - - Åpne i redigeringsmodus - Åpne i forhåndsvisningsmodus - Husk mitt siste valg - + + Ren endringsmodus + Ren forhåndsvisning + Rik endringsmodus + Husk mitt siste valg Liten @@ -172,13 +201,27 @@ %d er valgt %d valgt + + Slettet ett notat + Slettet %1$d notater + + + Gjenopprettet ett notat + Gjenopprettet %1$d notater + + + Del innhold i %1$d notat + Del innhold av %1$d notater + + --- `%1$s` \\`%1$s\\` ``` + ```` ```javascript Kontekstbasert formatering - Et viktig designmål for Notes-appen er å tilby et distraksjonsfritt verktøy. Selv om du vil kunne formatere tekstene dine med Markdown. For de forskjellige eksemplene nedenfor, kan du bruke snarveier slik at du kan formatere notatene dine uten å skrive inn kodene nedenfor. + Et viktig designmål for Notater-appen er å tilby et distraksjonsfritt verktøy. Selv om du vil kunne formatere tekstene dine med Markdown. For de forskjellige eksemplene nedenfor, kan du bruke snarveier slik at du kan formatere notatene dine uten å skrive inn kodene nedenfor. Bare velg et tekstområde eller trykk på markøren når som helst, så får du en popup-meny som inneholder ved siden av standardoppføringene %1$s, %2$s, %3$s oppføringer som %4$s eller %5$s. Tekst @@ -219,7 +262,11 @@ Verdi %1d + Koblinger må enten være komplette URL-er som starter med en protokoll og domene eller absolutte stier som starter med et %1$s-tegn. + For å samsvare med Markdown-formatet, vennligst bruk escape-tegn i bildets URL. Dette betyr for eksempel å erstatte mellomrom med %1$s i URL-en. Bilder + Fancy bilde + Annet Sorter etter dato endret Sorter alfabetisk @@ -227,10 +274,37 @@ Åpne App info Nettverksinnstillinger Oppdater + Ingen konto er konfigurert Du har ikke konfigurert noen konto enda. Velg konto + Popover for kontekstbasert formatering + + Fjerning av kontoen %1$s vil også uopprettelig slette en usynkronisert endring. + Fjerning av kontoen %1$s vil også uopprettelig slette %2$d usynkroniserte endringer. + + Fjern %1$s + Du må være koblet til internett for å kunne legge til en konto. + Sett mappe + Neste Forrige Sikkerhetskopi + Vi oppdaget en uopprettelig tilstand i appen. Ta sikkerhetskopi av usynkroniserte endringer og tøm lagringen av Notater-appen. + Mappe for å lagre notatene dine i Nextcloud + + .txt + .md + + Ny notatmappe: %1$s + Filetternavn + Filetternavn for nye notater i Nextcloud + Nytt filsuffiks: %1$s + HTTP-statuskode: %1$d + Importerer notater… + Importerer notat %1$d av %2$d… + Konto importert. + Feil ved lasting av rik endringsmodus + Bytt til ren endringsmodus + Tilbake diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 359808dc14dcc8059ba955787f851b1a2a843492..21bdab4c9e07862744cb584bfe8124e7db172cfb 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -59,6 +59,7 @@ Je gebruikt nu <strong>%1$s</strong> Beheerder Ontwikkelaars + oorspronkelijke ontwikkelaar Vertalers Nextcloud-gemeenschap op <a href=\"%1$s\">Transifex</a> Testers @@ -164,11 +165,9 @@ Uitzondering Vastmaken op startscherm Deze notitie is verwijderd - - Open in bewerkingsmodus - Open in voorbeeldmodus - Onthoud je laatste selectie - + + Onthoud je laatste selectie + Klein Gemiddeld @@ -291,4 +290,5 @@ \n \n***Notitie %1$d (laatst gewijzigd: %2$s):*** \n" - \ No newline at end of file + Terug + diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..da8bd1293ff7583a48486011d6ae229ca6165a11 --- /dev/null +++ b/app/src/main/res/values-oc/strings.xml @@ -0,0 +1,155 @@ + + + + Nòtas + Nòtas de Nextcloud + Totas las nòtas + Favoridas + Nòta novèla + La benvenguda a %1$s + Paramètres + Nòtas suprimidas + Recercar + Metòde de tria + Anullar + Modificar + Suprimir + Salvar + A prepaus + Ligam + %1$s suprimit + Restaurar %1$s + Desfar + Desorganizadas + Suprimir + Categoria + Favorit + Apercebut + Partejar + + Cercar dins %1$s + Cercar totas las nòtas + + Causir una categoria + + Uèi + Ièr + Aquesta setmana + La setmana passada + Aqueste mes + Lo mes passat + + Mòde d’afichatge per las nòtas + Tèma + Poliça monospace + Talha poliça + Sincro. sonque en Wi-Fi + Varrolh app. (Beta) + Identificants del periferic + Sincronizacion en rèireplan + Empachar captura d’ecran + Vista en grasilha + Fracàs de la sincronizacion : %1$s + Sincronizacion fracassada + Pas de connexion ret + Lo servidor es en mòde manteniment + Una error desconeguda s\'es produsida. + + Version + Manteneire + Desvolopaires + Traductors + Provaires + Còdi font + Avarias + Translate + Licéncia de l’app + Veire la licéncia + Icònas + Crèdits + Contribucions + Licéncia + + Lista de nòtas + Cap de nòta + Sola nòta + Nòta pas trobada + Normala + Password protection + + Error + Tampar + Copiar + Excepcion + Penjar a l’ecran d’acuèlh + Aqueste nòta es estada suprimida + Apondre un compte + + Musica + Films + Film + Professional(a) + Prètzfaches + Senhal + Senhals + Jòc + Jòcs + + Aviar + Cap de nòta pel moment + Mai + Desplaçar + Lectura sola + Cap de categoria + Apondre %1$s + Cambiar lo títol de la nòta + Modificar lo títol + Seguretat + Synchronization + Maneja comptes + + Clar + Escur + + + + %d seleccionat + %d seleccionats + + Tèxt + Un + Dos + Tres + Atail + Element 1 + Element 2 + + Documents estructurats + - Albert Einstein + + Còdi + Tablèus + + Colomna %1d + + Valor %1d + + Imatges + Imatge crane + + Autre + Triar per data de modificacion + Triar alfabeticament + Paramètres de batariá + Dobrir info app + Paramètres de ret + Actualizar + Cap de compte pas configurat pel moment + Causir un compte + Suprimir %1$s + + Seguent + Precedent + Salvagarda + Retorn + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 283f44a23efdca235a58bc14b7c4e9088d849047..658aa1e23b78cdf4dd12c7b351983cf584d82fd5 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -41,6 +41,7 @@ W zeszłym miesiącu Tryb wyświetlania notatek + Zachowanie otwierania notatek Motyw Czcionka monospace Rozmiar czcionki @@ -63,6 +64,7 @@ Aktualnie używasz %1$s Opiekun Deweloperzy + oryginalny autor Tłumacze Społeczność Nextcloud na %1$s Testerzy @@ -177,11 +179,11 @@ Zarządzaj kontami Formatowanie - - Otwórz w trybie edycji - Otwórz w trybie podglądu - Zapamiętaj ostatni wybór - + + Prosta modyfikacja + Prosty podgląd + Bogata modyfikacja + Zapamiętaj ostatni wybór Mała @@ -312,4 +314,7 @@ Importuję notatki… Importuję notatkę %1$d z %2$d… Konto zaimportowane. - + Błąd podczas ładowania bogatej modyfikacji + Przełącz na prostą modyfikację + Poprzednia + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index b1ff6a8bce5a50dbeebafacc19ed6ebbc65b3c19..b5aca4d85ef7d1cf4205ba39137f06c2921abb91 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -41,6 +41,7 @@ Último mês Modo de exibição de notas + Notes opening behaviour Tema Fonte monospaçada Tamanho da fonte @@ -63,6 +64,7 @@ Você está atualmente usando %1$s Mantenedor Desenvolvedores + autor original Tradutores Comunidade Nextcloud em %1$s Testadores @@ -177,11 +179,11 @@ Gerenciar contas Formatação - - Abrir em modo de edição - Abrir em modo de visualização - Lembrar minha última escolha - + + Plain edit mode + Plain preview + Rich edit mode + Lembrar minha última escolha Pequena @@ -227,7 +229,7 @@ Basta selecionar um intervalo de texto ou tocar no cursor em qualquer posição e você obterá um menu pop-up que contém ao lado das entradas padrão %1$s, %2$s, %3$s entradas como %4$s ou %5$s. Texto - É muito fácil fazer algumas palavras %1$snegritas%1$s e outras %2$sitálicas%2$s com Markdown. Você pode %3$sriscar%3$s algumas palavras e até [linkar para o Nextcloud](https://nextcloud.com). + É muito fácil fazer algumas palavras %1$snegritas%1$s e outras %2$sitálicas%2$s com Markdown. Você pode %3$sriscar%3$s algumas palavras e até [apontar para o Nextcloud](https://nextcloud.com). Listas Às vezes você deseja listas numeradas: @@ -307,4 +309,7 @@ Importando notas… Importando notas %1$d de %2$d… Conta importada. - + Error while loading rich editing + Switch to plain editing + Voltar + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 9cb7cef6c8bcc0373dd7c759c7ebfee22b235480..fb532ceac9719a09b0c74a4ec9c3ead89c8fe815 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -50,6 +50,9 @@ Sincronizare în fundal Oprește captura de ecran Vizualizare tip grilă + Menține ecranul deschis + Când vizionezi sau editezi o notiță + Sincronizare eșuată: %1$s Sincronizare eșuată Nu exista conexiune @@ -174,11 +177,8 @@ Administrare conturi Formatare - - Deschide în modul editare - Deschide în modul de previzualizare - Amintește-ți ultima mea selecție - + + Amintește-ți ultima mea selecție Mic @@ -304,4 +304,5 @@ Se importă notiţe... Se importă notiţele %1$d din %2$d ... Cont importat. - + Înapoi + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 18c23fd6333d28d4a9ca0f4860a735c93e39011a..cd75574ba2e01cfd7eb125804e8d7171b16e24cd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -34,6 +34,7 @@ На этой неделе Этот месяц Режим показа заметок + Поведение при открытии приложения Заметки Тема Моноширинный шрифт Размер шрифта @@ -54,6 +55,7 @@ Вы используете <strong>%1$s</strong> Сопровождающий Разработчики + первый автор Переводчики Сообщество Nextcloud на <a href=\"%1$s\">Transifex</a> Тестеры @@ -162,11 +164,12 @@ Синхронизация Управление аккаунтами Форматирование - - Открывать в режиме правки - Открывать в режиме просмотра - Запоминать выбранный режим - + + Режим простого редактирования + Режим простого просмотра + Режим форматированного редактирования + Запоминать выбранный режим + Маленький Средний @@ -295,4 +298,7 @@ \n - Массовое удаление \n - Рендеринг MarkDown \n - Переведено на многие языки - \ No newline at end of file + Ошибка при загрузке форматированного редактирования + Переключиться на простое редактирование + Назад + diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index a4872cc3a0e2960f90ce81d1bc7697f74f5b339b..bad18c59cf1ae404672d3e6faf741f7d95a6265b 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -151,11 +151,8 @@ Gesti contos Formatatzione - - Aberi in modalidade modìfica - Aberi in modalidade anteprima - Regorda s\'ùrtimu sèberu - + + Regorda s\'ùrtimu sèberu Piticu @@ -271,4 +268,5 @@ Estensione de archìviu pro notas noas in su Nextcloud tuo Sufissu de archìviu nou: %1$s Còdighe de istadu HTTP: %1$d + In segus diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 004cc36c9e956f33f369318997d29d979163b5bc..7a00023b0f615101d073e2fcbf98da7491623283 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -174,11 +174,8 @@ Spravovať účty Formátovanie - - Otvoriť v režime úprav - Otvoriť v režime ukážky - Zapamätať si môj posledný výber - + Zapamätať si môj posledný výber + Malá Stredná @@ -311,4 +308,5 @@ Podmienky používania Zásady ochrany osobných údajov Autori - \ No newline at end of file + Späť + diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 691fcf7af8feb026e12f721c5c5c95abaf8427cc..df4e9772a4cc215577711693017137c6cd0cfcfd 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -41,6 +41,7 @@ Zadnji mesec Način odpiranja zabeležk + Obnašanje odpiranja beležk Tema Pisava enotne širine Velikost pisave @@ -50,6 +51,9 @@ Usklajevanje v ozadju Prepreči zajem zaslona Mrežni pogled + Ohrani zaslon prižgan + Med pregledovanjem in urejanjem zabeležk + Usklajevanje je spodletelo: %1$s Usklajevanje je spodletelo Ni omrežne povezave @@ -60,6 +64,7 @@ Trenutno je v uporabi %1$s Vzdrževalec Razvijalci + izvorni avtor Prevajalci Skupnost Nextcloud na %1$s Preizkuševalci @@ -174,11 +179,11 @@ Upravljanje z računi Oblikovanje - - Zabeležka se odpre v urejevalnem načinu - Zabeležka se odpre v predogledu - Zapomni si zadnjo izbiro - + + Besedilni urejevalni način + Predogled urejevalnega načina + Oblikovni urejevalni način + Zapomni si zadnjo izbiro Majhna @@ -309,4 +314,7 @@ Poteka uvažanje sporočilc ... Poteka uvažanje sporočilca %1$d od %2$d… Račun je uspešno uvožen. - + Napaka nalaganja oblikovnega urejevalnika + Preklopi na besedilno urejanje + Nazaj + diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index b008f86e9ef41df7774b71c6d823bbe79c1b1955..294671ffbba9c135a2bf0dd0c3af5ac2b5aa8eff 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -41,13 +41,19 @@ Прошлог месеца Режим приказа белешки + Понашање отварања белешке Тема Фонт фиксне ширине Величина фонта Синхронизуј само преко бежичне везе + Закључавање апликације (бета) Акредитиве уређаја Позадинска синхронизација + Спречи снимак екрана Приказ мреже + Задржи укључен екран + Када се прегледа или уређује белешка + Синхронизација није успела%1$s Синхронизација није успела Нема мрежне везе @@ -58,6 +64,7 @@ Тренутно користите %1$s Одржаваоц Програмери + оригинални аутор Преводиоци Некстклауд заједница на %1$s Тестери @@ -105,14 +112,34 @@ Филмови Филм Посао + Обавеза + Обавезе + Листе за потврду Задаци + Рецепт Рецепти Ресторан + Ресторани + Храна + Пекара + + Кључ + + Кључеви Лозинка Лозинке + Креденцијал + Игра Игрице Пусти + Поклон + Поклони + + Дар + + Дарови + Налог је већ увезен Још нема белешки Притисните + дугме да направите нову белешку @@ -126,6 +153,7 @@ О, не - шта сад? 🙁 Покушајте да форсирано затворите апликацију и стартујете је поново. Можда је била неисправна конекција ка Некстклауд апликацији. Ако проблем настави да се дешава, пробајте да очистите податке од обе апликације: и Некстклауд и Некстклауд Белешки да бисте решили проблем. + Складиште можете да обришете тако што отворите информације о апликацији и изаберете Storage → Clear storage. ⚠️ Упозорење: Ово ће да обрише белешке које још увек нису синхронизоване! Изгледа да је верзија Некстклауд апликације стара. Посетите Play Store или Ф-дроид да скинете најновију верзију. Нешто није у реду да Вашом Некстклауд апликацијом. Пробајте да форсирано зауставите обе, и Некстклауд апликацију и апликацију Некстклауд Белешки. Ако ово заустављање не помогне, очистите податке за обе апликације. @@ -139,21 +167,23 @@ Проверите да ли сте инсталирали и укључили апликацију „Белешке” на Вашем серверу. Сервер је одговорио са HTTP 302 кодом, што значи да немате инсталирану апликацију Белешке или да Вам сервер није правилно конфигурисан. Ово може да буде проузорковано произвољним додавањима у .htaccess фајлу или другим Некстклауд апликацијама као што је OID Client. Искључите све оптимизације батерије за Некстаклауд и Белешке апликације. + The Андроид апликација Белешке захтева да је верзија Nextcloud Андроид апликације барем 3.18. Додата „%1$s” Дељени текст је празан Додај на белешку Промени назив белешке Измени наслов Безбедност + Изглед и понашање Синхронизација Управљање налозима Форматирање - - Отвори у режиму уређивања - Отвори у режиму прегледа - Запамти мој последњи избор - + + Режим простог уређивања + Прости преглед + Режим детаљног уређивања + Запамти мој последњи избор Мали @@ -192,6 +222,7 @@ `%1$s` \\`%1$s\\` ``` + ```` ```javascript Форматирање у зависности од контекста Главни циљ дизајна апликације Белешки је био да пружи алат који не стаје на пут. А моћи ћете и да форматирате текст Markdown синтактом. За разне од долепоменутих примера можете да користите и пречице, па можете форматирати белешке без куцања кодова испод. @@ -230,7 +261,16 @@ А ако желите бојење синтакте, додајте и језик: Табеле + + Колона %1d + + Вредност %1d + + Линкови морају бити или комплетне URL адресе које почињу протоколом и доменом или апсолутне путање које почињу карактером %1$s. + Да би сте били сагласни са Markdown форматом, молимо вас да користите означавајуће карактере у URL слика. Ово значи, на пример, да се размаци у URL замењују са %1$s. Слике + Фенси слика + Остало Сортирај по времену измене Сортирај абецедно @@ -238,9 +278,38 @@ Отвори инфо о апликацији Поставке мреже Ажурирај + Још увек није подешен ниједан налог Нисте подесили ниједан други налог. Одаберите налог + Искачући облачић форматирања у зависности од контекста + + Уклањање налога %1$s ће такође неповратно да обрише једну несинхронизовану измену. + Уклањање налога %1$s ће такође неповратно да обрише %2$d несинхронизоване измене. + Уклањање налога %1$s ће такође неповратно да обрише %2$d несинхронизованих измена. + + Уклони %1$s + Морате бити повезани на интернет да бисте додали налог. + Постави фолдер + Следећа Претходно + Резервна копија + Открили смо стање апликације које не може да се опорави. Молимо вас да направите резервну копију ваших несихронизованих измена и очистите складиште Notes апликације. + Фолдер у којем се на вашем Nextcloud серверу чувају белешке + + .txt + .md + + Нови фолдер за белешке: %1$s + Екстензија фајла + Екстензија фајла за нове белешке на вашем Nextcloud серверу + Суфикс новог фајла: %1$s + HTTP статусни кôд: %1$d + Белешке се увозе... + Увози се белешка %1$d од укупно %2$d… + Налог је увезен. + Грешка приликом учитавања детаљног уређивања + Пређи на просто уређивање + Назад diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8a3403061af6fc5b905e7b15ce5a4b81ccb74451..d737c3d3dfafb5b322a363fd699eda7a418902f3 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -36,6 +36,7 @@ Denna månad Förra månaden Visningsläge för anteckningar + Notes startfunktion Tema Monospace font Teckenstorlek @@ -45,6 +46,9 @@ Bakgrundssynkronisering Hindra skärmklipp Rutnätsvy + Håll skärmen igång + När du visar eller redigerar en anteckning + Synkronisering misslyckades: %1$s Synkroniseringen misslyckades Ingen nätverksanslutning @@ -54,6 +58,7 @@ Du använder %1$s för närvarande Underhållare Utvecklare + Ursprungsskribent Översättare Nextcloud community på %1$s Testare @@ -96,8 +101,10 @@ Film Arbete Uppgifter + Uppgifter Checklistor Uppgifter + Recept Recept Restaurang Restauranger @@ -147,20 +154,23 @@ Se till att du har installerat och aktiverat Notes-appen på din server. Din server svarade med ett HTTP 302 statusmeddelande, vilket innebär att du inte har installerat Notes-appen på din server eller att något är felkonfigurerat. Detta kan orsakas av manuella ändringar i en .htaccess-fil eller av Nextcloud-appar som \"OID Client\". Stäng av alla batterioptimeringar för Nextcloud och Notes-appen. + Anteckningar Android-appen kräver att Nextcloud Android-appen är minst version 3.18. La till \"%1$s\" Den delade texten var tom Lägg till i anteckning Ändra anteckningstitel Redigera titel Säkerhet + Utseende och beteende Synkronisering Hantera konton Formatering - - Öppna i redigera-läge - Öppna i förhandsvisningsläge - Kom ihåg mitt sista val - + + Enkel redigeringsfunktion + Enkel förhandsvisning + Rich redigeringsfunktion + Kom ihåg mitt sista val + Liten Medium @@ -191,10 +201,15 @@ `%1$s` \\`%1$s\\` ``` + ```` ```javascript Innehållsbaserad formatering Ett stort deignmål med Notes-appen är att tillahandahålla ett distraktionsfritt verktyg. Fast du kan formatera dina texter med Markdown. För de olika nedan nämnda exemplena kan du använda genvägar så att du kan formatera dina anteckningar utan att skriva in koderna nedan. + Markera bara ett stycke text eller tryck på din markör vid valfri position och du kommer få en popup-meny som utöver standardposterna såsom %1$s, %2$s, %3$s även innehåller poster som %4$s eller %5$s. + Text + Med Markdown är det enkelt att sätta en del ord i %1$sfetstil%1$s och andra %2$skursiva%2$s. Du kan %3$söverstyka%3$s ord och även [länka till Nextcloud](https://nextcloud.com). + Listor Ibland vill du ha en numrerad lista: En @@ -221,7 +236,14 @@ Markdown stödjer också något som kallas för kodavgränsning, vilket tillåter flera rader utan indentering: Och om du vill använda syntaxmarkering kan du inkludera språket: Tabeller + + Kolumn %1d + + Värde %1d + Bilder + Snygg bild + Övrigt Sortera efter ändrat datum Sortera alfabetiskt @@ -229,22 +251,20 @@ Öppna appinformation Nätverksinställningar Uppdatera + Inget konto konfigurerat än Du har inte konfigurerat några andra konton än. Välj konto + Kontextbaserad formaterings-popover + Ta bort %1$s + Du måste vara ansluten till internet för att lägga till ett konto. + Sätt mapp + Nästa Föregående Användarvillkor Upphovsmän Integritetspolicy - Recept - Utseende och beteende - Kolumn %1d - Värde %1d - Tjusig bild - Inget konto är konfigurerat än - Ta bort %1$s - Ställ in mapp Säkerhetskopia Filändelse Filändelse för nya anteckningar i Nextcloud @@ -256,17 +276,10 @@ \n \n***Anteckning %1$d (Senast ändrad: %2$s):*** \n" - Behåll skärmen på - När du läser eller redigerar en anteckning - Uppgifter - ```` - Det är väldigt lätt att göra vissa ord %1$sfeta%1$s och andra ord %2$skursiverade%2$s med Markdown. Du kan göra vissa ord %3$sgenomstrukna%3$s och till och med [länka till Nextcloud](https://nextcloud.com). Mapp för nya anteckningar: %1$s Konto importerat. - Androidappen Anteckningar kräver att Androidappen Nextcloud är minst på version 3.18. Länkar måste vara antingen kompletta URL:er som börjar med ett protokoll och domän eller en absolut sökväg som startar med tecknet %1$s. Använd avbrottstecken i en bilds URL för att anpassa den till Markdown-formatet. Till exempel ska mellanslag ersätts med %1$s i URL:en. - Sammanhangsbaserad formatteringspopup Vi upptäckte ett oåterkalleligt tillstånd för appen. Säkerhetskopiera dina osynkroniserade ändringar och rensa lagringsplatsen för Anteckningsappen. Mapp för att lagra dina anteckningar i din Nextcloud Ny filändelse: %1$s @@ -282,5 +295,9 @@ Om du tar bort kontot %1$s kommer en osynkroniserad ändring att oåterkalleligen raderas. Om du tar bort kontot %1$s kommer %2$d osynkroniserade ändringar att oåterkalleligen raderas. - Markera ett textområde eller tryck på markören vid valfri position och du kommer få upp en popup-meny med innehåll, intill standardposterna %1$s, %2$s, %3$s, likt posterna %4$s eller %5$s. - \ No newline at end of file + + .txt + .md + + Tillbaka + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1878de2ae08ee8341bdee7a454e1f1c9f6cbfa35..b29c641da0bf6ad2ba6190a57b8b67065ea8483f 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -41,6 +41,7 @@ Geçen ay Notların görüntülenme kipi + Not açılma davranışı Tema Sabit aralıklı yazı türü Yazı boyutu @@ -63,6 +64,7 @@ Şu anda %1$s kullanıyorsunuz Sorumlu Geliştiriciler + özgün yazar Çevirmenler %1$s üzerindeki Nextcloud topluluğu Deneyenler @@ -149,7 +151,7 @@ İşaret kutusu Kilidi açma notları Hayıır. Şimdi ne olacak? 🙁 - Lütfen uygulamayı kapanmaya zorlayıp yeniden başlatın. Yanlış bir Nextcloud uygulaması bağlantısı olmalı. + Lütfen uygulamayı zorla kapatıp yeniden başlatın. Yanlış bir Nextcloud uygulaması bağlantısı olmalı. Sorun sürerse, çözmek için hem Nextcloud uygulamasının hem de Nextcloud Notlar uygulamasının depolamalarını temizlemeyi deneyin. Depolamayı temizlemek için uygulama bilgilerini açın ve Depolama (Hafıza) → Depolamayı temizle (Veriyi sil) seçin. ⚠️ Uyarı: Bu işlem henüz eşitlenmemiş notları silmeyecek! Nextcloud uygulamanız eski görünüyor. Lütfen son sürümü almak için Play Store ya da F-Droid mağazalarına gidin. @@ -157,7 +159,7 @@ Zorla durdurmak sorunu çözmediyse iki uygulamanın da depolamalarını temizlemeyi deneyebilirsiniz. Belirtilen sürede sunucunuzdan bir yanıt alınamadı. Lütfen kopyanızın düzgün çalıştığından emin olun. Ağ bağlantınızı denetleyin. Mobil veriye geçmek ya da Wi-Fi bağlantısını kapatıp açmak yardımcı olabilir. - Sunucunuz doğru şekilde yanıt vermedi. Lütfen notlarınıza web arayüzü üzerinden erişebiliyor musunuz diye bakın. + Sunucunuz doğru şekilde yanıt vermedi. Lütfen notlarınıza site arayüzü üzerinden erişebiliyor musunuz diye bakın. Nextcloud kurulumunuz ile ilgili bir sorun var. Lütfen sunucu günlük dosyalarını inceleyin. Nextcloud kopyanızın bakım kipinde olmadığından emin olun. Nextcloud kopyanızda boş depolama alanı kalmamış. Lütfen yerel değişikliklerinizi bulut ile eşitlemek için bazı dosyaları silerek yer açın. @@ -177,11 +179,11 @@ Hesap yönetimi Biçimlendirme - - Düzenleme kipinde aç - Ön izleme kipinde aç - Son seçimim hatırlansın - + + Yalın düzenleme kipi + Yalın ön izleme + Zengin metin düzenleme kipi + Son seçimim hatırlansın Küçük @@ -302,4 +304,7 @@ Notlar içe aktarılıyor… Not %1$d / %2$d içe aktarılıyor… Hesap içe aktarıldı. - + Zengin düzenleme yüklenirken sorun çıktı + Yalın düzenlemeye geç + Geri + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2d72afd8f9cbae9a25dc351fcfc17933e80536ef..bf8a2d9b5c5d2ef239c73f45f240f88ee1327f0b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -4,13 +4,13 @@ Нотатки Нотатки для Nextcloud Усі нотатки - Улюблене + Вподобане Новий запис Вітаємо у %1$s Налаштування Вилучені нотатки Пошук - Метод сортування + Спосіб впорядкування Скасувати Перемкнути Редагувати @@ -41,6 +41,7 @@ Минулого місяця Показувати нотатки + Поведінка під час відкриття застосунку Нотатки Тема Моноширинний шрифт Розмір шрифту @@ -63,6 +64,7 @@ Ви використовуєте %1$s Утримувач Розробники + перший автор Перекладачі Спільнота Nextcloud на %1$s Тестувальники @@ -110,8 +112,8 @@ Відео Фільм Робочий - До роботи - До роботи + Завдання + Завдання Контрольні списки Завдання Рецепт @@ -157,7 +159,7 @@ Якщо примусове їх завершення не допомагає, спробуйте очистити сховище обох застосунків. За відведений час відповіді від вашого сервера не надійшло. Переконайтесь, що ваш сервер працює добре. Перевірте підключення до мережі. Іноді може допомогти вимкнення та ввімкнення мобільного передавання даних або Wi-Fi. - Відповідь вашого сервера була неправильною. Перевірте, чи можете ви отримати доступ до своїх нотаток через веб-інтерфейс. + Отримано неправильну відповідь від сервера. Перевірте, чи ви маєте доступ до вашіх нотаток через вебінтерфейс. Виникла проблема з налаштуванням Nextcloud. Будь ласка, подивіться файли журналу сервера. Будь ласка, перевірте, чи ваш сервер Nextcloud наразі не перебуває в режимі обслуговування. Ваш сервер Nextcloud не має вільного місця. Будь ласка, вилучіть непотрібні файлі, щоби можна було виконувати синхронізацію змін на пристрої з хмарою. @@ -167,21 +169,21 @@ Вимкніть усі оптимізації батареї для застосунків Nextcloud та Нотатки Nextcloud. Застосунок \"Нотатки\" для Android вимагає версію не менше 3.18 застосунку Nextcloud для Android. Додано \"%1$s\" - Поширений текст був порожнім + Поширений текст порожній Доповнити нотатку Змінити заголовок нотатки - Редагувати нотатку + Редагувати Безпека Вигляд і поведінка Синхронізація Облікові записи Форматування - - Відкривати у режимі редагування - Відкривати у режимі перегляду - Запам\'ятовувати останній вибір - + + Режим простого редагування + Режим простого перегляду + Режим форматованого редагування + Запам\'ятовувати останній вибір Малий @@ -238,7 +240,7 @@ Один Два Три - Іноді вам потрібні списки з позначками: + Іноді вам потрібні ненумеровані списки: Почніть рядок з риски А якщо у вас є підпункти, поставте два пробіли перед тире або зірочку: Ось так @@ -258,7 +260,7 @@ - Альберт Ейнштейн Код - Існує багато різних способів стилізувати код за допомогою Markdown. Якщо у вас є вбудовані блоки коду, оберніть їх у зворотні позначки: + Існує багато різних способів стилізувати код за допомогою Markdown. Якщо у вас є вбудовані блоки коду, оберніть їх символами зворотнього апострофу: Markdown також підтримує те, що називається огородженням коду, що дозволяє використовувати кілька рядків без відступу: А якщо ви хочете використовувати підсвічування синтаксису, додайте назву мови: @@ -312,4 +314,7 @@ Імпортування нотаток... Імпортування нотатки %1$d з %2$d... Обліковий запис імпортовано. - + Помилка під час завантаження форматованого редагування + Перемкнутися до простого редагування + Назад + diff --git a/app/src/main/res/values-v27/styles.xml b/app/src/main/res/values-v27/styles.xml index a4f7f37e965a018356e82513439d6a36fea3796b..d4d5bb767edb341fd271886fe9ca92066b625657 100644 --- a/app/src/main/res/values-v27/styles.xml +++ b/app/src/main/res/values-v27/styles.xml @@ -2,7 +2,7 @@ \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 9aa6bee4feb7dcf4167f87d8f5e950f6abb69c5c..65078d7de58925175b9e1cd473af3ab7a241a64d 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -50,6 +50,9 @@ Đồng bộ hoá trong nền Ngăn chặn chụp màn hình Hiển thị dạng lưới + Giữ màn hình luôn mở + Khi đang xem hoặc sửa ghi chú + Đồng bộ hoá thất bại: %1$s Đồng bộ hoá thất bại Không có kết nối mạng @@ -85,6 +88,8 @@ Ghi chú đơn Không tìm thấy ghi chú Vui lòng đăng nhập vào Ghi chú trước khi sử dụng tiện ích này + Biểu tượng ngôi sao thể hiện mục được yêu thích + Chọn ghi chú Tạo một ghi chú mới @@ -154,11 +159,8 @@ Quản lý tài khoản Định dạng - - Mở trong chế độ chỉnh sửa - Mở trong chế độ xem trước - Nhớ sự lựa chọn cuối của tôi - + + Nhớ sự lựa chọn cuối của tôi Nhỏ @@ -269,4 +271,5 @@ Đang nhập các ghi chú... Đang nhập ghi chú %1$d trong số %2$d... Đã nhập tài khoản. - + Quay lại + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5a9b1a915e759f26b7e254a91d8eabecda2ec9f6..6c4be0b8fa15dc741b68d80663cd0cf28cd2346b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -41,6 +41,7 @@ 上月 笔记的显示模式 + 笔记打开行为 主题 等宽字体 字体大小 @@ -50,6 +51,9 @@ 后台同步 防止截屏 网格视图 + 保持屏幕常亮 + 查看或编辑笔记时 + 同步失败:%1$s 同步失败 没有网络连接 @@ -60,13 +64,14 @@ 您正使用 %1$s 维护者 开发者 + 原作者 翻译人员 %1$s 上的 Nextcloud 社区 测试人员 源代码 - 此项目托管在 Github 上:%1$s + 此项目托管在GitHub上:%1$s 问题 - 你可以通过 GitHub 问题跟踪器报告 bug、提议增强已有功能和请求新功能:%1$s + 你可以通过GitHub议题跟踪器报告bug、提议增强已有功能和请求新功能:%1$s 翻译 加入 Transifex 上的 Nextcloud 团队,帮助我们翻译这个应用程序:%1$s 应用授权 @@ -174,11 +179,11 @@ 管理账号 格式 - - 以编辑模式打开 - 以预览模式打开 - 记住我上一次的选择 - + + 普通编辑模式 + 普通预览模式 + 富文本编辑模式 + 记住我上一次的选择 @@ -294,4 +299,7 @@ 导入笔记中… 正导入 %2$d 中的笔记 %1$d … 账号已导入 - + 加载富文本编辑时出错 + 切换到普通编辑模式 + 返回 + diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 66a4877a442b3490e39de8b07efb5fcc1c6ad32c..c28db2286fc4f8595a9f38d228eea08960100634 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -41,6 +41,7 @@ 上個月 筆記顯示模式 + Notes 打開行為 佈景主題 等寬字體 字體大小 @@ -63,6 +64,7 @@ 你目前使用 %1$s 保養員 開發員 + 原作者 翻譯員 %1$s 上的 Nextcloud 社群 測試員 @@ -177,11 +179,11 @@ 賬戶管理 正在格式化 - - 以編輯模式開啟 - 以預覽模式開啟 - 記住上次選擇 - + + 普通編輯模式 + 普通預覽 + 增強編輯模式 + 記住上次選擇 @@ -297,4 +299,7 @@ 正在導入筆記... 正導入筆記 %2$d 之 %1$d ... 已導入賬戶。 - + 載入增強編輯模式時發生錯誤 + 切換到普通編輯模式 + 返回 + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 61a77c0bbede1bdcb8e35fcd12f9fcd11c9a866b..b6c48a56427a97cb44071cd7bfa1d42e9f30698a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -177,11 +177,8 @@ 帳戶管理 文字樣式 - - 以編輯模式開啟 - 以預覽模式開啟 - 記住上次選擇 - + + 記住上次選擇 @@ -297,4 +294,5 @@ 匯入筆記中... 匯入筆記 %2$d 之 %1$d 中... 已匯入賬戶。 - + 返回 + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index a7557c374060a1b6055cdb4165353c8679af350e..7894ca84543e656169614d4ce64dd184aa16b5c2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -3,6 +3,7 @@ @string/pref_value_mode_edit @string/pref_value_mode_preview + @string/pref_value_mode_direct_edit @string/pref_value_mode_last @@ -15,4 +16,4 @@ @string/pref_value_theme_dark @string/pref_value_theme_system_default - \ No newline at end of file + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 29be49b2c8941391a4826ba5867c97ac47387b28..bf8f5ea5dbedaffd0df420440fc03d535f031772 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ Last month Display mode for notes + Notes opening behaviour Theme Monospace font Font size @@ -59,20 +60,22 @@ Server is in maintenance mode An unknown error has occurred. - https://github.com/stefan-niedermann/nextcloud-notes - https://github.com/stefan-niedermann/nextcloud-notes/issues/new/choose - https://github.com/stefan-niedermann/nextcloud-notes/blob/master/LICENSE - https://www.transifex.com/nextcloud/nextcloud/ - https://www.niedermann.it/ + https://github.com/nextcloud/notes-android + https://github.com/nextcloud/notes-android/issues/new/choose + https://github.com/nextcloud/notes-android/blob/main/LICENSE + https://app.transifex.com/nextcloud/nextcloud/ + https://www.niedermann.it/ https://github.com/nextcloud/notes/blob/76d15214f80f2bf7ea08427bff73ad145128f090/img/notes.svg https://materialdesignicons.com/ Version You are currently using %1$s Maintainer - Niedermann IT-Dienstleistungen + Nextcloud Developers - Stefan Niedermann, Kristof Hamann, HeaDBanGer84, Felix Edelmann, Daniel Bailey + %1$s, Kristof Hamann, HeaDBanGer84, Felix Edelmann, Daniel Bailey + Stefan Niedermann (%1$s) + original author Translators Nextcloud community on %1$s Transifex @@ -120,6 +123,7 @@ lastNoteMode backgroundSync edit + directEdit preview last small @@ -211,10 +215,17 @@ Manage accounts Formatting - - Open in edit mode - Open in preview mode - Remember my last selection + + Plain edit mode + Plain preview + Rich edit mode + Remember my last selection + + + @string/noteMode_plain_edit + @string/noteMode_plain_preview + @string/noteMode_rich_edit + @string/noteMode_remember_last @@ -313,7 +324,7 @@ Links have to be either complete URLs starting with a protocol and domain or absolute paths starting with a %1$s character. In order to conform to the Markdown format, please use escape characters in the image URL. This means for example, replace spaces with %1$s in the URL. - %20 + %20 / Images Fancy image @@ -369,4 +380,8 @@ Authors # We faced a conflict between your notes. Please choose the proper one. \n\n***Note %1$d (Last modified: %2$s):***\n + Error while loading rich editing + Switch to plain editing + Back + Mozilla/5.0 (Android) %1$s-android/%2$s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8bbadf582b263812886494795f13725909f82a4a..ff731cf1dcd3f914a18b5027a247bd42a65dd2e7 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -7,13 +7,13 @@ @color/accent @bool/isDayMode - ?attr/colorAccent - ?attr/colorAccent + @color/accent + @color/accent @color/color_default_primary_text - ?android:colorAccent + @color/accent @color/defaultTextHighlightBackground true - ?attr/colorPrimary + @color/bg_default @@ -77,12 +77,17 @@ @style/Toolbar.Button.Navigation.Tinted + +