Browse Source

Merge master

Signed-off-by: alperozturk <alper_ozturk@proton.me>
alperozturk 1 year ago
parent
commit
f378d61e6b
60 changed files with 1190 additions and 1915 deletions
  1. 1 1
      .github/workflows/analysis.yml
  2. 1 1
      .github/workflows/assembleFlavors.yml
  3. 1 1
      .github/workflows/check.yml
  4. 7 3
      .github/workflows/codeql.yml
  5. 3 3
      .github/workflows/command-rebase.yml
  6. 1 1
      .github/workflows/detectWrongSettings.yml
  7. 1 1
      .github/workflows/gradle-wrapper-validation.yml
  8. 1 1
      .github/workflows/qa.yml
  9. 3 3
      .github/workflows/scorecard.yml
  10. 1 1
      .github/workflows/screenShotTest.yml
  11. 1 1
      .github/workflows/unit-tests.yml
  12. 22 1
      CHANGELOG.md
  13. 12 6
      README.md
  14. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png
  15. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png
  16. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew.png
  17. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles.png
  18. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png
  19. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png
  20. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png
  21. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog.png
  22. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png
  23. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png
  24. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png
  25. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png
  26. BIN
      app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png
  27. 1 1
      app/src/androidTest/java/com/owncloud/android/AbstractIT.java
  28. 14 4
      app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java
  29. 2 26
      app/src/androidTest/java/com/owncloud/android/UploadIT.java
  30. 2 1
      app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt
  31. 11 5
      app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt
  32. 0 4
      app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
  33. 19 19
      app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt
  34. 6 0
      app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt
  35. 19 5
      app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.java
  36. 18 8
      app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java
  37. 16 14
      app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt
  38. 9 5
      app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java
  39. 20 13
      app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java
  40. 17 4
      app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java
  41. 0 144
      app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java
  42. 35 14
      app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java
  43. 6 5
      app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java
  44. 0 329
      app/src/main/java/com/owncloud/android/ui/dialog/SslValidatorDialog.java
  45. 30 37
      app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt
  46. 9 0
      app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java
  47. 0 691
      app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.java
  48. 726 0
      app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt
  49. 16 22
      app/src/main/res/layout/backup_fragment.xml
  50. 0 443
      app/src/main/res/layout/ssl_validator_layout.xml
  51. 0 58
      app/src/main/res/layout/storage_permission_dialog.xml
  52. 12 12
      app/src/main/res/values-eu/strings.xml
  53. 67 0
      app/src/main/res/values-ja-rJP/strings.xml
  54. 1 1
      app/src/main/res/values-ko/strings.xml
  55. 74 21
      app/src/main/res/values-pt-rPT/strings.xml
  56. 1 0
      app/src/main/res/values-vi/strings.xml
  57. 2 1
      app/src/main/res/values/dims.xml
  58. 0 2
      app/src/main/res/values/strings.xml
  59. 1 1
      scripts/analysis/analysis-wrapper.sh
  60. 1 1
      scripts/analysis/lint-results.txt

+ 1 - 1
.github/workflows/analysis.yml

@@ -33,7 +33,7 @@ jobs:
                         echo "pr=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
                         echo "pr=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
                         echo "repo=${{ github.event.pull_request.head.repo.full_name }}" >> "$GITHUB_OUTPUT"
                         echo "repo=${{ github.event.pull_request.head.repo.full_name }}" >> "$GITHUB_OUTPUT"
                     fi
                     fi
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
                 with:
                 with:
                     repository: ${{ steps.get-vars.outputs.repo }}
                     repository: ${{ steps.get-vars.outputs.repo }}
                     ref: ${{ steps.get-vars.outputs.branch }}
                     ref: ${{ steps.get-vars.outputs.branch }}

+ 1 - 1
.github/workflows/assembleFlavors.yml

@@ -19,7 +19,7 @@ jobs:
             matrix:
             matrix:
                 flavor: [ Generic, Gplay, Huawei ]
                 flavor: [ Generic, Gplay, Huawei ]
         steps:
         steps:
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v3
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: set up JDK 17
             -   name: set up JDK 17
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 with:
                 with:

+ 1 - 1
.github/workflows/check.yml

@@ -19,7 +19,7 @@ jobs:
             matrix:
             matrix:
                 task: [ detekt, spotlessKotlinCheck ]
                 task: [ detekt, spotlessKotlinCheck ]
         steps:
         steps:
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v3
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: Set up JDK 17
             -   name: Set up JDK 17
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 with:
                 with:

+ 7 - 3
.github/workflows/codeql.yml

@@ -12,6 +12,10 @@ on:
 permissions:
 permissions:
   contents: read
   contents: read
 
 
+concurrency:
+    group: code-ql-${{ github.head_ref || github.run_id }}
+    cancel-in-progress: true
+
 jobs:
 jobs:
   analyze:
   analyze:
     name: Analyze
     name: Analyze
@@ -26,13 +30,13 @@ jobs:
         language: [ 'java' ]
         language: [ 'java' ]
     steps:
     steps:
       - name: Checkout repository
       - name: Checkout repository
-        uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
       - name: Set Swap Space
       - name: Set Swap Space
         uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0
         uses: pierotofy/set-swap-space@49819abfb41bd9b44fb781159c033dba90353a7c # v1.0
         with:
         with:
           swap-size-gb: 10
           swap-size-gb: 10
       - name: Initialize CodeQL
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 # v2.22.3
+        uses: github/codeql-action/init@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4
         with:
         with:
           languages: ${{ matrix.language }}
           languages: ${{ matrix.language }}
       - name: Set up JDK 17
       - name: Set up JDK 17
@@ -46,4 +50,4 @@ jobs:
           echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties"
           echo "org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError" > "$HOME/.gradle/gradle.properties"
           ./gradlew assembleDebug
           ./gradlew assembleDebug
       - name: Perform CodeQL Analysis
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 # v2.22.3
+        uses: github/codeql-action/analyze@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4

+ 3 - 3
.github/workflows/command-rebase.yml

@@ -23,7 +23,7 @@ jobs:
 
 
     steps:
     steps:
       - name: Add reaction on start
       - name: Add reaction on start
-        uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2
+        uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3.1.0
         with:
         with:
           token: ${{ secrets.COMMAND_BOT_PAT }}
           token: ${{ secrets.COMMAND_BOT_PAT }}
           repository: ${{ github.event.repository.full_name }}
           repository: ${{ github.event.repository.full_name }}
@@ -31,7 +31,7 @@ jobs:
           reaction-type: "+1"
           reaction-type: "+1"
 
 
       - name: Checkout the latest code
       - name: Checkout the latest code
-        uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
         with:
         with:
           fetch-depth: 0
           fetch-depth: 0
           token: ${{ secrets.COMMAND_BOT_PAT }}
           token: ${{ secrets.COMMAND_BOT_PAT }}
@@ -42,7 +42,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
           GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
 
 
       - name: Add reaction on failure
       - name: Add reaction on failure
-        uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2
+        uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3.1.0
         if: failure()
         if: failure()
         with:
         with:
           token: ${{ secrets.COMMAND_BOT_PAT }}
           token: ${{ secrets.COMMAND_BOT_PAT }}

+ 1 - 1
.github/workflows/detectWrongSettings.yml

@@ -16,7 +16,7 @@ jobs:
         runs-on: ubuntu-22.04
         runs-on: ubuntu-22.04
 
 
         steps:
         steps:
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v3
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: Set up JDK 17
             -   name: Set up JDK 17
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 with:
                 with:

+ 1 - 1
.github/workflows/gradle-wrapper-validation.yml

@@ -18,5 +18,5 @@ jobs:
         name: "Validation"
         name: "Validation"
         runs-on: ubuntu-latest
         runs-on: ubuntu-latest
         steps:
         steps:
-            - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+            - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
             - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # v1.1.0
             - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # v1.1.0

+ 1 - 1
.github/workflows/qa.yml

@@ -19,7 +19,7 @@ jobs:
             -   name: Check if secrets are available
             -   name: Check if secrets are available
                 run: echo "::set-output name=ok::${{ secrets.KS_PASS != '' }}"
                 run: echo "::set-output name=ok::${{ secrets.KS_PASS != '' }}"
                 id: check-secrets
                 id: check-secrets
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v3
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
                 if: ${{ steps.check-secrets.outputs.ok == 'true' }}
                 if: ${{ steps.check-secrets.outputs.ok == 'true' }}
             -   name: set up JDK 17
             -   name: set up JDK 17
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3

+ 3 - 3
.github/workflows/scorecard.yml

@@ -24,12 +24,12 @@ jobs:
 
 
     steps:
     steps:
       - name: "Checkout code"
       - name: "Checkout code"
-        uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
         with:
         with:
           persist-credentials: false
           persist-credentials: false
 
 
       - name: "Run analysis"
       - name: "Run analysis"
-        uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
+        uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
         with:
         with:
           results_file: results.sarif
           results_file: results.sarif
           results_format: sarif
           results_format: sarif
@@ -37,6 +37,6 @@ jobs:
 
 
       # Upload the results to GitHub's code scanning dashboard.
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 # v2.22.3
+        uses: github/codeql-action/upload-sarif@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4
         with:
         with:
           sarif_file: results.sarif
           sarif_file: results.sarif

+ 1 - 1
.github/workflows/screenShotTest.yml

@@ -22,7 +22,7 @@ jobs:
                 color: [ blue ]
                 color: [ blue ]
                 api-level: [ 27 ]
                 api-level: [ 27 ]
         steps:
         steps:
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v3
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
 
 
             -   name: Gradle cache
             -   name: Gradle cache
                 uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3
                 uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3

+ 1 - 1
.github/workflows/unit-tests.yml

@@ -18,7 +18,7 @@ jobs:
     test:
     test:
         runs-on: ubuntu-latest
         runs-on: ubuntu-latest
         steps:
         steps:
-            -   uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+            -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
             -   name: Set up JDK 17
             -   name: Set up JDK 17
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
                 uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
                 with:
                 with:

+ 22 - 1
CHANGELOG.md

@@ -1,9 +1,30 @@
-## 3.24.1 (February 21, 2022)
+## 3.26.0 (September 16, 2023)
+
+- image editing
+- image details, with map
+- show other Nextcloud apps
+
+Minimum: NC 16 Server, Android 6.0 Marshmallow
+
+For a full list, please see https://github.com/nextcloud/android/milestone/84
+
+## 3.25.0 (June 13, 2023)
+
+- show Groupfolder
+- Tag in file listing
+
+Minimum: NC 16 Server, Android 6.0 Marshmallow
+
+For a full list, please see https://github.com/nextcloud/android/milestone/81
+
+## 3.24.1 (February 21, 2023)
 
 
 - Fix crash in previous version when connecting to old server versions
 - Fix crash in previous version when connecting to old server versions
 
 
 Minimum: NC 16 Server, Android 6.0 Marshmallow
 Minimum: NC 16 Server, Android 6.0 Marshmallow
 
 
+For a full list, please see https://github.com/nextcloud/android/milestone/80
+
 ## 3.24.0 (February 13, 2023)
 ## 3.24.0 (February 13, 2023)
 
 
 - Several performance optimizations by @starypatyk
 - Several performance optimizations by @starypatyk

+ 12 - 6
README.md

@@ -15,11 +15,17 @@ height="80">](https://f-droid.org/packages/com.nextcloud.client/)
 
 
 ## How to contribute :rocket:
 ## How to contribute :rocket:
 
 
-If you want to [contribute](https://nextcloud.com/contribute/) to Nextcloud, you are very welcome:
-
-*   our forum at https://help.nextcloud.com
-*   for translations of the app on [Transifex](https://app.transifex.com/nextcloud/nextcloud/android/)
-*   opening issues and PRs (including a corresponding issue)
+If you want to [contribute](https://nextcloud.com/contribute/) to the Nextcloud Android client app, there are many ways to help whether or not you are a coder: 
+
+*   helping out other users on our forum at https://help.nextcloud.com
+*   providing translations of the app on [Transifex](https://app.transifex.com/nextcloud/nextcloud/android/)
+*   reporting problems / suggesting enhancements by [opening new issues](https://github.com/nextcloud/android/issues/new/choose)
+*   implementing proposed bug fixes and enhancement ideas by submitting PRs (associated with a corresponding issue preferably)
+*   reviewing [pull requests](https://github.com/nextcloud/android/pulls) and providing feedback on code, implementation, and functionality
+*   installing and testing [pull request builds](https://github.com/nextcloud/android/pulls), [daily/dev builds](https://github.com/nextcloud/android#development-version-hammer), or [RCs/release candidate builds](https://github.com/nextcloud/android/releases) 
+*   enhancing Admin, User, or Developer [documentation](https://github.com/nextcloud/documentation/)
+*   hitting hard on the latest stable release by testing fundamental features and evaluating the user experience
+*   proactively getting familiar with [how to gather debug logs](https://github.com/nextcloud/android#getting-debug-info-via-logcat-mag) from your devices (so that you are prepared to provide a detailed report if you encounter a problem with the app in the future)
 
 
 ## Contribution Guidelines & License :scroll:
 ## Contribution Guidelines & License :scroll:
 
 
@@ -38,7 +44,7 @@ More information on how to contribute: <https://nextcloud.com/contribute/>
 ## Start contributing :hammer\_and\_wrench:
 ## Start contributing :hammer\_and\_wrench:
 
 
 Make sure you read [SETUP.md](https://github.com/nextcloud/android/blob/master/SETUP.md) and [CONTRIBUTING.md](https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md) before you start working on this project. But basically: fork this repository and contribute back using pull requests to the master branch.
 Make sure you read [SETUP.md](https://github.com/nextcloud/android/blob/master/SETUP.md) and [CONTRIBUTING.md](https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md) before you start working on this project. But basically: fork this repository and contribute back using pull requests to the master branch.
-Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/android/pulls) and working on [starter issues](https://github.com/nextcloud/android/issues?q=is%3Aopen+is%3Aissue+label%3A%22starter+issue%22).
+Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/android/pulls) and working on [starter issues](https://github.com/nextcloud/android/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
 
 
 ### Getting debug info via logcat :mag:
 ### Getting debug info via logcat :mag:
 
 

BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepBoth.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepExisting.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_keepNew.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ConflictsResolveActivityIT_screenshotTextFiles.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.activity.ContactsPreferenceActivityIT_openContactsPreference.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testNewFolderDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFileDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFilesDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFolderDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testRemoveFoldersDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.dialog.DialogFragmentIT_testStoragePermissionDialog.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailActivitiesFragment.png


BIN
app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailFragmentStaticServerIT_showFileDetailDetailsFragment.png


+ 1 - 1
app/src/androidTest/java/com/owncloud/android/AbstractIT.java

@@ -314,7 +314,7 @@ public abstract class AbstractIT {
         return currentActivity;
         return currentActivity;
     }
     }
 
 
-    protected void shortSleep() {
+    protected static void shortSleep() {
         try {
         try {
             Thread.sleep(2000);
             Thread.sleep(2000);
         } catch (InterruptedException e) {
         } catch (InterruptedException e) {

+ 14 - 4
app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java

@@ -135,10 +135,20 @@ public abstract class AbstractOnServerIT extends AbstractIT {
                                    .isSuccess());
                                    .isSuccess());
                 }
                 }
 
 
-                assertTrue(new RemoveFileRemoteOperation(remoteFile.getRemotePath())
-                               .execute(client)
-                               .isSuccess()
-                          );
+                boolean removeResult = false;
+                for (int i = 0; i < 5; i++) {
+                    removeResult = new RemoveFileRemoteOperation(remoteFile.getRemotePath())
+                        .execute(client)
+                        .isSuccess();
+                    
+                    if (removeResult) {
+                        break;
+                    }
+
+                    shortSleep();
+                }
+
+                assertTrue(removeResult);
             }
             }
         }
         }
     }
     }

+ 2 - 26
app/src/androidTest/java/com/owncloud/android/UploadIT.java

@@ -41,7 +41,6 @@ import com.owncloud.android.operations.RemoveFileOperation;
 import com.owncloud.android.operations.UploadFileOperation;
 import com.owncloud.android.operations.UploadFileOperation;
 import com.owncloud.android.utils.FileStorageUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.Test;
 
 
@@ -106,29 +105,6 @@ public class UploadIT extends AbstractOnServerIT {
         createDummyFiles();
         createDummyFiles();
     }
     }
 
 
-    @After
-    public void after() {
-        RemoteOperationResult result = new RefreshFolderOperation(getStorageManager().getFileByPath("/"),
-                                                                  System.currentTimeMillis() / 1000L,
-                                                                  false,
-                                                                  true,
-                                                                  getStorageManager(),
-                                                                  user,
-                                                                  targetContext)
-            .execute(client);
-
-        // cleanup only if folder exists
-        if (result.isSuccess() && getStorageManager().getFileByDecryptedRemotePath(FOLDER) != null) {
-            new RemoveFileOperation(getStorageManager().getFileByDecryptedRemotePath(FOLDER),
-                                    false,
-                                    user,
-                                    false,
-                                    targetContext,
-                                    getStorageManager())
-                .execute(client);
-        }
-    }
-
     @Test
     @Test
     public void testEmptyUpload() {
     public void testEmptyUpload() {
         OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
         OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
@@ -529,8 +505,8 @@ public class UploadIT extends AbstractOnServerIT {
 
 
         assertNotNull(ocFile);
         assertNotNull(ocFile);
         assertEquals(remotePath, ocFile.getRemotePath());
         assertEquals(remotePath, ocFile.getRemotePath());
-        assertEquals(new ImageDimension(451f, 529f), ocFile.getImageDimension());
-        assertEquals(new GeoLocation(49.99679166666667, 8.67198611111111), ocFile.getGeoLocation());
+        assertEquals(new ImageDimension(300f, 200f), ocFile.getImageDimension());
+        assertEquals(new GeoLocation(64, -46), ocFile.getGeoLocation());
     }
     }
 
 
     private void verifyStoragePath(OCFile file) {
     private void verifyStoragePath(OCFile file) {

+ 2 - 1
app/src/androidTest/java/com/owncloud/android/ui/LoginIT.kt

@@ -20,6 +20,7 @@
  */
  */
 package com.owncloud.android.ui
 package com.owncloud.android.ui
 
 
+import android.os.Build
 import androidx.test.core.app.ActivityScenario
 import androidx.test.core.app.ActivityScenario
 import androidx.test.espresso.Espresso
 import androidx.test.espresso.Espresso
 import androidx.test.espresso.action.ViewActions
 import androidx.test.espresso.action.ViewActions
@@ -65,7 +66,7 @@ class LoginIT : AbstractIT() {
      * The CI/CD pipeline is encountering issues related to the Android version for this functionality.
      * The CI/CD pipeline is encountering issues related to the Android version for this functionality.
      * Therefore the test will only be executed on Android versions 10 and above.
      * Therefore the test will only be executed on Android versions 10 and above.
      */
      */
-    @SdkSuppress(minSdkVersion = 29)
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     fun login() {
     fun login() {
         val arguments = InstrumentationRegistry.getArguments()
         val arguments = InstrumentationRegistry.getArguments()
         val baseUrl = arguments.getString("TEST_SERVER_URL")!!
         val baseUrl = arguments.getString("TEST_SERVER_URL")!!

+ 11 - 5
app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt

@@ -73,14 +73,19 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
     @Test
     @Test
     @ScreenshotTest
     @ScreenshotTest
     fun showFileDetailDetailsFragment() {
     fun showFileDetailDetailsFragment() {
-        val sut = testActivityRule.launchActivity(null)
-        sut.addFragment(ImageDetailFragment.newInstance(oCFile, user))
+        val activity = testActivityRule.launchActivity(null)
+        val sut = ImageDetailFragment.newInstance(oCFile, user)
+        activity.addFragment(sut)
 
 
-        waitForIdleSync()
-        shortSleep()
         shortSleep()
         shortSleep()
         shortSleep()
         shortSleep()
-        screenshot(sut)
+        waitForIdleSync()
+
+        activity.runOnUiThread {
+            sut.hideMap()
+        }
+
+        screenshot(activity)
     }
     }
 
 
     @Test
     @Test
@@ -182,6 +187,7 @@ class FileDetailFragmentStaticServerIT : AbstractIT() {
         waitForIdleSync()
         waitForIdleSync()
 
 
         activity.runOnUiThread {
         activity.runOnUiThread {
+            sut.fileDetailActivitiesFragment.disableLoadingActivities()
             sut
             sut
                 .fileDetailActivitiesFragment
                 .fileDetailActivitiesFragment
                 .setErrorContent(targetContext.resources.getString(R.string.file_detail_activity_error))
                 .setErrorContent(targetContext.resources.getString(R.string.file_detail_activity_error))

+ 0 - 4
app/src/main/java/com/nextcloud/client/di/ComponentsModule.java

@@ -92,7 +92,6 @@ import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.LocalStoragePathPickerDialogFragment;
 import com.owncloud.android.ui.dialog.LocalStoragePathPickerDialogFragment;
 import com.owncloud.android.ui.dialog.MultipleAccountsDialog;
 import com.owncloud.android.ui.dialog.MultipleAccountsDialog;
-import com.owncloud.android.ui.dialog.NoteDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
 import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
 import com.owncloud.android.ui.dialog.RenamePublicShareDialogFragment;
 import com.owncloud.android.ui.dialog.RenamePublicShareDialogFragment;
@@ -404,9 +403,6 @@ abstract class ComponentsModule {
     @ContributesAndroidInjector
     @ContributesAndroidInjector
     abstract Migrations migrations();
     abstract Migrations migrations();
 
 
-    @ContributesAndroidInjector
-    abstract NoteDialogFragment noteDialogFragment();
-
     @ContributesAndroidInjector
     @ContributesAndroidInjector
     abstract NotificationWork notificationWork();
     abstract NotificationWork notificationWork();
 
 

+ 19 - 19
app/src/main/java/com/nextcloud/client/jobs/FilesExportWork.kt

@@ -29,7 +29,6 @@ import android.content.ContentResolver
 import android.content.Context
 import android.content.Context
 import android.content.Intent
 import android.content.Intent
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
-import android.graphics.BitmapFactory
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationCompat
 import androidx.work.Worker
 import androidx.work.Worker
 import androidx.work.WorkerParameters
 import androidx.work.WorkerParameters
@@ -69,9 +68,7 @@ class FilesExportWork(
 
 
         val successfulExports = exportFiles(fileIDs)
         val successfulExports = exportFiles(fileIDs)
 
 
-        // show notification
         showSuccessNotification(successfulExports)
         showSuccessNotification(successfulExports)
-
         return Result.success()
         return Result.success()
     }
     }
 
 
@@ -105,7 +102,13 @@ class FilesExportWork(
 
 
     @Throws(IllegalStateException::class)
     @Throws(IllegalStateException::class)
     private fun exportFile(ocFile: OCFile) {
     private fun exportFile(ocFile: OCFile) {
-        FileExportUtils().exportFile(ocFile.fileName, ocFile.mimeType, contentResolver, ocFile, null)
+        FileExportUtils().exportFile(
+            ocFile.fileName,
+            ocFile.mimeType,
+            contentResolver,
+            ocFile,
+            null
+        )
     }
     }
 
 
     private fun downloadFile(ocFile: OCFile) {
     private fun downloadFile(ocFile: OCFile) {
@@ -119,19 +122,16 @@ class FilesExportWork(
     }
     }
 
 
     private fun showErrorNotification(successfulExports: Int) {
     private fun showErrorNotification(successfulExports: Int) {
-        if (successfulExports == 0) {
-            showNotification(
-                appContext.resources.getQuantityString(R.plurals.export_failed, successfulExports, successfulExports)
-            )
+        val message = if (successfulExports == 0) {
+            appContext.resources.getQuantityString(R.plurals.export_failed, successfulExports, successfulExports)
         } else {
         } else {
-            showNotification(
-                appContext.resources.getQuantityString(
-                    R.plurals.export_partially_failed,
-                    successfulExports,
-                    successfulExports
-                )
+            appContext.resources.getQuantityString(
+                R.plurals.export_partially_failed,
+                successfulExports,
+                successfulExports
             )
             )
         }
         }
+        showNotification(message)
     }
     }
 
 
     private fun showSuccessNotification(successfulExports: Int) {
     private fun showSuccessNotification(successfulExports: Int) {
@@ -152,9 +152,7 @@ class FilesExportWork(
             NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD
             NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD
         )
         )
             .setSmallIcon(R.drawable.notification_icon)
             .setSmallIcon(R.drawable.notification_icon)
-            .setLargeIcon(BitmapFactory.decodeResource(appContext.resources, R.drawable.notification_icon))
-            .setSubText(user.accountName)
-            .setContentText(message)
+            .setContentTitle(message)
             .setAutoCancel(true)
             .setAutoCancel(true)
 
 
         viewThemeUtils.androidx.themeNotificationCompatBuilder(appContext, notificationBuilder)
         viewThemeUtils.androidx.themeNotificationCompatBuilder(appContext, notificationBuilder)
@@ -166,7 +164,8 @@ class FilesExportWork(
             appContext,
             appContext,
             notificationId,
             notificationId,
             actionIntent,
             actionIntent,
-            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
+            PendingIntent.FLAG_CANCEL_CURRENT or
+                PendingIntent.FLAG_IMMUTABLE
         )
         )
         notificationBuilder.addAction(
         notificationBuilder.addAction(
             NotificationCompat.Action(
             NotificationCompat.Action(
@@ -176,7 +175,8 @@ class FilesExportWork(
             )
             )
         )
         )
 
 
-        val notificationManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+        val notificationManager = appContext
+            .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
         notificationManager.notify(notificationId, notificationBuilder.build())
         notificationManager.notify(notificationId, notificationBuilder.build())
     }
     }
 
 

+ 6 - 0
app/src/main/java/com/nextcloud/ui/ImageDetailFragment.kt

@@ -30,6 +30,7 @@ import android.os.Parcelable
 import android.view.LayoutInflater
 import android.view.LayoutInflater
 import android.view.View
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
+import androidx.annotation.VisibleForTesting
 import androidx.core.content.ContextCompat
 import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.Fragment
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
 import com.nextcloud.android.common.ui.theme.utils.ColorRole
@@ -260,6 +261,11 @@ class ImageDetailFragment : Fragment(), Injectable {
         binding.imageLocationMapCopyright.text = binding.imageLocationMap.tileProvider.tileSource.copyrightNotice
         binding.imageLocationMapCopyright.text = binding.imageLocationMap.tileProvider.tileSource.copyrightNotice
     }
     }
 
 
+    @VisibleForTesting
+    fun hideMap() {
+        binding.imageLocationMap.visibility = View.GONE
+    }
+
     @SuppressLint("SimpleDateFormat")
     @SuppressLint("SimpleDateFormat")
     private fun gatherMetadata() {
     private fun gatherMetadata() {
         val fileSize = DisplayUtils.bytesToHumanReadable(file.fileLength)
         val fileSize = DisplayUtils.bytesToHumanReadable(file.fileLength)

+ 19 - 5
app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.java

@@ -25,6 +25,7 @@ package com.owncloud.android.ui.dialog;
 import android.app.Dialog;
 import android.app.Dialog;
 import android.os.Bundle;
 import android.os.Bundle;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
@@ -59,7 +60,11 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
     @Override
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         super.onCreate(savedInstanceState);
-        user = getArguments().getParcelable(KEY_USER);
+
+        Bundle arguments = getArguments();
+        if (arguments != null) {
+            user = arguments.getParcelable(KEY_USER);
+        }
     }
     }
 
 
     @Override
     @Override
@@ -67,9 +72,18 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
         super.onStart();
         super.onStart();
 
 
         AlertDialog alertDialog = (AlertDialog) getDialog();
         AlertDialog alertDialog = (AlertDialog) getDialog();
-
-        viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                                                 alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
+        if (alertDialog != null) {
+
+            MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+            if (positiveButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+            }
+
+            MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+            if (negativeButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
+            }
+        }
     }
     }
 
 
     @NonNull
     @NonNull
@@ -82,7 +96,7 @@ public class AccountRemovalConfirmationDialog extends DialogFragment implements
             .setPositiveButton(R.string.common_ok,
             .setPositiveButton(R.string.common_ok,
                                (dialogInterface, i) -> backgroundJobManager.startAccountRemovalJob(user.getAccountName(),
                                (dialogInterface, i) -> backgroundJobManager.startAccountRemovalJob(user.getAccountName(),
                                                                                                    false))
                                                                                                    false))
-            .setNeutralButton(R.string.common_cancel, null);
+            .setNegativeButton(R.string.common_cancel, null);
 
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder);
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder);
 
 

+ 18 - 8
app/src/main/java/com/owncloud/android/ui/dialog/ChooseRichDocumentsTemplateDialogFragment.java

@@ -35,6 +35,7 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View;
 import android.widget.Button;
 import android.widget.Button;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets;
 import com.nextcloud.client.account.CurrentAccountProvider;
 import com.nextcloud.client.account.CurrentAccountProvider;
@@ -99,7 +100,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
     private RichDocumentsTemplateAdapter adapter;
     private RichDocumentsTemplateAdapter adapter;
     private OCFile parentFolder;
     private OCFile parentFolder;
     private OwnCloudClient client;
     private OwnCloudClient client;
-    private Button positiveButton;
+    private MaterialButton positiveButton;
     private DialogFragment waitDialog;
     private DialogFragment waitDialog;
 
 
     public enum Type {
     public enum Type {
@@ -126,11 +127,18 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
 
 
         AlertDialog alertDialog = (AlertDialog) getDialog();
         AlertDialog alertDialog = (AlertDialog) getDialog();
 
 
-        positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-        viewThemeUtils.platform.colorTextButtons(positiveButton,
-                                                 alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
-        positiveButton.setOnClickListener(this);
-        positiveButton.setEnabled(false);
+        if (alertDialog != null) {
+            positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+
+            MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+            if (negativeButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
+            }
+
+            positiveButton.setOnClickListener(this);
+            positiveButton.setEnabled(false);
+        }
 
 
         checkEnablingCreateButton();
         checkEnablingCreateButton();
     }
     }
@@ -205,12 +213,14 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
             }
             }
         });
         });
 
 
+        int titleTextId = getTitle(type);
+
         // Build the dialog
         // Build the dialog
         MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
         MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity);
         builder.setView(view)
         builder.setView(view)
             .setPositiveButton(R.string.create, null)
             .setPositiveButton(R.string.create, null)
-            .setNeutralButton(R.string.common_cancel, null)
-            .setTitle(getTitle(type));
+            .setNegativeButton(R.string.common_cancel, null)
+            .setTitle(titleTextId);
 
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(activity, builder);
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(activity, builder);
 
 

+ 16 - 14
app/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.kt

@@ -32,10 +32,10 @@ import android.os.Bundle
 import android.text.Editable
 import android.text.Editable
 import android.text.TextWatcher
 import android.text.TextWatcher
 import android.view.View
 import android.view.View
-import android.widget.Button
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.DialogFragment
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.GridLayoutManager
+import com.google.android.material.button.MaterialButton
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.nextcloud.android.lib.resources.directediting.DirectEditingCreateFileRemoteOperation
 import com.nextcloud.android.lib.resources.directediting.DirectEditingCreateFileRemoteOperation
 import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainListOfTemplatesRemoteOperation
 import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainListOfTemplatesRemoteOperation
@@ -90,7 +90,7 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
     private var adapter: TemplateAdapter? = null
     private var adapter: TemplateAdapter? = null
     private var parentFolder: OCFile? = null
     private var parentFolder: OCFile? = null
     private var title: String? = null
     private var title: String? = null
-    private var positiveButton: Button? = null
+    private var positiveButton: MaterialButton? = null
     private var creator: Creator? = null
     private var creator: Creator? = null
 
 
     enum class Type {
     enum class Type {
@@ -103,17 +103,18 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
         val alertDialog = dialog as AlertDialog
         val alertDialog = dialog as AlertDialog
-        val button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
 
 
-        viewThemeUtils.platform.colorTextButtons(
-            button,
-            alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL)
-        )
-        button.setOnClickListener(this)
-        button.isEnabled = false
-        button.isClickable = false
+        val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton
+        viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton)
+
+        val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton
+        viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton)
 
 
-        positiveButton = button
+        positiveButton.setOnClickListener(this)
+        positiveButton.isEnabled = false
+        positiveButton.isClickable = false
+
+        this.positiveButton = positiveButton
         checkEnablingCreateButton()
         checkEnablingCreateButton()
     }
     }
 
 
@@ -128,6 +129,7 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
 
 
         parentFolder = arguments.getParcelable(ARG_PARENT_FOLDER)
         parentFolder = arguments.getParcelable(ARG_PARENT_FOLDER)
         creator = arguments.getParcelable(ARG_CREATOR)
         creator = arguments.getParcelable(ARG_CREATOR)
+
         title = arguments.getString(ARG_HEADLINE, getString(R.string.select_template))
         title = arguments.getString(ARG_HEADLINE, getString(R.string.select_template))
         title = when (savedInstanceState) {
         title = when (savedInstanceState) {
             null -> arguments.getString(ARG_HEADLINE)
             null -> arguments.getString(ARG_HEADLINE)
@@ -175,7 +177,7 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
         val builder = MaterialAlertDialogBuilder(activity)
         val builder = MaterialAlertDialogBuilder(activity)
         builder.setView(view)
         builder.setView(view)
             .setPositiveButton(R.string.create, null)
             .setPositiveButton(R.string.create, null)
-            .setNeutralButton(R.string.common_cancel, null)
+            .setNegativeButton(R.string.common_cancel, null)
             .setTitle(title)
             .setTitle(title)
 
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.list.context, builder)
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.list.context, builder)
@@ -208,8 +210,8 @@ class ChooseTemplateDialogFragment : DialogFragment(), View.OnClickListener, Tem
     }
     }
 
 
     fun setTemplateList(templateList: TemplateList?) {
     fun setTemplateList(templateList: TemplateList?) {
-        adapter!!.setTemplateList(templateList)
-        adapter!!.notifyDataSetChanged()
+        adapter?.setTemplateList(templateList)
+        adapter?.notifyDataSetChanged()
     }
     }
 
 
     override fun onClick(template: Template) {
     override fun onClick(template: Template) {

+ 9 - 5
app/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java

@@ -29,6 +29,7 @@ import android.view.View;
 import android.widget.Button;
 import android.widget.Button;
 import android.widget.Toast;
 import android.widget.Toast;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
@@ -70,7 +71,7 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
     public OnConflictDecisionMadeListener listener;
     public OnConflictDecisionMadeListener listener;
     private User user;
     private User user;
     private final List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
     private final List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks = new ArrayList<>();
-    private Button positiveButton;
+    private MaterialButton positiveButton;
     @Inject ViewThemeUtils viewThemeUtils;
     @Inject ViewThemeUtils viewThemeUtils;
     @Inject SyncedFolderProvider syncedFolderProvider;
     @Inject SyncedFolderProvider syncedFolderProvider;
 
 
@@ -119,9 +120,11 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
             return;
             return;
         }
         }
 
 
-        positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-        viewThemeUtils.platform.colorTextButtons(positiveButton,
-                                                 alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
+        positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+        MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+
+        viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+        viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
         positiveButton.setEnabled(false);
         positiveButton.setEnabled(false);
     }
     }
 
 
@@ -175,7 +178,7 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
 
 
                 }
                 }
             })
             })
-            .setNeutralButton(R.string.common_cancel, (dialog, which) -> {
+            .setNegativeButton(R.string.common_cancel, (dialog, which) -> {
                 if (listener != null) {
                 if (listener != null) {
                     listener.conflictDecisionMade(Decision.CANCEL);
                     listener.conflictDecisionMade(Decision.CANCEL);
                 }
                 }
@@ -275,4 +278,5 @@ public class ConflictsResolveDialog extends DialogFragment implements Injectable
 
 
         asyncTasks.clear();
         asyncTasks.clear();
     }
     }
+
 }
 }

+ 20 - 13
app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java

@@ -31,6 +31,7 @@ import android.view.View;
 import android.widget.Button;
 import android.widget.Button;
 import android.widget.TextView;
 import android.widget.TextView;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
@@ -71,7 +72,7 @@ public class CreateFolderDialogFragment
 
 
 
 
     private OCFile mParentFolder;
     private OCFile mParentFolder;
-    private Button positiveButton;
+    private MaterialButton positiveButton;
 
 
 
 
     private EditBoxDialogBinding binding;
     private EditBoxDialogBinding binding;
@@ -101,13 +102,11 @@ public class CreateFolderDialogFragment
     private void bindButton() {
     private void bindButton() {
         Dialog dialog = getDialog();
         Dialog dialog = getDialog();
 
 
-        if (dialog instanceof AlertDialog) {
-            AlertDialog alertDialog = (AlertDialog) dialog;
-
-            positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
-            viewThemeUtils.platform.colorTextButtons(positiveButton,
-                                                     alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
+        if (dialog instanceof AlertDialog alertDialog) {
+            positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+            MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
         }
         }
     }
     }
 
 
@@ -186,17 +185,25 @@ public class CreateFolderDialogFragment
         });
         });
 
 
         // Build the dialog
         // Build the dialog
-        MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
-        builder.setView(view)
-            .setPositiveButton(R.string.folder_confirm_create, this)
-            .setNeutralButton(R.string.common_cancel, this)
-            .setTitle(R.string.uploader_info_dirname);
+        MaterialAlertDialogBuilder builder = buildMaterialAlertDialog(view);
 
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInputContainer.getContext(), builder);
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInputContainer.getContext(), builder);
 
 
         return builder.create();
         return builder.create();
     }
     }
 
 
+    private MaterialAlertDialogBuilder buildMaterialAlertDialog(View view) {
+        MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireActivity());
+
+        builder
+            .setView(view)
+            .setPositiveButton(R.string.folder_confirm_create, this)
+            .setNegativeButton(R.string.common_cancel, this)
+            .setTitle(R.string.uploader_info_dirname);
+
+        return builder;
+    }
+
     @Override
     @Override
     public void onClick(DialogInterface dialog, int which) {
     public void onClick(DialogInterface dialog, int which) {
         if (which == AlertDialog.BUTTON_POSITIVE) {
         if (which == AlertDialog.BUTTON_POSITIVE) {

+ 17 - 4
app/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java

@@ -30,6 +30,7 @@ import android.os.Bundle;
 import android.text.format.DateUtils;
 import android.text.format.DateUtils;
 import android.widget.DatePicker;
 import android.widget.DatePicker;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.utils.theme.ViewThemeUtils;
 import com.owncloud.android.utils.theme.ViewThemeUtils;
@@ -81,12 +82,24 @@ public class ExpirationDatePickerDialogFragment
     public void onStart() {
     public void onStart() {
         super.onStart();
         super.onStart();
         final Dialog currentDialog = getDialog();
         final Dialog currentDialog = getDialog();
+
         if (currentDialog != null) {
         if (currentDialog != null) {
             final DatePickerDialog dialog = (DatePickerDialog) currentDialog;
             final DatePickerDialog dialog = (DatePickerDialog) currentDialog;
 
 
-            viewThemeUtils.platform.colorTextButtons(dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL),
-                                                     dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE),
-                                                     dialog.getButton(DatePickerDialog.BUTTON_POSITIVE));
+            MaterialButton positiveButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
+            if (positiveButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+            }
+
+            MaterialButton negativeButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE);
+            if (negativeButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
+            }
+
+            MaterialButton neutralButton = (MaterialButton) dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL);
+            if (neutralButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton);
+            }
         }
         }
     }
     }
 
 
@@ -118,7 +131,7 @@ public class ExpirationDatePickerDialogFragment
         //show unset button only when date is already selected
         //show unset button only when date is already selected
         if (chosenDateInMillis > 0) {
         if (chosenDateInMillis > 0) {
             dialog.setButton(
             dialog.setButton(
-                Dialog.BUTTON_NEUTRAL,
+                Dialog.BUTTON_NEGATIVE,
                 getText(R.string.share_via_link_unset_password),
                 getText(R.string.share_via_link_unset_password),
                 (dialog1, which) -> {
                 (dialog1, which) -> {
                     if (onExpiryDateListener != null) {
                     if (onExpiryDateListener != null) {

+ 0 - 144
app/src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java

@@ -1,144 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Tobias Kaminsky
- * Copyright (C) 2018 Tobias Kaminsky
- * Copyright (C) 2018 Nextcloud GmbH.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.dialog;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.nextcloud.client.di.Injectable;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.NoteDialogBinding;
-import com.owncloud.android.lib.resources.shares.OCShare;
-import com.owncloud.android.ui.activity.ComponentsGetter;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.KeyboardUtils;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-
-/**
- * Dialog to input a multiline note for a share
- */
-public class NoteDialogFragment extends DialogFragment implements DialogInterface.OnClickListener, Injectable {
-
-    private static final String ARG_SHARE = "SHARE";
-
-    @Inject ViewThemeUtils viewThemeUtils;
-    @Inject KeyboardUtils keyboardUtils;
-
-    private OCShare share;
-    private NoteDialogBinding binding;
-
-    public static NoteDialogFragment newInstance(OCShare share) {
-        NoteDialogFragment frag = new NoteDialogFragment();
-
-        Bundle args = new Bundle();
-        args.putParcelable(ARG_SHARE, share);
-        frag.setArguments(args);
-
-        return frag;
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (getArguments() == null) {
-            throw new IllegalArgumentException("Arguments may not be null");
-        }
-        share = getArguments().getParcelable(ARG_SHARE);
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        AlertDialog alertDialog = (AlertDialog) getDialog();
-
-        viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                                                 alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        keyboardUtils.showKeyboardForEditText(requireDialog().getWindow(), binding.noteText);
-    }
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        // Inflate the layout for the dialog
-        LayoutInflater inflater = requireActivity().getLayoutInflater();
-        binding = NoteDialogBinding.inflate(inflater, null, false);
-        View view = binding.getRoot();
-
-        // Setup layout
-        binding.noteText.setText(share.getNote());
-        viewThemeUtils.material.colorTextInputLayout(binding.noteContainer);
-
-        // Build the dialog
-        MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(binding.noteContainer.getContext());
-        builder.setView(view)
-            .setPositiveButton(R.string.note_confirm, this)
-            .setNeutralButton(R.string.common_cancel, this)
-            .setTitle(R.string.send_note);
-
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.noteContainer.getContext(), builder);
-
-        return builder.create();
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        if (which == AlertDialog.BUTTON_POSITIVE) {
-            ComponentsGetter componentsGetter = (ComponentsGetter) getActivity();
-
-            if (componentsGetter != null) {
-                String note = "";
-
-                if (binding.noteText.getText() != null) {
-                    note = binding.noteText.getText().toString().trim();
-                }
-
-                componentsGetter.getFileOperationsHelper().updateNoteToShare(share, note);
-            } else {
-                DisplayUtils.showSnackMessage(requireActivity(), R.string.note_could_not_sent);
-            }
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        binding = null;
-    }
-}

+ 35 - 14
app/src/main/java/com/owncloud/android/ui/dialog/RemoveFilesDialogFragment.java

@@ -23,6 +23,7 @@ import android.app.Dialog;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.view.ActionMode;
 import android.view.ActionMode;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.OCFile;
@@ -96,15 +97,20 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
                 R.string.confirmation_remove_files_alert;
                 R.string.confirmation_remove_files_alert;
         }
         }
 
 
-        int localRemoveButton = (containsFolder || containsDown) ? R.string.confirmation_remove_local : -1;
-
         args.putInt(ARG_MESSAGE_RESOURCE_ID, messageStringId);
         args.putInt(ARG_MESSAGE_RESOURCE_ID, messageStringId);
         if (files.size() == SINGLE_SELECTION) {
         if (files.size() == SINGLE_SELECTION) {
-            args.putStringArray(ARG_MESSAGE_ARGUMENTS, new String[]{files.get(0).getFileName()});
+            args.putStringArray(ARG_MESSAGE_ARGUMENTS, new String[] { files.get(0).getFileName() } );
         }
         }
+
         args.putInt(ARG_POSITIVE_BTN_RES, R.string.file_delete);
         args.putInt(ARG_POSITIVE_BTN_RES, R.string.file_delete);
-        args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
-        args.putInt(ARG_NEGATIVE_BTN_RES, localRemoveButton);
+
+        if (containsFolder || containsDown) {
+            args.putInt(ARG_NEGATIVE_BTN_RES, R.string.confirmation_remove_local);
+            args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
+        } else {
+            args.putInt(ARG_NEGATIVE_BTN_RES, R.string.file_keep);
+        }
+
         args.putParcelableArrayList(ARG_TARGET_FILES, files);
         args.putParcelableArrayList(ARG_TARGET_FILES, files);
         frag.setArguments(args);
         frag.setArguments(args);
 
 
@@ -131,9 +137,16 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
         AlertDialog alertDialog = (AlertDialog) getDialog();
         AlertDialog alertDialog = (AlertDialog) getDialog();
 
 
         if (alertDialog != null) {
         if (alertDialog != null) {
-            viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                                                     alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE),
-                                                     alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
+            MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+
+            MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
+
+            MaterialButton neutralButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
+            if (neutralButton != null) {
+                viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton);
+            }
         }
         }
     }
     }
 
 
@@ -141,10 +154,14 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
     @Override
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         Dialog dialog = super.onCreateDialog(savedInstanceState);
         Dialog dialog = super.onCreateDialog(savedInstanceState);
-        mTargetFiles = getArguments().getParcelableArrayList(ARG_TARGET_FILES);
+        Bundle arguments = getArguments();
 
 
-        setOnConfirmationListener(this);
+        if (arguments == null) {
+            return dialog;
+        }
 
 
+        mTargetFiles = arguments.getParcelableArrayList(ARG_TARGET_FILES);
+        setOnConfirmationListener(this);
         return dialog;
         return dialog;
     }
     }
 
 
@@ -154,9 +171,7 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
      */
      */
     @Override
     @Override
     public void onConfirmation(String callerTag) {
     public void onConfirmation(String callerTag) {
-        ComponentsGetter cg = (ComponentsGetter) getActivity();
-        cg.getFileOperationsHelper().removeFiles(mTargetFiles, false, false);
-        finishActionMode();
+        removeFiles(false);
     }
     }
 
 
     /**
     /**
@@ -164,8 +179,14 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
      */
      */
     @Override
     @Override
     public void onCancel(String callerTag) {
     public void onCancel(String callerTag) {
+        removeFiles(true);
+    }
+
+    private void removeFiles(boolean onlyLocalCopy) {
         ComponentsGetter cg = (ComponentsGetter) getActivity();
         ComponentsGetter cg = (ComponentsGetter) getActivity();
-        cg.getFileOperationsHelper().removeFiles(mTargetFiles, true, false);
+        if (cg != null) {
+            cg.getFileOperationsHelper().removeFiles(mTargetFiles, onlyLocalCopy, false);
+        }
         finishActionMode();
         finishActionMode();
     }
     }
 
 

+ 6 - 5
app/src/main/java/com/owncloud/android/ui/dialog/RenamePublicShareDialogFragment.java

@@ -28,6 +28,7 @@ import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View;
 
 
+import com.google.android.material.button.MaterialButton;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.google.android.material.dialog.MaterialAlertDialogBuilder;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.R;
 import com.owncloud.android.R;
@@ -52,8 +53,6 @@ public class RenamePublicShareDialogFragment
 
 
     private static final String ARG_PUBLIC_SHARE = "PUBLIC_SHARE";
     private static final String ARG_PUBLIC_SHARE = "PUBLIC_SHARE";
 
 
-    public static final String RENAME_PUBLIC_SHARE_FRAGMENT = "RENAME_PUBLIC_SHARE_FRAGMENT";
-
     @Inject ViewThemeUtils viewThemeUtils;
     @Inject ViewThemeUtils viewThemeUtils;
     @Inject KeyboardUtils keyboardUtils;
     @Inject KeyboardUtils keyboardUtils;
 
 
@@ -75,8 +74,10 @@ public class RenamePublicShareDialogFragment
         AlertDialog alertDialog = (AlertDialog) getDialog();
         AlertDialog alertDialog = (AlertDialog) getDialog();
 
 
         if (alertDialog != null) {
         if (alertDialog != null) {
-            viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_POSITIVE),
-                                                     alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL));
+            MaterialButton positiveButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+            MaterialButton negativeButton = (MaterialButton) alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton);
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton);
         }
         }
     }
     }
 
 
@@ -104,7 +105,7 @@ public class RenamePublicShareDialogFragment
         MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(view.getContext());
         MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(view.getContext());
         builder.setView(view)
         builder.setView(view)
             .setPositiveButton(R.string.file_rename, this)
             .setPositiveButton(R.string.file_rename, this)
-            .setNeutralButton(R.string.common_cancel, this)
+            .setNegativeButton(R.string.common_cancel, this)
             .setTitle(R.string.public_share_name);
             .setTitle(R.string.public_share_name);
 
 
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInput.getContext(), builder);
         viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.userInput.getContext(), builder);

+ 0 - 329
app/src/main/java/com/owncloud/android/ui/dialog/SslValidatorDialog.java

@@ -1,329 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   @author David A. Velasco
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.dialog;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.SslValidatorLayoutBinding;
-import com.owncloud.android.lib.common.network.CertificateCombinedException;
-import com.owncloud.android.lib.common.network.NetworkUtils;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.security.auth.x500.X500Principal;
-
-/**
- * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
- */
-public class SslValidatorDialog extends Dialog {
-
-    private final static String TAG = SslValidatorDialog.class.getSimpleName();
-
-    private OnSslValidatorListener mListener;
-    private CertificateCombinedException mException;
-    private SslValidatorLayoutBinding binding;
-    
-    
-    /**
-     * Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
-     * be trusted.
-     * 
-     * @param context       Android context where the dialog will live.
-     * @param result        Result of a failed remote operation.
-     * @param listener      Object to notice when the server certificate was added to the local certificates store.
-     * @return              A new SslValidatorDialog instance. NULL if the operation can not be recovered
-     *                      by setting the certificate as reliable.
-     */
-    public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
-        if (result != null && result.isSslRecoverableException()) {
-            return new SslValidatorDialog(context, listener);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Private constructor. 
-     * 
-     * Instances have to be created through static {@link SslValidatorDialog#newInstance}.
-     * 
-     * @param context       Android context where the dialog will live
-     * @param listener      Object to notice when the server certificate was added to the local certificates store.
-     */
-    private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
-        super(context);
-        mListener = listener;
-    }
-    
-    /**
-     * {@inheritDoc}
-     */
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
-        binding = SslValidatorLayoutBinding.inflate(getLayoutInflater());
-        setContentView(binding.getRoot());
-
-        binding.ok.setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        try {
-                            saveServerCert();
-                            dismiss();
-                            if (mListener != null) {
-                                mListener.onSavedCertificate();
-                            } else {
-                                Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
-                            }
-                            
-                        } catch (GeneralSecurityException | IOException e) {
-                            dismiss();
-                            if (mListener != null) {
-                                mListener.onFailedSavingCertificate();
-                            }
-                            Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
-                            
-                        }
-                    }
-                });
-
-        binding.cancel.setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        cancel();
-                    }
-                });
-
-        binding.detailsBtn.setOnClickListener(
-                new View.OnClickListener() {
-                   @Override
-                    public void onClick(View v) {
-                       View detailsScroll = findViewById(R.id.details_scroll);
-                       if (detailsScroll.getVisibility() == View.VISIBLE) {
-                           detailsScroll.setVisibility(View.GONE);
-                           ((Button) v).setText(R.string.ssl_validator_btn_details_see);
-                       } else {
-                           detailsScroll.setVisibility(View.VISIBLE);
-                           ((Button) v).setText(R.string.ssl_validator_btn_details_hide);
-                       }
-                    }
-                });
-    }
-    
-    
-    public void updateResult(RemoteOperationResult result) {
-        if (result.isSslRecoverableException()) {
-            mException = (CertificateCombinedException) result.getException();
-            
-            /// clean
-            binding.reasonCertNotTrusted.setVisibility(View.GONE);
-            binding.reasonCertExpired.setVisibility(View.GONE);
-            binding.reasonCertNotYetValid.setVisibility(View.GONE);
-            binding.reasonHostnameNotVerified.setVisibility(View.GONE);
-            binding.detailsScroll.setVisibility(View.GONE);
-
-            /// refresh
-            if (mException.getCertPathValidatorException() != null) {
-                binding.reasonCertNotTrusted.setVisibility(View.VISIBLE);
-            }
-            
-            if (mException.getCertificateExpiredException() != null) {
-                binding.reasonCertExpired.setVisibility(View.VISIBLE);
-            }
-            
-            if (mException.getCertificateNotYetValidException() != null) {
-                binding.reasonCertNotYetValid.setVisibility(View.VISIBLE);
-            } 
-
-            if (mException.getSslPeerUnverifiedException() != null ) {
-                binding.reasonHostnameNotVerified.setVisibility(View.VISIBLE);
-            }
-            
-            showCertificateData(mException.getServerCertificate());
-        }
-    }
-    
-    private void showCertificateData(X509Certificate cert) {
-
-        if (cert != null) {
-            showSubject(cert.getSubjectX500Principal());
-            showIssuer(cert.getIssuerX500Principal());
-            showValidity(cert.getNotBefore(), cert.getNotAfter());
-            showSignature(cert);
-            
-        } else {
-            // this should not happen, TODO
-            Log_OC.d("certNull", "This should not happen");
-        }
-    }
-
-    private void showSignature(X509Certificate cert) {
-        binding.valueSignature.setText(getHex(cert.getSignature()));
-        binding.valueSignatureAlgorithm.setText(cert.getSigAlgName());
-    }
-    
-    public String getHex(final byte [] raw) {
-        if (raw == null) {
-           return null;
-        }
-        final StringBuilder hex = new StringBuilder(2 * raw.length);
-        for (final byte b : raw) {
-           final int hiVal = (b & 0xF0) >> 4;
-           final int loVal = b & 0x0F;
-           hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
-           hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
-        }
-        return hex.toString();
-     }    
-
-    @SuppressWarnings("deprecation")
-    private void showValidity(Date notBefore, Date notAfter) {
-        binding.valueValidityFrom.setText(notBefore.toLocaleString());
-        binding.valueValidityTo.setText(notAfter.toLocaleString());
-    }
-
-    private void showSubject(X500Principal subject) {
-        Map<String, String> s = parsePrincipal(subject);
-        
-        if (s.get("CN") != null) {
-            binding.valueSubjectCN.setText(s.get("CN"));
-            binding.valueSubjectCN.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectCN.setVisibility(View.GONE);
-        }
-        if (s.get("O") != null) {
-            binding.valueSubjectO.setText(s.get("O"));
-            binding.valueSubjectO.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectO.setVisibility(View.GONE);
-        }
-        if (s.get("OU") != null) {
-            binding.valueSubjectOU.setText(s.get("OU"));
-            binding.valueSubjectOU.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectOU.setVisibility(View.GONE);
-        }
-        if (s.get("C") != null) {
-            binding.valueSubjectC.setText(s.get("C"));
-            binding.valueSubjectC.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectC.setVisibility(View.GONE);
-        }
-        if (s.get("ST") != null) {
-            binding.valueSubjectST.setText(s.get("ST"));
-            binding.valueSubjectST.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectST.setVisibility(View.GONE);
-        }
-        if (s.get("L") != null) {
-            binding.valueSubjectL.setText(s.get("L"));
-            binding.valueSubjectL.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueSubjectL.setVisibility(View.GONE);
-        }
-    }
-    
-    private void showIssuer(X500Principal issuer) {
-        Map<String, String> s = parsePrincipal(issuer);
-        
-        if (s.get("CN") != null) {
-            binding.valueIssuerCN.setText(s.get("CN"));
-            binding.valueIssuerCN.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerCN.setVisibility(View.GONE);
-        }
-        if (s.get("O") != null) {
-            binding.valueIssuerO.setText(s.get("O"));
-            binding.valueIssuerO.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerO.setVisibility(View.GONE);
-        }
-        if (s.get("OU") != null) {
-            binding.valueIssuerOU.setText(s.get("OU"));
-            binding.valueIssuerOU.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerOU.setVisibility(View.GONE);
-        }
-        if (s.get("C") != null) {
-            binding.valueIssuerC.setText(s.get("C"));
-            binding.valueIssuerC.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerC.setVisibility(View.GONE);
-        }
-        if (s.get("ST") != null) {
-            binding.valueIssuerST.setText(s.get("ST"));
-            binding.valueIssuerST.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerST.setVisibility(View.GONE);
-        }
-        if (s.get("L") != null) {
-            binding.valueIssuerL.setText(s.get("L"));
-            binding.valueIssuerL.setVisibility(View.VISIBLE);
-        } else {
-            binding.valueIssuerL.setVisibility(View.GONE);
-        }
-    }
-
-    private Map<String, String> parsePrincipal(X500Principal principal) {
-        Map<String, String> result = new HashMap<>();
-        String toParse = principal.getName();
-        String[] pieces = toParse.split(",");
-        String[] tokens = {"CN", "O", "OU", "C", "ST", "L"};
-        for (String piece : pieces) {
-            for (String token : tokens) {
-                if (piece.startsWith(token + "=")) {
-                    result.put(token, piece.substring(token.length() + 1));
-                }
-            }
-        }
-        return result;
-    }
-
-    private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
-        if (mException.getServerCertificate() != null) {
-            // TODO make this asynchronously, it can take some time
-            NetworkUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
-        }
-    }
-    
-    public interface OnSslValidatorListener {
-        public void onSavedCertificate();
-        public void onFailedSavingCertificate();
-    }
-}

+ 30 - 37
app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt

@@ -24,15 +24,14 @@ import android.app.Dialog
 import android.os.Build
 import android.os.Build
 import android.os.Bundle
 import android.os.Bundle
 import android.os.Parcelable
 import android.os.Parcelable
-import android.view.View
 import androidx.annotation.RequiresApi
 import androidx.annotation.RequiresApi
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AlertDialog
 import androidx.core.os.bundleOf
 import androidx.core.os.bundleOf
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.DialogFragment
+import com.google.android.material.button.MaterialButton
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.nextcloud.client.di.Injectable
 import com.nextcloud.client.di.Injectable
 import com.owncloud.android.R
 import com.owncloud.android.R
-import com.owncloud.android.databinding.StoragePermissionDialogBinding
 import com.owncloud.android.utils.theme.ViewThemeUtils
 import com.owncloud.android.utils.theme.ViewThemeUtils
 import kotlinx.parcelize.Parcelize
 import kotlinx.parcelize.Parcelize
 import javax.inject.Inject
 import javax.inject.Inject
@@ -43,10 +42,7 @@ import javax.inject.Inject
  * Allows choosing "full access" (MANAGE_ALL_FILES) or "read-only media" (READ_EXTERNAL_STORAGE)
  * Allows choosing "full access" (MANAGE_ALL_FILES) or "read-only media" (READ_EXTERNAL_STORAGE)
  */
  */
 @RequiresApi(Build.VERSION_CODES.R)
 @RequiresApi(Build.VERSION_CODES.R)
-class StoragePermissionDialogFragment :
-    DialogFragment(), Injectable {
-
-    private lateinit var binding: StoragePermissionDialogBinding
+class StoragePermissionDialogFragment : DialogFragment(), Injectable {
 
 
     private var permissionRequired = false
     private var permissionRequired = false
 
 
@@ -64,51 +60,48 @@ class StoragePermissionDialogFragment :
         super.onStart()
         super.onStart()
         dialog?.let {
         dialog?.let {
             val alertDialog = it as AlertDialog
             val alertDialog = it as AlertDialog
-            viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE))
-        }
-    }
 
 
-    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
-        // Inflate the layout for the dialog
-        val inflater = requireActivity().layoutInflater
-        binding = StoragePermissionDialogBinding.inflate(inflater, null, false)
+            val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton)
 
 
-        val view: View = binding.root
-        val explanationResource = when {
-            permissionRequired -> R.string.file_management_permission_text
-            else -> R.string.file_management_permission_optional_text
-        }
-        binding.storagePermissionExplanation.text = getString(explanationResource, getString(R.string.app_name))
+            val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(negativeButton)
 
 
-        // Setup layout
-        viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.btnFullAccess)
-        binding.btnFullAccess.setOnClickListener {
-            setResult(Result.FULL_ACCESS)
-            dismiss()
-        }
-        viewThemeUtils.platform.colorTextButtons(binding.btnReadOnly)
-        binding.btnReadOnly.setOnClickListener {
-            setResult(Result.MEDIA_READ_ONLY)
-            dismiss()
+            val neutralButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) as MaterialButton
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(neutralButton)
         }
         }
+    }
 
 
-        // Build the dialog
-        val titleResource = when {
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        val title = when {
             permissionRequired -> R.string.file_management_permission
             permissionRequired -> R.string.file_management_permission
             else -> R.string.file_management_permission_optional
             else -> R.string.file_management_permission_optional
         }
         }
+        val explanationResource = when {
+            permissionRequired -> R.string.file_management_permission_text
+            else -> R.string.file_management_permission_optional_text
+        }
+        val message = getString(explanationResource, getString(R.string.app_name))
 
 
-        val builder = MaterialAlertDialogBuilder(binding.btnReadOnly.context)
-            .setTitle(titleResource)
-            .setView(view)
-            .setNegativeButton(R.string.common_cancel) { _, _ ->
+        val dialogBuilder = MaterialAlertDialogBuilder(requireContext())
+            .setTitle(title)
+            .setMessage(message)
+            .setPositiveButton(R.string.storage_permission_full_access) { _, _ ->
+                setResult(Result.FULL_ACCESS)
+                dismiss()
+            }
+            .setNegativeButton(R.string.storage_permission_media_read_only) { _, _ ->
+                setResult(Result.MEDIA_READ_ONLY)
+                dismiss()
+            }
+            .setNeutralButton(R.string.common_cancel) { _, _ ->
                 setResult(Result.CANCEL)
                 setResult(Result.CANCEL)
                 dismiss()
                 dismiss()
             }
             }
 
 
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.btnReadOnly.context, builder)
+        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), dialogBuilder)
 
 
-        return builder.create()
+        return dialogBuilder.create()
     }
     }
 
 
     private fun setResult(result: Result) {
     private fun setResult(result: Result) {

+ 9 - 0
app/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java

@@ -284,6 +284,10 @@ public class FileDetailActivitiesFragment extends Fragment implements
             });
             });
             return;
             return;
         }
         }
+        
+        if (!isLoadingActivities) {
+            return;
+        }
 
 
         Thread t = new Thread(() -> {
         Thread t = new Thread(() -> {
             try {
             try {
@@ -454,6 +458,11 @@ public class FileDetailActivitiesFragment extends Fragment implements
     public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
     public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
         return false;
         return false;
     }
     }
+    
+    @VisibleForTesting
+    public void disableLoadingActivities() {
+        isLoadingActivities = false;
+    }
 
 
     private static class SubmitCommentTask extends AsyncTask<Void, Void, Boolean> {
     private static class SubmitCommentTask extends AsyncTask<Void, Void, Boolean> {
 
 

+ 0 - 691
app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.java

@@ -1,691 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Mario Danic
- * @author TSI-mc
- * Copyright (C) 2017 Mario Danic
- * Copyright (C) 2017 Nextcloud GmbH.
- * Copyright (C) 2023 TSI-mc
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.fragment.contactsbackup;
-
-import android.Manifest;
-import android.app.DatePickerDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.DatePicker;
-import android.widget.Toast;
-
-import com.nextcloud.client.account.User;
-import com.nextcloud.client.di.Injectable;
-import com.nextcloud.client.jobs.BackgroundJobManager;
-import com.nextcloud.java.util.Optional;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.BackupFragmentBinding;
-import com.owncloud.android.datamodel.ArbitraryDataProvider;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.operations.RefreshFolderOperation;
-import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
-import com.owncloud.android.ui.activity.SettingsActivity;
-import com.owncloud.android.ui.fragment.FileFragment;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-import com.owncloud.android.utils.PermissionUtil;
-import com.owncloud.android.utils.theme.ThemeUtils;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.ActionBar;
-import androidx.fragment.app.Fragment;
-import third_parties.daveKoeller.AlphanumComparator;
-
-import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP;
-import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP;
-
-public class BackupFragment extends FileFragment implements DatePickerDialog.OnDateSetListener, Injectable {
-    public static final String TAG = BackupFragment.class.getSimpleName();
-    private static final String ARG_SHOW_SIDEBAR = "SHOW_SIDEBAR";
-    private static final String KEY_CALENDAR_PICKER_OPEN = "IS_CALENDAR_PICKER_OPEN";
-    private static final String KEY_CALENDAR_DAY = "CALENDAR_DAY";
-    private static final String KEY_CALENDAR_MONTH = "CALENDAR_MONTH";
-    private static final String KEY_CALENDAR_YEAR = "CALENDAR_YEAR";
-
-    public static final String PREFERENCE_CONTACTS_BACKUP_ENABLED = "PREFERENCE_CONTACTS_BACKUP_ENABLED";
-    public static final String PREFERENCE_CALENDAR_BACKUP_ENABLED = "PREFERENCE_CALENDAR_BACKUP_ENABLED";
-
-
-    private BackupFragmentBinding binding;
-
-    @Inject BackgroundJobManager backgroundJobManager;
-    @Inject ThemeUtils themeUtils;
-
-    @Inject ArbitraryDataProvider arbitraryDataProvider;
-    @Inject ViewThemeUtils viewThemeUtils;
-
-    private Date selectedDate;
-    private boolean calendarPickerOpen;
-
-    private DatePickerDialog datePickerDialog;
-
-    private CompoundButton.OnCheckedChangeListener dailyBackupCheckedChangeListener;
-    private CompoundButton.OnCheckedChangeListener contactsCheckedListener;
-    private CompoundButton.OnCheckedChangeListener calendarCheckedListener;
-    private User user;
-    private boolean showSidebar = true;
-    //flag to check if calendar backup should be shown and backup should be done or not
-    private boolean showCalendarBackup = true;
-    public static BackupFragment create(boolean showSidebar) {
-        BackupFragment fragment = new BackupFragment();
-        Bundle bundle = new Bundle();
-        bundle.putBoolean(ARG_SHOW_SIDEBAR, showSidebar);
-        fragment.setArguments(bundle);
-        return fragment;
-    }
-
-    private boolean isCalendarBackupEnabled() {
-        return arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CALENDAR_BACKUP_ENABLED);
-    }
-
-    private void setCalendarBackupEnabled(final boolean enabled) {
-        arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREFERENCE_CALENDAR_BACKUP_ENABLED, enabled);
-    }
-
-    private boolean isContactsBackupEnabled() {
-        return arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_BACKUP_ENABLED);
-    }
-
-    private void setContactsBackupEnabled(final boolean enabled) {
-        arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREFERENCE_CONTACTS_BACKUP_ENABLED, enabled);
-    }
-
-    @Override
-    public View onCreateView(@NonNull final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-
-        // use grey as fallback for elements where custom theming is not available
-        if (themeUtils.themingEnabled(getContext())) {
-            getContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
-        }
-
-        binding = BackupFragmentBinding.inflate(inflater, container, false);
-        View view = binding.getRoot();
-
-        setHasOptionsMenu(true);
-
-        if (getArguments() != null) {
-            showSidebar = getArguments().getBoolean(ARG_SHOW_SIDEBAR);
-        }
-
-        showCalendarBackup = requireContext().getResources().getBoolean(R.bool.show_calendar_backup);
-
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-        user = contactsPreferenceActivity.getUser().orElseThrow(RuntimeException::new);
-
-        ActionBar actionBar = contactsPreferenceActivity != null ? contactsPreferenceActivity.getSupportActionBar() : null;
-
-        if (actionBar != null) {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            viewThemeUtils.files.themeActionBar(requireContext(), actionBar,
-                                                showCalendarBackup ? R.string.backup_title : R.string.contact_backup_title);
-        }
-
-
-        viewThemeUtils.androidx.colorSwitchCompat(binding.contacts);
-        viewThemeUtils.androidx.colorSwitchCompat(binding.calendar);
-        viewThemeUtils.androidx.colorSwitchCompat(binding.dailyBackup);
-        binding.dailyBackup.setChecked(arbitraryDataProvider.getBooleanValue(user,
-                                                                             PREFERENCE_CONTACTS_AUTOMATIC_BACKUP));
-
-        binding.contacts.setChecked(isContactsBackupEnabled() && checkContactBackupPermission());
-        binding.calendar.setChecked(isCalendarBackupEnabled() && checkCalendarBackupPermission(getContext()));
-
-        binding.calendar.setVisibility(showCalendarBackup ? View.VISIBLE : View.GONE);
-
-        setupCheckListeners();
-
-        setBackupNowButtonVisibility();
-
-        binding.backupNow.setOnClickListener(v -> backupNow());
-
-        binding.contactsDatepicker.setOnClickListener(v -> openCleanDate());
-
-        // display last backup
-        Long lastBackupTimestamp = arbitraryDataProvider.getLongValue(user, PREFERENCE_CONTACTS_LAST_BACKUP);
-
-        if (lastBackupTimestamp == -1) {
-            binding.lastBackupWithDate.setVisibility(View.GONE);
-        } else {
-            binding.lastBackupWithDate.setText(
-                String.format(getString(R.string.last_backup),
-                              DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp)));
-        }
-
-        if (savedInstanceState != null && savedInstanceState.getBoolean(KEY_CALENDAR_PICKER_OPEN, false)) {
-            if (savedInstanceState.getInt(KEY_CALENDAR_YEAR, -1) != -1 &&
-                savedInstanceState.getInt(KEY_CALENDAR_MONTH, -1) != -1 &&
-                savedInstanceState.getInt(KEY_CALENDAR_DAY, -1) != -1) {
-                selectedDate = new Date(savedInstanceState.getInt(KEY_CALENDAR_YEAR),
-                                        savedInstanceState.getInt(KEY_CALENDAR_MONTH), savedInstanceState.getInt(KEY_CALENDAR_DAY));
-            }
-            calendarPickerOpen = true;
-        }
-
-        viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.backupNow);
-        viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.contactsDatepicker);
-
-        viewThemeUtils.platform.colorTextView(binding.dataToBackUpTitle);
-        viewThemeUtils.platform.colorTextView(binding.backupSettingsTitle);
-
-        return view;
-    }
-
-    private void setupCheckListeners() {
-        dailyBackupCheckedChangeListener = (buttonView, isChecked) -> {
-            if (checkAndAskForContactsReadPermission()) {
-                setAutomaticBackup(isChecked);
-            }
-        };
-        binding.dailyBackup.setOnCheckedChangeListener(dailyBackupCheckedChangeListener);
-
-
-        contactsCheckedListener = (buttonView, isChecked) -> {
-            if (isChecked) {
-                if (checkAndAskForContactsReadPermission()) {
-                    setContactsBackupEnabled(true);
-                }
-            } else {
-                setContactsBackupEnabled(false);
-            }
-            setBackupNowButtonVisibility();
-            setAutomaticBackup(binding.dailyBackup.isChecked());
-        };
-        binding.contacts.setOnCheckedChangeListener(contactsCheckedListener);
-
-        calendarCheckedListener = (buttonView, isChecked) -> {
-            if (isChecked) {
-                if (checkAndAskForCalendarReadPermission()) {
-                    setCalendarBackupEnabled(true);
-                }
-            } else {
-                setCalendarBackupEnabled(false);
-            }
-            setBackupNowButtonVisibility();
-            setAutomaticBackup(binding.dailyBackup.isChecked());
-        };
-        binding.calendar.setOnCheckedChangeListener(calendarCheckedListener);
-    }
-
-    private void setBackupNowButtonVisibility() {
-        if (binding.contacts.isChecked() || binding.calendar.isChecked()) {
-            binding.backupNow.setVisibility(View.VISIBLE);
-        } else {
-            binding.backupNow.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        if (calendarPickerOpen) {
-            if (selectedDate != null) {
-                openDate(selectedDate);
-            } else {
-                openDate(null);
-            }
-        }
-
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-        if (contactsPreferenceActivity != null) {
-            String backupFolderPath = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
-            refreshBackupFolder(backupFolderPath,
-                                contactsPreferenceActivity.getApplicationContext(),
-                                contactsPreferenceActivity.getStorageManager());
-        }
-    }
-
-    private void refreshBackupFolder(final String backupFolderPath,
-                                     final Context context,
-                                     final FileDataStorageManager storageManager) {
-        AsyncTask<String, Integer, Boolean> task = new AsyncTask<String, Integer, Boolean>() {
-            @Override
-            protected Boolean doInBackground(String... path) {
-                OCFile folder = storageManager.getFileByPath(path[0]);
-
-                if (folder != null) {
-                    RefreshFolderOperation operation = new RefreshFolderOperation(folder, System.currentTimeMillis(),
-                                                                                  false, false, storageManager, user, context);
-
-                    RemoteOperationResult result = operation.execute(user, context);
-                    return result.isSuccess();
-                } else {
-                    return Boolean.FALSE;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(Boolean result) {
-                if (result && binding != null) {
-                    OCFile backupFolder = storageManager.getFileByPath(backupFolderPath);
-
-                    List<OCFile> backupFiles = storageManager
-                        .getFolderContent(backupFolder, false);
-
-                    Collections.sort(backupFiles, new AlphanumComparator<>());
-
-                    if (backupFiles == null || backupFiles.isEmpty()) {
-                        binding.contactsDatepicker.setVisibility(View.INVISIBLE);
-                    } else {
-                        binding.contactsDatepicker.setVisibility(View.VISIBLE);
-                    }
-                }
-            }
-        };
-
-        task.execute(backupFolderPath);
-    }
-
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-
-        boolean retval;
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                if (showSidebar) {
-                    if (contactsPreferenceActivity.isDrawerOpen()) {
-                        contactsPreferenceActivity.closeDrawer();
-                    } else {
-                        contactsPreferenceActivity.openDrawer();
-                    }
-                } else if (getActivity() != null) {
-                    getActivity().finish();
-                } else {
-                    Intent settingsIntent = new Intent(getContext(), SettingsActivity.class);
-                    settingsIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                    startActivity(settingsIntent);
-                }
-                retval = true;
-                break;
-
-            default:
-                retval = super.onOptionsItemSelected(item);
-                break;
-        }
-        return retval;
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
-        if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC) {
-            for (int index = 0; index < permissions.length; index++) {
-                if (Manifest.permission.READ_CONTACTS.equalsIgnoreCase(permissions[index])) {
-                    if (grantResults[index] >= 0) {
-                        // if approved, exit for loop
-                        setContactsBackupEnabled(true);
-                        break;
-                    }
-
-                    // if not accepted, disable again
-                    binding.contacts.setOnCheckedChangeListener(null);
-                    binding.contacts.setChecked(false);
-                    binding.contacts.setOnCheckedChangeListener(contactsCheckedListener);
-                }
-            }
-        }
-
-        if (requestCode == PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC) {
-            boolean readGranted = false;
-            boolean writeGranted = false;
-            for (int index = 0; index < permissions.length; index++) {
-                if (Manifest.permission.WRITE_CALENDAR.equalsIgnoreCase(permissions[index]) && grantResults[index] >= 0) {
-                    writeGranted = true;
-                } else if (Manifest.permission.READ_CALENDAR.equalsIgnoreCase(permissions[index]) && grantResults[index] >= 0) {
-                    readGranted = true;
-                }
-            }
-            if (!readGranted || !writeGranted) {
-                // if not accepted, disable again
-                binding.calendar.setOnCheckedChangeListener(null);
-                binding.calendar.setChecked(false);
-                binding.calendar.setOnCheckedChangeListener(calendarCheckedListener);
-            } else {
-                setCalendarBackupEnabled(true);
-            }
-        }
-
-        setBackupNowButtonVisibility();
-        setAutomaticBackup(binding.dailyBackup.isChecked());
-    }
-
-    public void backupNow() {
-        if (isContactsBackupEnabled() && checkContactBackupPermission()) {
-            startContactsBackupJob();
-        }
-
-        if (showCalendarBackup && isCalendarBackupEnabled() && checkCalendarBackupPermission(requireContext())) {
-            startCalendarBackupJob();
-        }
-
-        DisplayUtils.showSnackMessage(requireView().findViewById(R.id.contacts_linear_layout),
-                                      R.string.contacts_preferences_backup_scheduled);
-    }
-
-    private void startContactsBackupJob() {
-        ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
-        if (activity != null) {
-            Optional<User> optionalUser = activity.getUser();
-            if (optionalUser.isPresent()) {
-                backgroundJobManager.startImmediateContactsBackup(optionalUser.get());
-            }
-        }
-    }
-
-    private void startCalendarBackupJob() {
-        ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
-        if (activity != null) {
-            Optional<User> optionalUser = activity.getUser();
-            if (optionalUser.isPresent()) {
-                backgroundJobManager.startImmediateCalendarBackup(optionalUser.get());
-            }
-        }
-    }
-
-    private void setAutomaticBackup(final boolean enabled) {
-
-        final ContactsPreferenceActivity activity = (ContactsPreferenceActivity) getActivity();
-        if (activity == null) {
-            return;
-        }
-        Optional<User> optionalUser = activity.getUser();
-        if (!optionalUser.isPresent()) {
-            return;
-        }
-        User user = optionalUser.get();
-        if (enabled) {
-            if (isContactsBackupEnabled()) {
-                Log_OC.d(TAG, "Scheduling contacts backup job");
-                backgroundJobManager.schedulePeriodicContactsBackup(user);
-            } else {
-                Log_OC.d(TAG, "Cancelling contacts backup job");
-                backgroundJobManager.cancelPeriodicContactsBackup(user);
-            }
-            if (isCalendarBackupEnabled()) {
-                Log_OC.d(TAG, "Scheduling calendar backup job");
-                backgroundJobManager.schedulePeriodicCalendarBackup(user);
-            } else {
-                Log_OC.d(TAG, "Cancelling calendar backup job");
-                backgroundJobManager.cancelPeriodicCalendarBackup(user);
-            }
-        } else {
-            Log_OC.d(TAG, "Cancelling all backup jobs");
-            backgroundJobManager.cancelPeriodicContactsBackup(user);
-            backgroundJobManager.cancelPeriodicCalendarBackup(user);
-        }
-
-        arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
-                                                    PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
-                                                    String.valueOf(enabled));
-    }
-
-    private boolean checkAndAskForContactsReadPermission() {
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-
-        // check permissions
-        if (PermissionUtil.checkSelfPermission(contactsPreferenceActivity, Manifest.permission.READ_CONTACTS)) {
-            return true;
-        } else {
-            // No explanation needed, request the permission.
-            requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
-                               PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC);
-            return false;
-        }
-    }
-
-    private boolean checkAndAskForCalendarReadPermission() {
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-
-        // check permissions
-        if (checkCalendarBackupPermission(contactsPreferenceActivity)) {
-            return true;
-        } else {
-            // No explanation needed, request the permission.
-            requestPermissions(new String[]{Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR},
-                               PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC);
-            return false;
-        }
-    }
-
-    private boolean checkCalendarBackupPermission(final Context context) {
-        return PermissionUtil.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) && PermissionUtil.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR);
-    }
-
-    private boolean checkContactBackupPermission() {
-        return PermissionUtil.checkSelfPermission(getContext(), Manifest.permission.READ_CONTACTS);
-    }
-
-    public void openCleanDate() {
-        if (checkAndAskForCalendarReadPermission() && checkAndAskForContactsReadPermission()) {
-            openDate(null);
-        }
-    }
-
-    public void openDate(@Nullable Date savedDate) {
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-
-        if (contactsPreferenceActivity == null) {
-            Toast.makeText(getContext(), getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show();
-            return;
-        }
-
-        String contactsBackupFolderString =
-            getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
-        String calendarBackupFolderString =
-            getResources().getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR;
-
-        FileDataStorageManager storageManager = contactsPreferenceActivity.getStorageManager();
-
-        OCFile contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString);
-        OCFile calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString);
-
-        List<OCFile> backupFiles = storageManager.getFolderContent(contactsBackupFolder, false);
-        backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false));
-
-        Collections.sort(backupFiles, (o1, o2) -> {
-            return Long.compare(o1.getModificationTimestamp(), o2.getModificationTimestamp());
-        });
-
-        Calendar cal = Calendar.getInstance();
-        int year;
-        int month;
-        int day;
-
-        if (savedDate == null) {
-            year = cal.get(Calendar.YEAR);
-            month = cal.get(Calendar.MONTH) + 1;
-            day = cal.get(Calendar.DAY_OF_MONTH);
-        } else {
-            year = savedDate.getYear();
-            month = savedDate.getMonth();
-            day = savedDate.getDay();
-        }
-
-        if (backupFiles.size() > 0 && backupFiles.get(backupFiles.size() - 1) != null) {
-            datePickerDialog = new DatePickerDialog(contactsPreferenceActivity, this, year, month, day);
-            datePickerDialog.getDatePicker().setMaxDate(backupFiles.get(backupFiles.size() - 1)
-                                                            .getModificationTimestamp());
-            datePickerDialog.getDatePicker().setMinDate(backupFiles.get(0).getModificationTimestamp());
-
-            datePickerDialog.setOnDismissListener(dialog -> selectedDate = null);
-
-            datePickerDialog.setTitle("");
-            datePickerDialog.show();
-
-            viewThemeUtils.platform.colorTextButtons(datePickerDialog.getButton(DatePickerDialog.BUTTON_NEGATIVE),
-                                                     datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE));
-
-            // set background to transparent
-            datePickerDialog.getButton(DatePickerDialog.BUTTON_NEGATIVE).setBackgroundColor(0x00000000);
-            datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).setBackgroundColor(0x00000000);
-        } else {
-            DisplayUtils.showSnackMessage(getView().findViewById(R.id.contacts_linear_layout),
-                                          R.string.contacts_preferences_something_strange_happened);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        binding = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (datePickerDialog != null) {
-            datePickerDialog.dismiss();
-        }
-    }
-
-    @Override
-    public void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-        if (datePickerDialog != null) {
-            outState.putBoolean(KEY_CALENDAR_PICKER_OPEN, datePickerDialog.isShowing());
-
-            if (datePickerDialog.isShowing()) {
-                outState.putInt(KEY_CALENDAR_DAY, datePickerDialog.getDatePicker().getDayOfMonth());
-                outState.putInt(KEY_CALENDAR_MONTH, datePickerDialog.getDatePicker().getMonth());
-                outState.putInt(KEY_CALENDAR_YEAR, datePickerDialog.getDatePicker().getYear());
-            }
-        }
-    }
-
-    @Override
-    public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
-        final ContactsPreferenceActivity contactsPreferenceActivity = (ContactsPreferenceActivity) getActivity();
-
-        if (contactsPreferenceActivity == null) {
-            Toast.makeText(getContext(), getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show();
-            return;
-        }
-
-        selectedDate = new Date(year, month, dayOfMonth);
-
-        String contactsBackupFolderString =
-            getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
-        String calendarBackupFolderString =
-            getResources().getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR;
-
-        FileDataStorageManager storageManager = contactsPreferenceActivity.getStorageManager();
-
-        OCFile contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString);
-        OCFile calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString);
-
-        List<OCFile> backupFiles = storageManager.getFolderContent(contactsBackupFolder, false);
-        backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false));
-
-        // find file with modification with date and time between 00:00 and 23:59
-        // if more than one file exists, take oldest
-        Calendar date = Calendar.getInstance();
-        date.set(year, month, dayOfMonth);
-
-        // start
-        date.set(Calendar.HOUR, 0);
-        date.set(Calendar.MINUTE, 0);
-        date.set(Calendar.SECOND, 1);
-        date.set(Calendar.MILLISECOND, 0);
-        date.set(Calendar.AM_PM, Calendar.AM);
-        long start = date.getTimeInMillis();
-
-        // end
-        date.set(Calendar.HOUR, 23);
-        date.set(Calendar.MINUTE, 59);
-        date.set(Calendar.SECOND, 59);
-        long end = date.getTimeInMillis();
-
-        OCFile contactsBackupToRestore = null;
-        List<OCFile> calendarBackupsToRestore = new ArrayList<>();
-
-        for (OCFile file : backupFiles) {
-            if (start < file.getModificationTimestamp() && end > file.getModificationTimestamp()) {
-                // contact
-                if (MimeTypeUtil.isVCard(file)) {
-                    if (contactsBackupToRestore == null) {
-                        contactsBackupToRestore = file;
-                    } else if (contactsBackupToRestore.getModificationTimestamp() < file.getModificationTimestamp()) {
-                        contactsBackupToRestore = file;
-                    }
-                }
-
-                // calendars
-                if (showCalendarBackup && MimeTypeUtil.isCalendar(file)) {
-                    calendarBackupsToRestore.add(file);
-                }
-            }
-        }
-
-        List<OCFile> backupToRestore = new ArrayList<>();
-
-        if (contactsBackupToRestore != null) {
-            backupToRestore.add(contactsBackupToRestore);
-        }
-
-        backupToRestore.addAll(calendarBackupsToRestore);
-
-
-        if (backupToRestore.isEmpty()) {
-            DisplayUtils.showSnackMessage(getView().findViewById(R.id.contacts_linear_layout),
-                                          R.string.contacts_preferences_no_file_found);
-        } else {
-            final User user = contactsPreferenceActivity.getUser().orElseThrow(RuntimeException::new);
-            OCFile[] files = new OCFile[backupToRestore.size()];
-            Fragment contactListFragment = BackupListFragment.newInstance(backupToRestore.toArray(files), user);
-
-            contactsPreferenceActivity.getSupportFragmentManager().
-                beginTransaction()
-                .replace(R.id.frame_container, contactListFragment, BackupListFragment.TAG)
-                .addToBackStack(ContactsPreferenceActivity.BACKUP_TO_LIST)
-                .commit();
-        }
-    }
-}

+ 726 - 0
app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt

@@ -0,0 +1,726 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * @author TSI-mc
+ * Copyright (C) 2017 Mario Danic
+ * Copyright (C) 2017 Nextcloud GmbH.
+ * Copyright (C) 2023 TSI-mc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.fragment.contactsbackup
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.DatePickerDialog
+import android.app.DatePickerDialog.OnDateSetListener
+import android.content.Context
+import android.content.Intent
+import android.os.AsyncTask
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CompoundButton
+import android.widget.DatePicker
+import android.widget.Toast
+import com.nextcloud.client.account.User
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.jobs.BackgroundJobManager
+import com.owncloud.android.R
+import com.owncloud.android.databinding.BackupFragmentBinding
+import com.owncloud.android.datamodel.ArbitraryDataProvider
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.operations.RefreshFolderOperation
+import com.owncloud.android.ui.activity.ContactsPreferenceActivity
+import com.owncloud.android.ui.activity.SettingsActivity
+import com.owncloud.android.ui.fragment.FileFragment
+import com.owncloud.android.utils.DisplayUtils
+import com.owncloud.android.utils.MimeTypeUtil
+import com.owncloud.android.utils.PermissionUtil
+import com.owncloud.android.utils.PermissionUtil.checkSelfPermission
+import com.owncloud.android.utils.theme.ThemeUtils
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import third_parties.daveKoeller.AlphanumComparator
+import java.util.Calendar
+import java.util.Collections
+import java.util.Date
+import javax.inject.Inject
+
+@Suppress("TooManyFunctions")
+class BackupFragment : FileFragment(), OnDateSetListener, Injectable {
+    private lateinit var binding: BackupFragmentBinding
+
+    @JvmField
+    @Inject
+    var backgroundJobManager: BackgroundJobManager? = null
+
+    @JvmField
+    @Inject
+    var themeUtils: ThemeUtils? = null
+
+    @JvmField
+    @Inject
+    var arbitraryDataProvider: ArbitraryDataProvider? = null
+
+    @JvmField
+    @Inject
+    var viewThemeUtils: ViewThemeUtils? = null
+
+    private var selectedDate: Date? = null
+    private var calendarPickerOpen = false
+    private var datePickerDialog: DatePickerDialog? = null
+    private var contactsCheckedListener: CompoundButton.OnCheckedChangeListener? = null
+    private var calendarCheckedListener: CompoundButton.OnCheckedChangeListener? = null
+    private var user: User? = null
+    private var showSidebar = true
+
+    // flag to check if calendar backup should be shown and backup should be done or not
+    private var showCalendarBackup = true
+    private var isCalendarBackupEnabled: Boolean
+        get() = user?.let { arbitraryDataProvider?.getBooleanValue(it, PREFERENCE_CALENDAR_BACKUP_ENABLED) } ?: false
+        private set(enabled) {
+            arbitraryDataProvider!!.storeOrUpdateKeyValue(
+                user!!.accountName,
+                PREFERENCE_CALENDAR_BACKUP_ENABLED,
+                enabled
+            )
+        }
+
+    private var isContactsBackupEnabled: Boolean
+        get() = user?.let { arbitraryDataProvider?.getBooleanValue(it, PREFERENCE_CONTACTS_BACKUP_ENABLED) } ?: false
+        private set(enabled) {
+            arbitraryDataProvider!!.storeOrUpdateKeyValue(
+                user!!.accountName,
+                PREFERENCE_CONTACTS_BACKUP_ENABLED,
+                enabled
+            )
+        }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        // use grey as fallback for elements where custom theming is not available
+        if (themeUtils?.themingEnabled(context) == true) {
+            requireContext().theme.applyStyle(R.style.FallbackThemingTheme, true)
+        }
+
+        binding = BackupFragmentBinding.inflate(inflater, container, false)
+        val view: View = binding.root
+
+        setHasOptionsMenu(true)
+
+        if (arguments != null) {
+            showSidebar = requireArguments().getBoolean(ARG_SHOW_SIDEBAR)
+        }
+
+        showCalendarBackup = requireContext().resources.getBoolean(R.bool.show_calendar_backup)
+
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+        user = contactsPreferenceActivity?.user?.orElseThrow { RuntimeException() }
+
+        setupSwitches(user)
+
+        setupCheckListeners()
+        setBackupNowButtonVisibility()
+
+        setOnClickListeners()
+
+        contactsPreferenceActivity?.let {
+            displayLastBackup(it)
+            applyUserColorToActionBar(it)
+        }
+
+        setupDates(savedInstanceState)
+        applyUserColor()
+
+        return view
+    }
+
+    private fun setupSwitches(user: User?) {
+        user?.let {
+            binding.dailyBackup.isChecked = arbitraryDataProvider?.getBooleanValue(
+                it,
+                ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP
+            ) ?: false
+        }
+
+        binding.contacts.isChecked = isContactsBackupEnabled && checkContactBackupPermission()
+        binding.calendar.isChecked = isCalendarBackupEnabled && checkCalendarBackupPermission(requireContext())
+        binding.calendar.visibility = if (showCalendarBackup) View.VISIBLE else View.GONE
+    }
+
+    private fun setupCheckListeners() {
+        binding.dailyBackup.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+            if (checkAndAskForContactsReadPermission()) {
+                setAutomaticBackup(isChecked)
+            }
+        }
+
+        initContactsCheckedListener()
+        binding.contacts.setOnCheckedChangeListener(contactsCheckedListener)
+
+        initCalendarCheckedListener()
+        binding.calendar.setOnCheckedChangeListener(calendarCheckedListener)
+    }
+
+    private fun initContactsCheckedListener() {
+        contactsCheckedListener =
+            CompoundButton.OnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+                if (isChecked) {
+                    if (checkAndAskForContactsReadPermission()) {
+                        isContactsBackupEnabled = true
+                    }
+                } else {
+                    isContactsBackupEnabled = false
+                }
+                setBackupNowButtonVisibility()
+                setAutomaticBackup(binding.dailyBackup.isChecked)
+            }
+    }
+
+    private fun initCalendarCheckedListener() {
+        calendarCheckedListener =
+            CompoundButton.OnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
+                if (isChecked) {
+                    if (checkAndAskForCalendarReadPermission()) {
+                        isCalendarBackupEnabled = true
+                    }
+                } else {
+                    isCalendarBackupEnabled = false
+                }
+                setBackupNowButtonVisibility()
+                setAutomaticBackup(binding.dailyBackup.isChecked)
+            }
+    }
+
+    private fun setBackupNowButtonVisibility() {
+        binding.backupNow.visibility =
+            if (binding.contacts.isChecked || binding.calendar.isChecked) View.VISIBLE else View.INVISIBLE
+    }
+
+    private fun setOnClickListeners() {
+        binding.backupNow.setOnClickListener { backupNow() }
+        binding.contactsDatepicker.setOnClickListener { openCleanDate() }
+    }
+
+    private fun displayLastBackup(contactsPreferenceActivity: ContactsPreferenceActivity) {
+        val lastBackupTimestamp = user?.let {
+            arbitraryDataProvider?.getLongValue(
+                it,
+                ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP
+            )
+        } ?: -1L
+
+        if (lastBackupTimestamp == -1L) {
+            binding.lastBackupWithDate.visibility = View.GONE
+        } else {
+            binding.lastBackupWithDate.text = String.format(
+                getString(R.string.last_backup),
+                DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp)
+            )
+        }
+    }
+
+    private fun applyUserColorToActionBar(contactsPreferenceActivity: ContactsPreferenceActivity) {
+        val actionBar = contactsPreferenceActivity.supportActionBar
+
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true)
+            viewThemeUtils?.files?.themeActionBar(
+                requireContext(),
+                actionBar,
+                if (showCalendarBackup) R.string.backup_title else R.string.contact_backup_title
+            )
+        }
+    }
+
+    private fun setupDates(savedInstanceState: Bundle?) {
+        if (savedInstanceState != null && savedInstanceState.getBoolean(KEY_CALENDAR_PICKER_OPEN, false)) {
+            if (savedInstanceState.getInt(KEY_CALENDAR_YEAR, -1) != -1 && savedInstanceState.getInt(
+                    KEY_CALENDAR_MONTH,
+                    -1
+                ) != -1 && savedInstanceState.getInt(
+                    KEY_CALENDAR_DAY, -1
+                ) != -1
+            ) {
+                val cal = Calendar.getInstance()
+                cal[Calendar.YEAR] = savedInstanceState.getInt(KEY_CALENDAR_YEAR)
+                cal[Calendar.MONTH] = savedInstanceState.getInt(KEY_CALENDAR_MONTH)
+                cal[Calendar.DAY_OF_MONTH] = savedInstanceState.getInt(KEY_CALENDAR_DAY)
+                selectedDate = cal.time
+            }
+            calendarPickerOpen = true
+        }
+    }
+
+    private fun applyUserColor() {
+        viewThemeUtils?.androidx?.colorSwitchCompat(binding.contacts)
+        viewThemeUtils?.androidx?.colorSwitchCompat(binding.calendar)
+        viewThemeUtils?.androidx?.colorSwitchCompat(binding.dailyBackup)
+
+        viewThemeUtils?.material?.colorMaterialButtonPrimaryFilled(binding.backupNow)
+        viewThemeUtils?.material?.colorMaterialButtonPrimaryOutlined(binding.contactsDatepicker)
+
+        viewThemeUtils?.platform?.colorTextView(binding.dataToBackUpTitle)
+        viewThemeUtils?.platform?.colorTextView(binding.backupSettingsTitle)
+    }
+
+    override fun onResume() {
+        super.onResume()
+
+        if (calendarPickerOpen) {
+            if (selectedDate != null) {
+                openDate(selectedDate)
+            } else {
+                openDate(null)
+            }
+        }
+
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+        if (contactsPreferenceActivity != null) {
+            val backupFolderPath = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
+            refreshBackupFolder(
+                backupFolderPath,
+                contactsPreferenceActivity.applicationContext,
+                contactsPreferenceActivity.storageManager
+            )
+        }
+    }
+
+    private fun refreshBackupFolder(
+        backupFolderPath: String,
+        context: Context,
+        storageManager: FileDataStorageManager
+    ) {
+        val task: AsyncTask<String, Int, Boolean> =
+            @SuppressLint("StaticFieldLeak")
+            object : AsyncTask<String, Int, Boolean>() {
+                @Deprecated("Deprecated in Java")
+                override fun doInBackground(vararg path: String): Boolean {
+                    val folder = storageManager.getFileByPath(path[0])
+                    return if (folder != null) {
+                        val operation = RefreshFolderOperation(
+                            folder,
+                            System.currentTimeMillis(),
+                            false,
+                            false,
+                            storageManager,
+                            user,
+                            context
+                        )
+                        val result = operation.execute(user, context)
+                        result.isSuccess
+                    } else {
+                        java.lang.Boolean.FALSE
+                    }
+                }
+
+                @Deprecated("Deprecated in Java")
+                override fun onPostExecute(result: Boolean) {
+                    if (result) {
+                        val backupFolder = storageManager.getFileByPath(backupFolderPath)
+                        val backupFiles = storageManager
+                            .getFolderContent(backupFolder, false)
+                        Collections.sort(backupFiles, AlphanumComparator())
+                        if (backupFiles == null || backupFiles.isEmpty()) {
+                            binding.contactsDatepicker.visibility = View.INVISIBLE
+                        } else {
+                            binding.contactsDatepicker.visibility = View.VISIBLE
+                        }
+                    }
+                }
+            }
+
+        task.execute(backupFolderPath)
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+
+        val retval: Boolean
+        when (item.itemId) {
+            android.R.id.home -> {
+                if (showSidebar) {
+                    if (contactsPreferenceActivity!!.isDrawerOpen) {
+                        contactsPreferenceActivity.closeDrawer()
+                    } else {
+                        contactsPreferenceActivity.openDrawer()
+                    }
+                } else if (activity != null) {
+                    requireActivity().finish()
+                } else {
+                    val settingsIntent = Intent(context, SettingsActivity::class.java)
+                    settingsIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
+                    startActivity(settingsIntent)
+                }
+                retval = true
+            }
+
+            else -> retval = super.onOptionsItemSelected(item)
+        }
+        return retval
+    }
+
+    @Deprecated("Deprecated in Java")
+    @Suppress("NestedBlockDepth")
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+        if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC) {
+            for (index in permissions.indices) {
+                if (Manifest.permission.READ_CONTACTS.equals(permissions[index], ignoreCase = true)) {
+                    if (grantResults[index] >= 0) {
+                        // if approved, exit for loop
+                        isContactsBackupEnabled = true
+                        break
+                    }
+
+                    // if not accepted, disable again
+                    binding.contacts.setOnCheckedChangeListener(null)
+                    binding.contacts.isChecked = false
+                    binding.contacts.setOnCheckedChangeListener(contactsCheckedListener)
+                }
+            }
+        }
+        if (requestCode == PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC) {
+            var readGranted = false
+            var writeGranted = false
+            for (index in permissions.indices) {
+                if (Manifest.permission.WRITE_CALENDAR.equals(
+                        permissions[index],
+                        ignoreCase = true
+                    ) && grantResults[index] >= 0
+                ) {
+                    writeGranted = true
+                } else if (Manifest.permission.READ_CALENDAR.equals(
+                        permissions[index],
+                        ignoreCase = true
+                    ) && grantResults[index] >= 0
+                ) {
+                    readGranted = true
+                }
+            }
+            if (!readGranted || !writeGranted) {
+                // if not accepted, disable again
+                binding.calendar.setOnCheckedChangeListener(null)
+                binding.calendar.isChecked = false
+                binding.calendar.setOnCheckedChangeListener(calendarCheckedListener)
+            } else {
+                isCalendarBackupEnabled = true
+            }
+        }
+        setBackupNowButtonVisibility()
+        setAutomaticBackup(binding.dailyBackup.isChecked)
+    }
+
+    private fun backupNow() {
+        if (isContactsBackupEnabled && checkContactBackupPermission()) {
+            startContactsBackupJob()
+        }
+        if (showCalendarBackup && isCalendarBackupEnabled && checkCalendarBackupPermission(requireContext())) {
+            startCalendarBackupJob()
+        }
+        DisplayUtils.showSnackMessage(
+            requireView().findViewById<View>(R.id.contacts_linear_layout),
+            R.string.contacts_preferences_backup_scheduled
+        )
+    }
+
+    private fun startContactsBackupJob() {
+        val activity = activity as ContactsPreferenceActivity?
+        if (activity != null) {
+            val optionalUser = activity.user
+            if (optionalUser.isPresent) {
+                backgroundJobManager!!.startImmediateContactsBackup(optionalUser.get())
+            }
+        }
+    }
+
+    private fun startCalendarBackupJob() {
+        val activity = activity as ContactsPreferenceActivity?
+        if (activity != null) {
+            val optionalUser = activity.user
+            if (optionalUser.isPresent) {
+                backgroundJobManager!!.startImmediateCalendarBackup(optionalUser.get())
+            }
+        }
+    }
+
+    private fun setAutomaticBackup(enabled: Boolean) {
+        val activity = activity as ContactsPreferenceActivity? ?: return
+        val optionalUser = activity.user
+        if (!optionalUser.isPresent) {
+            return
+        }
+        val user = optionalUser.get()
+        if (enabled) {
+            if (isContactsBackupEnabled) {
+                Log_OC.d(TAG, "Scheduling contacts backup job")
+                backgroundJobManager?.schedulePeriodicContactsBackup(user)
+            } else {
+                Log_OC.d(TAG, "Cancelling contacts backup job")
+                backgroundJobManager?.cancelPeriodicContactsBackup(user)
+            }
+            if (isCalendarBackupEnabled) {
+                Log_OC.d(TAG, "Scheduling calendar backup job")
+                backgroundJobManager?.schedulePeriodicCalendarBackup(user)
+            } else {
+                Log_OC.d(TAG, "Cancelling calendar backup job")
+                backgroundJobManager?.cancelPeriodicCalendarBackup(user)
+            }
+        } else {
+            Log_OC.d(TAG, "Cancelling all backup jobs")
+            backgroundJobManager?.cancelPeriodicContactsBackup(user)
+            backgroundJobManager?.cancelPeriodicCalendarBackup(user)
+        }
+        arbitraryDataProvider?.storeOrUpdateKeyValue(
+            user.accountName,
+            ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
+            enabled.toString()
+        )
+    }
+
+    private fun checkAndAskForContactsReadPermission(): Boolean {
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+
+        // check permissions
+        return if (checkSelfPermission(contactsPreferenceActivity!!, Manifest.permission.READ_CONTACTS)) {
+            true
+        } else {
+            // No explanation needed, request the permission.
+            requestPermissions(
+                arrayOf(Manifest.permission.READ_CONTACTS),
+                PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC
+            )
+            false
+        }
+    }
+
+    private fun checkAndAskForCalendarReadPermission(): Boolean {
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+
+        // check permissions
+        return if (contactsPreferenceActivity?.let { checkCalendarBackupPermission(it) } == true) {
+            true
+        } else {
+            // No explanation needed, request the permission.
+            requestPermissions(
+                arrayOf(
+                    Manifest.permission.READ_CALENDAR,
+                    Manifest.permission.WRITE_CALENDAR
+                ),
+                PermissionUtil.PERMISSIONS_READ_CALENDAR_AUTOMATIC
+            )
+            false
+        }
+    }
+
+    private fun checkCalendarBackupPermission(context: Context): Boolean {
+        return checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) && checkSelfPermission(
+            context, Manifest.permission.WRITE_CALENDAR
+        )
+    }
+
+    private fun checkContactBackupPermission(): Boolean {
+        return checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS)
+    }
+
+    private fun openCleanDate() {
+        if (checkAndAskForCalendarReadPermission() && checkAndAskForContactsReadPermission()) {
+            openDate(null)
+        }
+    }
+
+    private fun openDate(savedDate: Date?) {
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+        if (contactsPreferenceActivity == null) {
+            Toast.makeText(context, getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show()
+            return
+        }
+
+        val contactsBackupFolderString = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
+        val calendarBackupFolderString = resources.getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR
+        val storageManager = contactsPreferenceActivity.storageManager
+        val contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString)
+        val calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString)
+
+        val backupFiles = storageManager.getFolderContent(contactsBackupFolder, false)
+        backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false))
+        backupFiles.sortWith { o1: OCFile?, o2: OCFile? ->
+            if (o1 != null && o2 != null) {
+                o1.modificationTimestamp.compareTo(o2.modificationTimestamp)
+            } else {
+                -1
+            }
+        }
+
+        val cal = Calendar.getInstance()
+        val year: Int
+        val month: Int
+        val day: Int
+        if (savedDate == null) {
+            year = cal[Calendar.YEAR]
+            month = cal[Calendar.MONTH] + 1
+            day = cal[Calendar.DAY_OF_MONTH]
+        } else {
+            year = savedDate.year
+            month = savedDate.month
+            day = savedDate.day
+        }
+        if (backupFiles.size > 0 && backupFiles[backupFiles.size - 1] != null) {
+            datePickerDialog = DatePickerDialog(contactsPreferenceActivity, this, year, month, day)
+            datePickerDialog?.datePicker?.maxDate = backupFiles[backupFiles.size - 1]!!
+                .modificationTimestamp
+            datePickerDialog?.datePicker?.minDate = backupFiles[0]!!.modificationTimestamp
+            datePickerDialog?.setOnDismissListener { selectedDate = null }
+            datePickerDialog?.setTitle("")
+            datePickerDialog?.show()
+
+            viewThemeUtils?.platform?.colorTextButtons(
+                datePickerDialog!!.getButton(DatePickerDialog.BUTTON_NEGATIVE),
+                datePickerDialog!!.getButton(DatePickerDialog.BUTTON_POSITIVE)
+            )
+
+            // set background to transparent
+            datePickerDialog?.getButton(DatePickerDialog.BUTTON_NEGATIVE)?.setBackgroundColor(0x00000000)
+            datePickerDialog?.getButton(DatePickerDialog.BUTTON_POSITIVE)?.setBackgroundColor(0x00000000)
+        } else {
+            DisplayUtils.showSnackMessage(
+                requireView().findViewById<View>(R.id.contacts_linear_layout),
+                R.string.contacts_preferences_something_strange_happened
+            )
+        }
+    }
+
+    override fun onStop() {
+        super.onStop()
+
+        datePickerDialog?.dismiss()
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+
+        datePickerDialog?.let {
+            outState.putBoolean(KEY_CALENDAR_PICKER_OPEN, it.isShowing)
+
+            if (it.isShowing) {
+                outState.putInt(KEY_CALENDAR_DAY, it.datePicker.dayOfMonth)
+                outState.putInt(KEY_CALENDAR_MONTH, it.datePicker.month)
+                outState.putInt(KEY_CALENDAR_YEAR, it.datePicker.year)
+            }
+        }
+    }
+
+    @Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "ComplexMethod", "LongMethod", "MagicNumber")
+    override fun onDateSet(view: DatePicker, year: Int, month: Int, dayOfMonth: Int) {
+        val contactsPreferenceActivity = activity as ContactsPreferenceActivity?
+        if (contactsPreferenceActivity == null) {
+            Toast.makeText(context, getString(R.string.error_choosing_date), Toast.LENGTH_LONG).show()
+            return
+        }
+
+        selectedDate = Date(year, month, dayOfMonth)
+        val contactsBackupFolderString = resources.getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR
+        val calendarBackupFolderString = resources.getString(R.string.calendar_backup_folder) + OCFile.PATH_SEPARATOR
+        val storageManager = contactsPreferenceActivity.storageManager
+        val contactsBackupFolder = storageManager.getFileByDecryptedRemotePath(contactsBackupFolderString)
+        val calendarBackupFolder = storageManager.getFileByDecryptedRemotePath(calendarBackupFolderString)
+        val backupFiles = storageManager.getFolderContent(contactsBackupFolder, false)
+        backupFiles.addAll(storageManager.getFolderContent(calendarBackupFolder, false))
+
+        // find file with modification with date and time between 00:00 and 23:59
+        // if more than one file exists, take oldest
+        val date = Calendar.getInstance()
+        date[year, month] = dayOfMonth
+
+        // start
+        date[Calendar.HOUR] = 0
+        date[Calendar.MINUTE] = 0
+        date[Calendar.SECOND] = 1
+        date[Calendar.MILLISECOND] = 0
+        date[Calendar.AM_PM] = Calendar.AM
+        val start = date.timeInMillis
+
+        // end
+        date[Calendar.HOUR] = 23
+        date[Calendar.MINUTE] = 59
+        date[Calendar.SECOND] = 59
+        val end = date.timeInMillis
+        var contactsBackupToRestore: OCFile? = null
+        val calendarBackupsToRestore: MutableList<OCFile> = ArrayList()
+        for (file in backupFiles) {
+            if (start < file.modificationTimestamp && end > file.modificationTimestamp) {
+                // contact
+                if (MimeTypeUtil.isVCard(file)) {
+                    if (contactsBackupToRestore == null) {
+                        contactsBackupToRestore = file
+                    } else if (contactsBackupToRestore.modificationTimestamp < file.modificationTimestamp) {
+                        contactsBackupToRestore = file
+                    }
+                }
+
+                // calendars
+                if (showCalendarBackup && MimeTypeUtil.isCalendar(file)) {
+                    calendarBackupsToRestore.add(file)
+                }
+            }
+        }
+        val backupToRestore: MutableList<OCFile> = ArrayList()
+        if (contactsBackupToRestore != null) {
+            backupToRestore.add(contactsBackupToRestore)
+        }
+        backupToRestore.addAll(calendarBackupsToRestore)
+        if (backupToRestore.isEmpty()) {
+            DisplayUtils.showSnackMessage(
+                requireView().findViewById<View>(R.id.contacts_linear_layout),
+                R.string.contacts_preferences_no_file_found
+            )
+        } else {
+            val user = contactsPreferenceActivity.user.orElseThrow { RuntimeException() }
+            val files: Array<OCFile?> = arrayOfNulls(backupToRestore.size)
+
+            val contactListFragment = BackupListFragment.newInstance(files, user)
+
+            contactsPreferenceActivity.supportFragmentManager.beginTransaction()
+                .replace(R.id.frame_container, contactListFragment, BackupListFragment.TAG)
+                .addToBackStack(ContactsPreferenceActivity.BACKUP_TO_LIST)
+                .commit()
+        }
+    }
+
+    companion object {
+        val TAG = BackupFragment::class.java.simpleName
+        private const val ARG_SHOW_SIDEBAR = "SHOW_SIDEBAR"
+        private const val KEY_CALENDAR_PICKER_OPEN = "IS_CALENDAR_PICKER_OPEN"
+        private const val KEY_CALENDAR_DAY = "CALENDAR_DAY"
+        private const val KEY_CALENDAR_MONTH = "CALENDAR_MONTH"
+        private const val KEY_CALENDAR_YEAR = "CALENDAR_YEAR"
+        const val PREFERENCE_CONTACTS_BACKUP_ENABLED = "PREFERENCE_CONTACTS_BACKUP_ENABLED"
+        const val PREFERENCE_CALENDAR_BACKUP_ENABLED = "PREFERENCE_CALENDAR_BACKUP_ENABLED"
+
+        @JvmStatic
+        fun create(showSidebar: Boolean): BackupFragment {
+            val fragment = BackupFragment()
+            val bundle = Bundle()
+            bundle.putBoolean(ARG_SHOW_SIDEBAR, showSidebar)
+            fragment.arguments = bundle
+            return fragment
+        }
+    }
+}

+ 16 - 22
app/src/main/res/layout/backup_fragment.xml

@@ -17,8 +17,8 @@
   You should have received a copy of the GNU Affero General Public
   You should have received a copy of the GNU Affero General Public
   License along with this program. If not, see <http://www.gnu.org/licenses/>.
   License along with this program. If not, see <http://www.gnu.org/licenses/>.
 -->
 -->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/contacts_linear_layout"
     android:id="@+id/contacts_linear_layout"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
@@ -30,14 +30,14 @@
         android:layout_margin="@dimen/standard_margin"
         android:layout_margin="@dimen/standard_margin"
         android:orientation="vertical">
         android:orientation="vertical">
 
 
-        <TextView
+        <com.google.android.material.textview.MaterialTextView
             android:id="@+id/data_to_back_up_title"
             android:id="@+id/data_to_back_up_title"
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/data_to_back_up"
             android:text="@string/data_to_back_up"
             android:textStyle="bold" />
             android:textStyle="bold" />
 
 
-        <androidx.appcompat.widget.SwitchCompat
+        <com.google.android.material.materialswitch.MaterialSwitch
             android:id="@+id/contacts"
             android:id="@+id/contacts"
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_height="wrap_content"
@@ -46,22 +46,21 @@
             android:textColor="@color/text_color"
             android:textColor="@color/text_color"
             android:textSize="@dimen/two_line_primary_text_size" />
             android:textSize="@dimen/two_line_primary_text_size" />
 
 
-        <androidx.appcompat.widget.SwitchCompat
+        <com.google.android.material.materialswitch.MaterialSwitch
             android:id="@+id/calendar"
             android:id="@+id/calendar"
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_height="wrap_content"
             android:minHeight="48dp"
             android:minHeight="48dp"
             android:text="@string/calendar"
             android:text="@string/calendar"
             android:textColor="@color/text_color"
             android:textColor="@color/text_color"
-            android:textSize="@dimen/two_line_primary_text_size"
-            />
+            android:textSize="@dimen/two_line_primary_text_size" />
 
 
         <LinearLayout
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical">
             android:orientation="vertical">
 
 
-            <TextView
+            <com.google.android.material.textview.MaterialTextView
                 android:id="@+id/backup_settings_title"
                 android:id="@+id/backup_settings_title"
                 android:layout_width="match_parent"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
@@ -73,7 +72,7 @@
                 android:layout_width="match_parent"
                 android:layout_width="match_parent"
                 android:layout_height="48dp">
                 android:layout_height="48dp">
 
 
-                <androidx.appcompat.widget.SwitchCompat
+                <com.google.android.material.materialswitch.MaterialSwitch
                     android:id="@+id/daily_backup"
                     android:id="@+id/daily_backup"
                     android:layout_width="wrap_content"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_height="wrap_content"
@@ -89,14 +88,14 @@
                     android:orientation="vertical"
                     android:orientation="vertical"
                     android:gravity="center_vertical">
                     android:gravity="center_vertical">
 
 
-                    <TextView
+                    <com.google.android.material.textview.MaterialTextView
                         android:layout_width="wrap_content"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_height="wrap_content"
                         android:text="@string/daily_backup"
                         android:text="@string/daily_backup"
                         android:textColor="@color/text_color"
                         android:textColor="@color/text_color"
                         android:textSize="@dimen/two_line_primary_text_size" />
                         android:textSize="@dimen/two_line_primary_text_size" />
 
 
-                    <TextView
+                    <com.google.android.material.textview.MaterialTextView
                         android:id="@+id/last_backup_with_date"
                         android:id="@+id/last_backup_with_date"
                         android:layout_width="wrap_content"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_height="wrap_content"
@@ -113,32 +112,27 @@
             android:layout_width="match_parent"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_height="match_parent"
             android:layout_marginTop="@dimen/standard_margin"
             android:layout_marginTop="@dimen/standard_margin"
-            android:gravity="center"
-            android:orientation="horizontal"
-            android:weightSum="1.0">
+            android:gravity="end"
+            android:orientation="horizontal">
 
 
             <com.google.android.material.button.MaterialButton
             <com.google.android.material.button.MaterialButton
                 android:id="@+id/contacts_datepicker"
                 android:id="@+id/contacts_datepicker"
-                style="@style/OutlinedButton"
-                android:layout_width="0dp"
+                style="@style/Widget.Material3.Button.OutlinedButton"
+                android:layout_width="@dimen/backup_button_width"
                 android:layout_height="match_parent"
                 android:layout_height="match_parent"
-                android:layout_weight="0.5"
                 android:minHeight="@dimen/minimum_size_for_touchable_area"
                 android:minHeight="@dimen/minimum_size_for_touchable_area"
                 android:text="@string/restore_backup"
                 android:text="@string/restore_backup"
                 android:visibility="invisible"
                 android:visibility="invisible"
-                app:cornerRadius="@dimen/button_corner_radius"
                 tools:visibility="visible" />
                 tools:visibility="visible" />
 
 
             <com.google.android.material.button.MaterialButton
             <com.google.android.material.button.MaterialButton
                 android:id="@+id/backup_now"
                 android:id="@+id/backup_now"
-                android:layout_width="0dp"
+                android:layout_width="@dimen/backup_button_width"
                 android:layout_height="match_parent"
                 android:layout_height="match_parent"
                 android:layout_marginStart="@dimen/standard_half_margin"
                 android:layout_marginStart="@dimen/standard_half_margin"
-                android:layout_weight="0.5"
                 android:minHeight="@dimen/minimum_size_for_touchable_area"
                 android:minHeight="@dimen/minimum_size_for_touchable_area"
                 android:text="@string/contacts_backup_button"
                 android:text="@string/contacts_backup_button"
-                android:theme="@style/Button.Primary"
-                app:cornerRadius="@dimen/button_corner_radius" />
+                android:theme="@style/Widget.Material3.Button.IconButton.Filled" />
 
 
         </LinearLayout>
         </LinearLayout>
 
 

+ 0 - 443
app/src/main/res/layout/ssl_validator_layout.xml

@@ -1,443 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    ownCloud Android client application
-
-    Copyright (C) 2015 ownCloud Inc.
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2,
-    as published by the Free Software Foundation.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/root"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:orientation="vertical"
-	android:padding="@dimen/standard_padding">
-
-	<TextView
-		android:id="@+id/header"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:text="@string/ssl_validator_header"
-		android:paddingBottom="@dimen/standard_padding"
-        android:textStyle="bold"
-        android:textColor="@color/text_color"
-		android:textAppearance="?android:attr/textAppearanceMedium" />
-
-	<TextView
-		android:id="@+id/reason_cert_not_trusted"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_gravity="start"
-        android:paddingStart="@dimen/standard_half_padding"
-        android:paddingEnd="@dimen/zero"
-		android:text="@string/ssl_validator_reason_cert_not_trusted"
-		android:textAppearance="?android:attr/textAppearanceSmall"
-		 />
-
-	<TextView
-		android:id="@+id/reason_cert_expired"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_gravity="start"
-        android:paddingStart="@dimen/standard_half_padding"
-        android:paddingEnd="@dimen/zero"
-		android:text="@string/ssl_validator_reason_cert_expired"
-		android:textAppearance="?android:attr/textAppearanceSmall"
-		 />
-
-	<TextView
-		android:id="@+id/reason_cert_not_yet_valid"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_gravity="start"
-        android:paddingStart="@dimen/standard_half_padding"
-        android:paddingEnd="@dimen/zero"
-		android:text="@string/ssl_validator_reason_cert_not_yet_valid"
-		android:textAppearance="?android:attr/textAppearanceSmall"
-		 />
-
-	<TextView
-		android:id="@+id/reason_hostname_not_verified"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_gravity="start"
-        android:paddingStart="@dimen/standard_half_padding"
-        android:paddingEnd="@dimen/zero"
-		android:text="@string/ssl_validator_reason_hostname_not_verified"
-		android:textAppearance="?android:attr/textAppearanceSmall"
-		 />
-
-    <ScrollView
-        android:id="@+id/details_scroll"
-        android:visibility="gone"
-    	android:padding="@dimen/standard_half_padding"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/scroll_view_height">
-
-		<LinearLayout
-			android:id="@+id/details_view"
-			android:layout_width="wrap_content"
-			android:layout_height="wrap_content"
-			android:gravity="start"
-			android:orientation="vertical" >
-
-				<TextView
-        			android:id="@+id/label_subject"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text="@string/ssl_validator_label_subject"
-        			android:textAppearance="?android:attr/textAppearanceMedium"
-        		/>
-
-				<TextView
-				    android:id="@+id/label_subject_CN"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_CN"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_CN"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_subject_O"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_O"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_O"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_subject_OU"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_OU"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_OU"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_subject_ST"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_ST"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_ST"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_subject_C"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_C"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_C"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_subject_L"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_L"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_subject_L"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-
-				<TextView
-        			android:id="@+id/label_issuer"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text="@string/ssl_validator_label_issuer"
-        			android:textAppearance="?android:attr/textAppearanceMedium"
-        		/>
-
-				<TextView
-				    android:id="@+id/label_issuer_CN"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_CN"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_CN"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_issuer_O"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_O"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_O"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_issuer_OU"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_OU"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_OU"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_issuer_ST"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_ST"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_ST"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_issuer_C"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_C"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_C"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_issuer_L"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_L"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_issuer_L"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-        			android:id="@+id/label_validity"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text="@string/ssl_validator_label_validity"
-        			android:textAppearance="?android:attr/textAppearanceMedium"
-        		/>
-
-				<TextView
-				    android:id="@+id/label_validity_from"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_validity_from"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_validity_from"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/label_validity_to"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:text="@string/ssl_validator_label_validity_to"
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-				<TextView
-				    android:id="@+id/value_validity_to"
-				    android:layout_width="wrap_content"
-				    android:layout_height="wrap_content"
-				    android:paddingBottom="@dimen/alternate_half_padding"
-				    android:text=""
-				    android:textAppearance="?android:attr/textAppearanceSmall"
-				/>
-
-
-				<TextView
-        			android:id="@+id/label_signature"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text="@string/ssl_validator_label_signature"
-        			android:textAppearance="?android:attr/textAppearanceMedium"
-        		/>
-
-				<TextView
-        			android:id="@+id/label_signature_algorithm"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-        			android:text="@string/ssl_validator_label_signature_algorithm"
-        			android:textAppearance="?android:attr/textAppearanceSmall"
-        		/>
-
-								<TextView
-        			android:id="@+id/value_signature_algorithm"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text=""
-        			android:textAppearance="?android:attr/textAppearanceSmall"
-        		/>
-
-
-				<TextView
-        			android:id="@+id/value_signature"
-        			android:layout_width="wrap_content"
-        			android:layout_height="wrap_content"
-					android:paddingBottom="@dimen/alternate_half_padding"
-        			android:text=""
-        			android:textAppearance="?android:attr/textAppearanceSmall"
-        		/>
-
-		</LinearLayout>
-
-    </ScrollView>
-
-	<TextView
-        android:id="@+id/question"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-		android:paddingTop="@dimen/standard_padding"
-        android:text="@string/ssl_validator_question"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        >
-    </TextView>
-
-	<LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center" >
-
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/cancel"
-			style="@style/Button.Borderless"
-            android:layout_width="@dimen/zero"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/common_no" />
-
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/details_btn"
-			style="@style/Button.Borderless"
-            android:layout_width="@dimen/zero"
-            android:layout_height="wrap_content"
-            android:layout_weight="2"
-            android:text="@string/ssl_validator_btn_details_see" />
-
-        <com.google.android.material.button.MaterialButton
-            android:id="@+id/ok"
-			style="@style/Button.Borderless"
-            android:layout_width="@dimen/zero"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:text="@string/common_yes" />
-
-    </LinearLayout>
-
-</LinearLayout>

+ 0 - 58
app/src/main/res/layout/storage_permission_dialog.xml

@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Nextcloud Android client application
-  ~
-  ~ @author Álvaro Brey Vilas
-  ~ Copyright (C) 2022 Álvaro Brey Vilas
-  ~ Copyright (C) 2022 Nextcloud GmbH
-  ~
-  ~ This program is free software: you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation, either version 3 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-  -->
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    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"
-    android:clickable="true"
-    android:focusable="true"
-    android:orientation="vertical"
-    android:paddingHorizontal="?dialogPreferredPadding">
-
-    <TextView
-        android:id="@+id/storage_permission_explanation"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:text="@string/file_management_permission_optional_text" />
-
-    <com.google.android.material.button.MaterialButton
-        android:layout_marginTop="@dimen/standard_padding"
-        android:id="@+id/btn_full_access"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/storage_permission_full_access"
-        android:theme="@style/Button.Primary"
-        app:cornerRadius="@dimen/button_corner_radius"
-        app:layout_constraintTop_toBottomOf="@id/storage_permission_explanation" />
-
-    <com.google.android.material.button.MaterialButton
-        android:id="@+id/btn_read_only"
-        style="@style/OutlinedButton"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/storage_permission_media_read_only"
-        app:cornerRadius="@dimen/button_corner_radius"
-        app:layout_constraintTop_toBottomOf="@id/btn_full_access" />
-
-
-</androidx.constraintlayout.widget.ConstraintLayout>

+ 12 - 12
app/src/main/res/values-eu/strings.xml

@@ -307,7 +307,7 @@
     <string name="etm_transfer_remote_path">Urruneko bide-izena</string>
     <string name="etm_transfer_remote_path">Urruneko bide-izena</string>
     <string name="etm_transfer_type">Transferitu</string>
     <string name="etm_transfer_type">Transferitu</string>
     <string name="etm_transfer_type_download">Deskargatu</string>
     <string name="etm_transfer_type_download">Deskargatu</string>
-    <string name="etm_transfer_type_upload">Kargatu</string>
+    <string name="etm_transfer_type_upload">Igo</string>
     <string name="fab_label">Gehitu edo igo</string>
     <string name="fab_label">Gehitu edo igo</string>
     <string name="failed_to_download">Huts egin du fitxategia deskarga-kudeatzailera pasatzean</string>
     <string name="failed_to_download">Huts egin du fitxategia deskarga-kudeatzailera pasatzean</string>
     <string name="failed_to_print">Huts egin du fitxategia inprimatzean</string>
     <string name="failed_to_print">Huts egin du fitxategia inprimatzean</string>
@@ -342,7 +342,7 @@
     <string name="file_management_permission">Baimenak behar dira</string>
     <string name="file_management_permission">Baimenak behar dira</string>
     <string name="file_management_permission_optional">Biltegiratze-baimenak</string>
     <string name="file_management_permission_optional">Biltegiratze-baimenak</string>
     <string name="file_management_permission_optional_text">%1$shobeto dabil biltegia atzitzeko baimenekin. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo argazki eta bideoak \"irakurtzeko soilik\" baimena eman.</string>
     <string name="file_management_permission_optional_text">%1$shobeto dabil biltegia atzitzeko baimenekin. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo argazki eta bideoak \"irakurtzeko soilik\" baimena eman.</string>
-    <string name="file_management_permission_text">%1$s-k fitxategiak kudeatzeko baimenak behar ditu fitxategiak kargatzeko. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo \"irakurtzeko soilik\" baimena argazki eta bideoentzat.</string>
+    <string name="file_management_permission_text">%1$s-k fitxategiak kudeatzeko baimenak behar ditu fitxategiak igotzeko. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo \"irakurtzeko soilik\" baimena argazki eta bideoentzat.</string>
     <string name="file_migration_checking_destination">Helburua egiaztatzen...</string>
     <string name="file_migration_checking_destination">Helburua egiaztatzen...</string>
     <string name="file_migration_cleaning">Garbitzen…</string>
     <string name="file_migration_cleaning">Garbitzen…</string>
     <string name="file_migration_dialog_title">Datu-biltegiratze karpeta eguneratzen</string>
     <string name="file_migration_dialog_title">Datu-biltegiratze karpeta eguneratzen</string>
@@ -424,7 +424,7 @@
     <string name="image_preview_unit_millimetres">%s mm</string>
     <string name="image_preview_unit_millimetres">%s mm</string>
     <string name="image_preview_unit_seconds">%s s</string>
     <string name="image_preview_unit_seconds">%s s</string>
     <string name="in_folder">%1$skarpetan</string>
     <string name="in_folder">%1$skarpetan</string>
-    <string name="instant_upload_existing">Existitzen diren fitxategiak ere kargatu</string>
+    <string name="instant_upload_existing">Existitzen diren fitxategiak ere igo</string>
     <string name="instant_upload_on_charging">Igo bakarrik gailua kargatzean</string>
     <string name="instant_upload_on_charging">Igo bakarrik gailua kargatzean</string>
     <string name="instant_upload_path">/InstantUpload</string>
     <string name="instant_upload_path">/InstantUpload</string>
     <string name="invalid_url">URL baliogabea</string>
     <string name="invalid_url">URL baliogabea</string>
@@ -433,7 +433,7 @@
     <string name="last_backup">Azken babeskopia: %1$s</string>
     <string name="last_backup">Azken babeskopia: %1$s</string>
     <string name="link">Esteka</string>
     <string name="link">Esteka</string>
     <string name="link_name">Esteka-izena</string>
     <string name="link_name">Esteka-izena</string>
-    <string name="link_share_allow_upload_and_editing">Onartu kargatzea eta edizioa</string>
+    <string name="link_share_allow_upload_and_editing">Onartu igotzea eta edizioa</string>
     <string name="link_share_editing">Edizioa</string>
     <string name="link_share_editing">Edizioa</string>
     <string name="link_share_file_drop">Fitxategia jaregitea (igotzeko soilik)</string>
     <string name="link_share_file_drop">Fitxategia jaregitea (igotzeko soilik)</string>
     <string name="link_share_view_only">Ikustea soilik</string>
     <string name="link_share_view_only">Ikustea soilik</string>
@@ -522,7 +522,7 @@
     <string name="notification_channel_push_description">Erakutsi zerbitzariak bidalitako push jakinarazpenak: Aipamenak iruzkinetan, urruneko partekatze berrien harrera, administratzaileak argitaratutako argitalpenak etab.</string>
     <string name="notification_channel_push_description">Erakutsi zerbitzariak bidalitako push jakinarazpenak: Aipamenak iruzkinetan, urruneko partekatze berrien harrera, administratzaileak argitaratutako argitalpenak etab.</string>
     <string name="notification_channel_push_name">Push jakinarazpenak</string>
     <string name="notification_channel_push_name">Push jakinarazpenak</string>
     <string name="notification_channel_upload_description">Erakutsi igoeraren egoera</string>
     <string name="notification_channel_upload_description">Erakutsi igoeraren egoera</string>
-    <string name="notification_channel_upload_name_short">Kargak</string>
+    <string name="notification_channel_upload_name_short">Igotzeak</string>
     <string name="notification_icon">Jakinarazpen ikonoa</string>
     <string name="notification_icon">Jakinarazpen ikonoa</string>
     <string name="notifications_no_results_headline">Jakinarazpenik ez</string>
     <string name="notifications_no_results_headline">Jakinarazpenik ez</string>
     <string name="notifications_no_results_message">Begiratu beranduago, mesedez.</string>
     <string name="notifications_no_results_message">Begiratu beranduago, mesedez.</string>
@@ -556,13 +556,13 @@
     <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
     <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
     <string name="player_stop">gelditu</string>
     <string name="player_stop">gelditu</string>
     <string name="player_toggle">txandakatu</string>
     <string name="player_toggle">txandakatu</string>
-    <string name="power_save_check_dialog_message">Energia aurrezteko kontrola desgaituz gero, fitxategiak bateria baxu dagoenean kargatu litezke!</string>
+    <string name="power_save_check_dialog_message">Energia aurrezteko kontrola desgaituz gero, fitxategiak bateria baxu dagoenean igo litezke!</string>
     <string name="pref_behaviour_entries_delete_file">ezabatua</string>
     <string name="pref_behaviour_entries_delete_file">ezabatua</string>
     <string name="pref_behaviour_entries_keep_file">jatorrizko karpetan mantenduko da</string>
     <string name="pref_behaviour_entries_keep_file">jatorrizko karpetan mantenduko da</string>
     <string name="pref_behaviour_entries_move">aplikazioaren karpetara mugitu da</string>
     <string name="pref_behaviour_entries_move">aplikazioaren karpetara mugitu da</string>
     <string name="pref_instant_name_collision_policy_dialogTitle">Zer egin fitxategia dagoeneko existitzen bada?</string>
     <string name="pref_instant_name_collision_policy_dialogTitle">Zer egin fitxategia dagoeneko existitzen bada?</string>
     <string name="pref_instant_name_collision_policy_entries_always_ask">Galdetu beti</string>
     <string name="pref_instant_name_collision_policy_entries_always_ask">Galdetu beti</string>
-    <string name="pref_instant_name_collision_policy_entries_cancel">Saltatu karga</string>
+    <string name="pref_instant_name_collision_policy_entries_cancel">Saltatu igotzea</string>
     <string name="pref_instant_name_collision_policy_entries_overwrite">Gainidatzi urruneko bertsioa</string>
     <string name="pref_instant_name_collision_policy_entries_overwrite">Gainidatzi urruneko bertsioa</string>
     <string name="pref_instant_name_collision_policy_entries_rename">Aldatu izena bertsio berriari</string>
     <string name="pref_instant_name_collision_policy_entries_rename">Aldatu izena bertsio berriari</string>
     <string name="pref_instant_name_collision_policy_title">Zer egin fitxategia dagoeneko existitzen bada?</string>
     <string name="pref_instant_name_collision_policy_title">Zer egin fitxategia dagoeneko existitzen bada?</string>
@@ -610,7 +610,7 @@
     <string name="prefs_show_hidden_files">Erakutsi ezkutuko fitxategiak</string>
     <string name="prefs_show_hidden_files">Erakutsi ezkutuko fitxategiak</string>
     <string name="prefs_sourcecode">Eskuratu iturburu-kodea</string>
     <string name="prefs_sourcecode">Eskuratu iturburu-kodea</string>
     <string name="prefs_storage_path">Datu-biltegiratze karpeta</string>
     <string name="prefs_storage_path">Datu-biltegiratze karpeta</string>
-    <string name="prefs_sycned_folders_summary">Kudeatu karpeten kargatze automatikoa</string>
+    <string name="prefs_sycned_folders_summary">Kudeatu karpeten igotze automatikoa</string>
     <string name="prefs_synced_folders_local_path_title">Karpeta lokala</string>
     <string name="prefs_synced_folders_local_path_title">Karpeta lokala</string>
     <string name="prefs_synced_folders_remote_path_title">Urruneko karpeta</string>
     <string name="prefs_synced_folders_remote_path_title">Urruneko karpeta</string>
     <string name="prefs_theme_title">Gaia</string>
     <string name="prefs_theme_title">Gaia</string>
@@ -799,7 +799,7 @@
     <string name="sync_fail_ticker_unauthorized">Sinkronizazioak huts egin du, hasi saioa berriz</string>
     <string name="sync_fail_ticker_unauthorized">Sinkronizazioak huts egin du, hasi saioa berriz</string>
     <string name="sync_file_nothing_to_do_msg">Fitxategien edukiak dagoeneko sinkronizaturik</string>
     <string name="sync_file_nothing_to_do_msg">Fitxategien edukiak dagoeneko sinkronizaturik</string>
     <string name="sync_folder_failed_content">%1$s karpetaren sinkronizazioa ezin izan da osatu</string>
     <string name="sync_folder_failed_content">%1$s karpetaren sinkronizazioa ezin izan da osatu</string>
-    <string name="sync_foreign_files_forgotten_explanation">1.3.16 bertsiotik aurrera, gailu honetatik kargatzen diren fitxategiak %1$s  karpeta lokalera kopiatzen dira, fitxategi bat hainbat konturekin sinkronizatzen denean datuak gal ez daitezen.\n\nAldaketa honen ondorioz, aplikazio honen aurreko bertsioekin kargatutako fitxategi guztiak %2$s karpetara kopiatu dira. Hala ere, kontua sinkronizatzean gertatutako akats batek eragiketa hori osatzea eragotzi du. Fitxategiak dauden bezala utz ditzakezu eta %3$s(r)ako esteka ezabatu, edo fitxategia(k) %1$s karpetara eraman eta %4$s(r)ako esteka mantendu.\n\nBehean zerrendatuta daude fitxategi lokala(k) eta estekatutako %5$s(e)ko urruneko fitxategia(k).</string>
+    <string name="sync_foreign_files_forgotten_explanation">1.3.16 bertsiotik aurrera, gailu honetatik kargatzen diren fitxategiak %1$s  karpeta lokalera kopiatzen dira, fitxategi bat hainbat konturekin sinkronizatzen denean datuak gal ez daitezen.\n\nAldaketa honen ondorioz, aplikazio honen aurreko bertsioekin igotako fitxategi guztiak %2$s karpetara kopiatu dira. Hala ere, kontua sinkronizatzean gertatutako akats batek eragiketa hori osatzea eragotzi du. Fitxategiak dauden bezala utz ditzakezu eta %3$s(r)ako esteka ezabatu, edo fitxategia(k) %1$s karpetara eraman eta %4$s(r)ako esteka mantendu.\n\nBehean zerrendatuta daude fitxategi lokala(k) eta estekatutako %5$s(e)ko urruneko fitxategia(k).</string>
     <string name="sync_foreign_files_forgotten_ticker">Fitxategi lokal batzuk ahaztu dira</string>
     <string name="sync_foreign_files_forgotten_ticker">Fitxategi lokal batzuk ahaztu dira</string>
     <string name="sync_in_progress">Fitxategiaren bertsio berriena ekartzen.</string>
     <string name="sync_in_progress">Fitxategiaren bertsio berriena ekartzen.</string>
     <string name="sync_not_enough_space_dialog_action_choose">Aukeratu zer sinkronizatu</string>
     <string name="sync_not_enough_space_dialog_action_choose">Aukeratu zer sinkronizatu</string>
@@ -810,7 +810,7 @@
     <string name="sync_string_files">Fitxategiak</string>
     <string name="sync_string_files">Fitxategiak</string>
     <string name="synced_folder_settings_button">Ezarpenak botoia</string>
     <string name="synced_folder_settings_button">Ezarpenak botoia</string>
     <string name="synced_folders_configure_folders">Konfiguratu karpetak</string>
     <string name="synced_folders_configure_folders">Konfiguratu karpetak</string>
-    <string name="synced_folders_new_info">Berehalako karga erabat aldatu da. Konfiguratu berriro zure karga automatikoa menu nagusitik.\n\nGozatu karga automatiko berri eta hedatuaz. </string>
+    <string name="synced_folders_new_info">Berehalako igotzeak guztiz aldatu dira. Konfiguratu berriro zure igotze automatikoa menu nagusitik.\n\nGozatu igotze automatiko berri eta hedatuaz. </string>
     <string name="synced_folders_no_results">Ez da multimedia karpetarik aurkitu</string>
     <string name="synced_folders_no_results">Ez da multimedia karpetarik aurkitu</string>
     <string name="synced_folders_preferences_folder_path"> %1$s-(r)entzat</string>
     <string name="synced_folders_preferences_folder_path"> %1$s-(r)entzat</string>
     <string name="synced_folders_type">Mota</string>
     <string name="synced_folders_type">Mota</string>
@@ -856,7 +856,7 @@
     <string name="upload_file_dialog_filetype_snippet_text">Kode-zati testu-fitxategia(.txt)</string>
     <string name="upload_file_dialog_filetype_snippet_text">Kode-zati testu-fitxategia(.txt)</string>
     <string name="upload_file_dialog_title">Sartu igoko den fitxategi-izena eta fitxategi mota</string>
     <string name="upload_file_dialog_title">Sartu igoko den fitxategi-izena eta fitxategi mota</string>
     <string name="upload_files">Igo fitxategiak</string>
     <string name="upload_files">Igo fitxategiak</string>
-    <string name="upload_item_action_button">Kargatu elementuaren ekintza botoia</string>
+    <string name="upload_item_action_button">Elementua igotzeko ekintza botoia</string>
     <string name="upload_list_delete">Ezabatu</string>
     <string name="upload_list_delete">Ezabatu</string>
     <string name="upload_list_empty_headline">Ezin dira fitxategiak igo</string>
     <string name="upload_list_empty_headline">Ezin dira fitxategiak igo</string>
     <string name="upload_list_empty_text_auto_upload">Edukiak igo edo auto-igotzea aktiba ezazu.</string>
     <string name="upload_list_empty_text_auto_upload">Edukiak igo edo auto-igotzea aktiba ezazu.</string>
@@ -875,7 +875,7 @@
     <string name="uploader_error_message_read_permission_not_granted">%1$s ez dago baimendua jasotako fitxategia irakurtzeko</string>
     <string name="uploader_error_message_read_permission_not_granted">%1$s ez dago baimendua jasotako fitxategia irakurtzeko</string>
     <string name="uploader_error_message_source_file_not_copied">Fitxategia ezin izan da aldi baterako karpetan kopiatu. Saiatu berriro bidaltzen.</string>
     <string name="uploader_error_message_source_file_not_copied">Fitxategia ezin izan da aldi baterako karpetan kopiatu. Saiatu berriro bidaltzen.</string>
     <string name="uploader_error_message_source_file_not_found">Kargatzeko hautatutako fitxategia ez da aurkitu. Mesedez, egiaztatu fitxategia existitzen dela.</string>
     <string name="uploader_error_message_source_file_not_found">Kargatzeko hautatutako fitxategia ez da aurkitu. Mesedez, egiaztatu fitxategia existitzen dela.</string>
-    <string name="uploader_error_title_file_cannot_be_uploaded">Fitxategi hau ezin da kargatu</string>
+    <string name="uploader_error_title_file_cannot_be_uploaded">Fitxategi hau ezin da igo</string>
     <string name="uploader_error_title_no_file_to_upload">Ez dago fitxategirik kargatzeko</string>
     <string name="uploader_error_title_no_file_to_upload">Ez dago fitxategirik kargatzeko</string>
     <string name="uploader_info_dirname">Karpetaren izena</string>
     <string name="uploader_info_dirname">Karpetaren izena</string>
     <string name="uploader_top_message">Aukeratu karga-karpeta</string>
     <string name="uploader_top_message">Aukeratu karga-karpeta</string>

+ 67 - 0
app/src/main/res/values-ja-rJP/strings.xml

@@ -30,9 +30,11 @@
     <string name="activity_icon">アクティビティ</string>
     <string name="activity_icon">アクティビティ</string>
     <string name="add_another_public_share_link">別のリンクを作成</string>
     <string name="add_another_public_share_link">別のリンクを作成</string>
     <string name="add_new_public_share">新規公開共有リンクを追加</string>
     <string name="add_new_public_share">新規公開共有リンクを追加</string>
+    <string name="add_new_secure_file_drop">新しいセキュアなファイルドロップを追加</string>
     <string name="add_to_cloud">%1$s に追加</string>
     <string name="add_to_cloud">%1$s に追加</string>
     <string name="advanced_settings">高度な設定</string>
     <string name="advanced_settings">高度な設定</string>
     <string name="allow_resharing">再共有を許可</string>
     <string name="allow_resharing">再共有を許可</string>
+    <string name="app_widget_description">ダッシュボードから一つのウィジェットを表示</string>
     <string name="appbar_search_in">%s の中を検索</string>
     <string name="appbar_search_in">%s の中を検索</string>
     <string name="associated_account_not_found">関連付けられたアカウントが見つかりません!</string>
     <string name="associated_account_not_found">関連付けられたアカウントが見つかりません!</string>
     <string name="auth_access_failed">アクセスに失敗しました: %1$s</string>
     <string name="auth_access_failed">アクセスに失敗しました: %1$s</string>
@@ -85,11 +87,14 @@
     <string name="calendars">カレンダー</string>
     <string name="calendars">カレンダー</string>
     <string name="certificate_load_problem">証明書の読み込みに問題がありました。</string>
     <string name="certificate_load_problem">証明書の読み込みに問題がありました。</string>
     <string name="changelog_dev_version">開発バージョンの変更履歴</string>
     <string name="changelog_dev_version">開発バージョンの変更履歴</string>
+    <string name="check_back_later_or_reload">後で確認するか、再読込をしてください</string>
     <string name="checkbox">チェックボックス</string>
     <string name="checkbox">チェックボックス</string>
     <string name="choose_local_folder">ローカルフォルダーを選択…</string>
     <string name="choose_local_folder">ローカルフォルダーを選択…</string>
+    <string name="choose_location">場所を選択</string>
     <string name="choose_remote_folder">リモートフォルダを選択…</string>
     <string name="choose_remote_folder">リモートフォルダを選択…</string>
     <string name="choose_template_helper_text">テンプレートを選択してファイル名を入力してください。</string>
     <string name="choose_template_helper_text">テンプレートを選択してファイル名を入力してください。</string>
     <string name="choose_which_file">保存するファイルを選択</string>
     <string name="choose_which_file">保存するファイルを選択</string>
+    <string name="choose_widget">ウィジェットを選択</string>
     <string name="clear_notifications_failed">通知の削除に失敗</string>
     <string name="clear_notifications_failed">通知の削除に失敗</string>
     <string name="clear_status_message">メッセージを消去</string>
     <string name="clear_status_message">メッセージを消去</string>
     <string name="clear_status_message_after">ステータスメッセージの有効期限</string>
     <string name="clear_status_message_after">ステータスメッセージの有効期限</string>
@@ -149,6 +154,7 @@
     <string name="conflict_local_file">ローカルファイル</string>
     <string name="conflict_local_file">ローカルファイル</string>
     <string name="conflict_message_description">両方のバージョンを選択した場合、ローカルファイルはファイル名に数字が追加されます。</string>
     <string name="conflict_message_description">両方のバージョンを選択した場合、ローカルファイルはファイル名に数字が追加されます。</string>
     <string name="conflict_server_file">サーバーファイル</string>
     <string name="conflict_server_file">サーバーファイル</string>
+    <string name="contact_backup_title">連絡先のバックアップ</string>
     <string name="contactlist_item_icon">連絡先リストのユーザーアイコン</string>
     <string name="contactlist_item_icon">連絡先リストのユーザーアイコン</string>
     <string name="contactlist_no_permission">許可がありません、インポートできません。</string>
     <string name="contactlist_no_permission">許可がありません、インポートできません。</string>
     <string name="contacts">連絡先</string>
     <string name="contacts">連絡先</string>
@@ -193,13 +199,20 @@
     <string name="did_not_check_for_dupes">重複をチェックしませんでした</string>
     <string name="did_not_check_for_dupes">重複をチェックしませんでした</string>
     <string name="digest_algorithm_not_available">このスマートフォンでは、ダイジェストアルゴリズムが利用できません。</string>
     <string name="digest_algorithm_not_available">このスマートフォンでは、ダイジェストアルゴリズムが利用できません。</string>
     <string name="direct_login_failed">ダイレクトリンクからのログインに失敗しました!</string>
     <string name="direct_login_failed">ダイレクトリンクからのログインに失敗しました!</string>
+    <string name="direct_login_text">%1$s で %2$s へログインする</string>
     <string name="disable_new_media_folder_detection_notifications">無効</string>
     <string name="disable_new_media_folder_detection_notifications">無効</string>
     <string name="dismiss">閉じる</string>
     <string name="dismiss">閉じる</string>
     <string name="dismiss_notification_description">通知を閉じる</string>
     <string name="dismiss_notification_description">通知を閉じる</string>
     <string name="dnd">取り込み中</string>
     <string name="dnd">取り込み中</string>
+    <string name="document_scan_export_dialog_images">複数の画像</string>
+    <string name="document_scan_export_dialog_pdf">PDFファイル</string>
+    <string name="document_scan_export_dialog_title">エクスポートする種類を選択</string>
+    <string name="document_scan_pdf_generation_failed">PDFの生成に失敗しました</string>
+    <string name="document_scan_pdf_generation_in_progress">PDFを生成中...</string>
     <string name="done">完了</string>
     <string name="done">完了</string>
     <string name="dontClear">消去しない</string>
     <string name="dontClear">消去しない</string>
     <string name="download_cannot_create_file">ローカルファイルが作成できません</string>
     <string name="download_cannot_create_file">ローカルファイルが作成できません</string>
+    <string name="download_download_invalid_local_file_name">ローカルファイルに対して無効なファイル名</string>
     <string name="download_latest_dev_version">最新の開発バージョンをダウンロード</string>
     <string name="download_latest_dev_version">最新の開発バージョンをダウンロード</string>
     <string name="downloader_download_failed_content">%1$sをダウンロードできませんでした</string>
     <string name="downloader_download_failed_content">%1$sをダウンロードできませんでした</string>
     <string name="downloader_download_failed_credentials_error">ダウンロード失敗、要 再ログイン</string>
     <string name="downloader_download_failed_credentials_error">ダウンロード失敗、要 再ログイン</string>
@@ -229,10 +242,14 @@
     <string name="drawer_quota">%2$s 中%1$s が使われています。</string>
     <string name="drawer_quota">%2$s 中%1$s が使われています。</string>
     <string name="drawer_quota_unlimited">%1$s使用中</string>
     <string name="drawer_quota_unlimited">%1$s使用中</string>
     <string name="drawer_synced_folders">自動アップロード</string>
     <string name="drawer_synced_folders">自動アップロード</string>
+    <string name="e2e_not_yet_setup">E2E暗号化が未設定</string>
+    <string name="e2e_offline">インターネット接続なしには不可能です</string>
     <string name="ecosystem_apps_display_more">さらに表示</string>
     <string name="ecosystem_apps_display_more">さらに表示</string>
     <string name="ecosystem_apps_display_notes">ノート</string>
     <string name="ecosystem_apps_display_notes">ノート</string>
     <string name="ecosystem_apps_display_talk">トーク</string>
     <string name="ecosystem_apps_display_talk">トーク</string>
+    <string name="ecosystem_apps_more">もっと Nextcloud アプリを見る</string>
     <string name="ecosystem_apps_notes">Nextcloud ノート</string>
     <string name="ecosystem_apps_notes">Nextcloud ノート</string>
+    <string name="ecosystem_apps_talk">Nextcloud Talk</string>
     <string name="encrypted">暗号化設定</string>
     <string name="encrypted">暗号化設定</string>
     <string name="end_to_end_encryption_confirm_button">暗号化を設定する</string>
     <string name="end_to_end_encryption_confirm_button">暗号化を設定する</string>
     <string name="end_to_end_encryption_decrypting">復号化中…</string>
     <string name="end_to_end_encryption_decrypting">復号化中…</string>
@@ -262,7 +279,9 @@
     <string name="error_report_issue_text">問題を報告しますか? (GitHubのアカウントが必要です)</string>
     <string name="error_report_issue_text">問題を報告しますか? (GitHubのアカウントが必要です)</string>
     <string name="error_retrieving_file">ファイルの取得中にエラーが発生しました</string>
     <string name="error_retrieving_file">ファイルの取得中にエラーが発生しました</string>
     <string name="error_retrieving_templates">テンプレートの取得中にエラーが発生しました</string>
     <string name="error_retrieving_templates">テンプレートの取得中にエラーが発生しました</string>
+    <string name="error_showing_encryption_dialog">暗号化設定ダイアログの表示エラー</string>
     <string name="error_starting_direct_camera_upload">カメラ起動エラー</string>
     <string name="error_starting_direct_camera_upload">カメラ起動エラー</string>
+    <string name="error_starting_doc_scan">文書スキャンの開始エラー</string>
     <string name="etm_accounts">アカウント</string>
     <string name="etm_accounts">アカウント</string>
     <string name="etm_background_job_name">ジョブの名前</string>
     <string name="etm_background_job_name">ジョブの名前</string>
     <string name="etm_background_job_progress">進捗状況</string>
     <string name="etm_background_job_progress">進捗状況</string>
@@ -293,6 +312,7 @@
     <string name="failed_update_ui">UIの更新に失敗しました</string>
     <string name="failed_update_ui">UIの更新に失敗しました</string>
     <string name="favorite">お気に入りに追加</string>
     <string name="favorite">お気に入りに追加</string>
     <string name="favorite_icon">お気に入り</string>
     <string name="favorite_icon">お気に入り</string>
+    <string name="file_already_exists">ファイル名が既に存在します</string>
     <string name="file_delete">削除</string>
     <string name="file_delete">削除</string>
     <string name="file_detail_activity_error">ファイルのアクティビティ取得エラー</string>
     <string name="file_detail_activity_error">ファイルのアクティビティ取得エラー</string>
     <string name="file_details_no_content">詳細のロードに失敗しました</string>
     <string name="file_details_no_content">詳細のロードに失敗しました</string>
@@ -358,6 +378,7 @@
     <string name="filename_hint">ファイル名</string>
     <string name="filename_hint">ファイル名</string>
     <string name="first_run_1_text">あなたのデータをセキュアなままコントロールしましょう</string>
     <string name="first_run_1_text">あなたのデータをセキュアなままコントロールしましょう</string>
     <string name="first_run_2_text">セキュアなコラボレーションとファイル交換</string>
     <string name="first_run_2_text">セキュアなコラボレーションとファイル交換</string>
+    <string name="first_run_3_text">使いやすいWebメールやカレンダーや &amp; 連絡先</string>
     <string name="first_run_4_text">画面共有やオンラインミーティングやウェブ会議</string>
     <string name="first_run_4_text">画面共有やオンラインミーティングやウェブ会議</string>
     <string name="folder_already_exists">フォルダーはすでに存在します</string>
     <string name="folder_already_exists">フォルダーはすでに存在します</string>
     <string name="folder_confirm_create">作成</string>
     <string name="folder_confirm_create">作成</string>
@@ -383,6 +404,18 @@
     <string name="hint_password">パスワード</string>
     <string name="hint_password">パスワード</string>
     <string name="host_not_available">サーバーが利用できません</string>
     <string name="host_not_available">サーバーが利用できません</string>
     <string name="host_your_own_server">自分のサーバーをホストする</string>
     <string name="host_your_own_server">自分のサーバーをホストする</string>
+    <string name="icon_of_dashboard_widget">ダッシュボードウィジェットのアイコン</string>
+    <string name="image_editor_file_edited_suffix">編集された</string>
+    <string name="image_editor_flip_horizontal">左右反転</string>
+    <string name="image_editor_flip_vertical">上下反転</string>
+    <string name="image_editor_rotate_ccw">反時計回りに回す</string>
+    <string name="image_editor_rotate_cw">時計回りに回す</string>
+    <string name="image_editor_unable_to_edit_image">画像の編集が不可能です</string>
+    <string name="image_preview_filedetails">ファイルの詳細</string>
+    <string name="image_preview_unit_iso">ISO %s</string>
+    <string name="image_preview_unit_megapixel">%s MP</string>
+    <string name="image_preview_unit_millimetres">%s mm</string>
+    <string name="image_preview_unit_seconds">%s 秒</string>
     <string name="in_folder">フォルダー%1$sの中で</string>
     <string name="in_folder">フォルダー%1$sの中で</string>
     <string name="instant_upload_existing">既存のファイルもアップロード</string>
     <string name="instant_upload_existing">既存のファイルもアップロード</string>
     <string name="instant_upload_on_charging">充電中のみアップロード</string>
     <string name="instant_upload_on_charging">充電中のみアップロード</string>
@@ -403,12 +436,14 @@
     <string name="local_file_not_found_message">ローカルファイルシステムにファイルが見つかりません</string>
     <string name="local_file_not_found_message">ローカルファイルシステムにファイルが見つかりません</string>
     <string name="local_folder_friendly_path">%1$s/%2$s</string>
     <string name="local_folder_friendly_path">%1$s/%2$s</string>
     <string name="local_folder_list_empty">これ以上フォルダーがありません。</string>
     <string name="local_folder_list_empty">これ以上フォルダーがありません。</string>
+    <string name="locate_folder">フォルダーの配置</string>
     <string name="lock_expiration_info">有効期限: %1$s</string>
     <string name="lock_expiration_info">有効期限: %1$s</string>
     <string name="lock_file">ファイルをロック</string>
     <string name="lock_file">ファイルをロック</string>
     <string name="locked_by">%1$sによりロック</string>
     <string name="locked_by">%1$sによりロック</string>
     <string name="locked_by_app">%1$sアプリによりロック</string>
     <string name="locked_by_app">%1$sアプリによりロック</string>
     <string name="log_send_mail_subject">%1$s アンドロイドアプリログ</string>
     <string name="log_send_mail_subject">%1$s アンドロイドアプリログ</string>
     <string name="log_send_no_mail_app">ログを送信するためのアプリが見つかりません。メールクライアントをインストールしてください。</string>
     <string name="log_send_no_mail_app">ログを送信するためのアプリが見つかりません。メールクライアントをインストールしてください。</string>
+    <string name="logged_in_as">%1$sとしてログインしました</string>
     <string name="login">ログイン</string>
     <string name="login">ログイン</string>
     <string name="login_url_helper_text">%1$sをWeb画面でブラウザーで開くときのURL</string>
     <string name="login_url_helper_text">%1$sをWeb画面でブラウザーで開くときのURL</string>
     <string name="logs_menu_delete">ログを消去</string>
     <string name="logs_menu_delete">ログを消去</string>
@@ -459,6 +494,8 @@
     <string name="no_browser_available">リンクを処理するアプリがありません</string>
     <string name="no_browser_available">リンクを処理するアプリがありません</string>
     <string name="no_calendar_exists">カレンダーがありません</string>
     <string name="no_calendar_exists">カレンダーがありません</string>
     <string name="no_email_app_available">メールアドレスの利用可能なアプリはありません</string>
     <string name="no_email_app_available">メールアドレスの利用可能なアプリはありません</string>
+    <string name="no_items">アイテムがありません</string>
+    <string name="no_map_app_availble">マップを処理するアプリケーションがありません</string>
     <string name="no_mutliple_accounts_allowed">利用できるアカウントは1つだけです</string>
     <string name="no_mutliple_accounts_allowed">利用できるアカウントは1つだけです</string>
     <string name="no_pdf_app_available">PDFを処理するアプリケーションがありません</string>
     <string name="no_pdf_app_available">PDFを処理するアプリケーションがありません</string>
     <string name="no_send_app">選択されたファイルの送信で利用可能なアプリがありません</string>
     <string name="no_send_app">選択されたファイルの送信で利用可能なアプリがありません</string>
@@ -497,11 +534,13 @@
     <string name="pass_code_removed">パスコードを削除しました</string>
     <string name="pass_code_removed">パスコードを削除しました</string>
     <string name="pass_code_stored">パスコードを保存しました</string>
     <string name="pass_code_stored">パスコードを保存しました</string>
     <string name="pass_code_wrong">パスコードが正しくありません</string>
     <string name="pass_code_wrong">パスコードが正しくありません</string>
+    <string name="pdf_password_protected">パスワード保護されたPDFファイルを開くことは出来ません。外部ビューワを使ってください</string>
     <string name="pdf_zoom_tip">ズームするにはページをタップ</string>
     <string name="pdf_zoom_tip">ズームするにはページをタップ</string>
     <string name="permission_allow">許可</string>
     <string name="permission_allow">許可</string>
     <string name="permission_deny">拒否</string>
     <string name="permission_deny">拒否</string>
     <string name="permission_storage_access">ファイルをダウンロードとアップロードする追加の権限が必要です。</string>
     <string name="permission_storage_access">ファイルをダウンロードとアップロードする追加の権限が必要です。</string>
     <string name="picture_set_as_no_app">画像を設定するアプリが見つかりませんでした</string>
     <string name="picture_set_as_no_app">画像を設定するアプリが見つかりませんでした</string>
+    <string name="pin_home">ホームスクリーンにピン留めする</string>
     <string name="placeholder_fileSize">389 KB</string>
     <string name="placeholder_fileSize">389 KB</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_media_time">12:23:45</string>
     <string name="placeholder_media_time">12:23:45</string>
@@ -531,6 +570,7 @@
     <string name="prefs_category_more">もっと見る</string>
     <string name="prefs_category_more">もっと見る</string>
     <string name="prefs_daily_backup_summary">カレンダーと連絡先を毎日バックアップ</string>
     <string name="prefs_daily_backup_summary">カレンダーと連絡先を毎日バックアップ</string>
     <string name="prefs_daily_contact_backup_summary">連絡先のデイリーバックアップ</string>
     <string name="prefs_daily_contact_backup_summary">連絡先のデイリーバックアップ</string>
+    <string name="prefs_e2e_active">End-to-end 暗号化を設定中!</string>
     <string name="prefs_e2e_mnemonic">E2Eニーモニック</string>
     <string name="prefs_e2e_mnemonic">E2Eニーモニック</string>
     <string name="prefs_e2e_no_device_credentials">ニーモニックを表示するには、デバイスクレデンシャルを有効にしてください。</string>
     <string name="prefs_e2e_no_device_credentials">ニーモニックを表示するには、デバイスクレデンシャルを有効にしてください。</string>
     <string name="prefs_enable_media_scan_notifications">メディアのスキャン結果通知を表示する</string>
     <string name="prefs_enable_media_scan_notifications">メディアのスキャン結果通知を表示する</string>
@@ -540,7 +580,9 @@
     <string name="prefs_imprint">インプリント</string>
     <string name="prefs_imprint">インプリント</string>
     <string name="prefs_instant_behaviour_dialogTitle">元のファイルの扱い…</string>
     <string name="prefs_instant_behaviour_dialogTitle">元のファイルの扱い…</string>
     <string name="prefs_instant_behaviour_title">元のファイルになります…</string>
     <string name="prefs_instant_behaviour_title">元のファイルになります…</string>
+    <string name="prefs_instant_upload_path_use_date_subfolders_summary">日付を基にしたサブフォルダーに保存</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">サブフォルダーを利用</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">サブフォルダーを利用</string>
+    <string name="prefs_keys_exist">このクライアントに End-to-End 暗号化を追加</string>
     <string name="prefs_license">ライセンス</string>
     <string name="prefs_license">ライセンス</string>
     <string name="prefs_lock">アプリパスコード</string>
     <string name="prefs_lock">アプリパスコード</string>
     <string name="prefs_lock_device_credentials_enabled">デバイスの資格情報が有効です</string>
     <string name="prefs_lock_device_credentials_enabled">デバイスの資格情報が有効です</string>
@@ -551,6 +593,7 @@
     <string name="prefs_lock_using_passcode">パスコード</string>
     <string name="prefs_lock_using_passcode">パスコード</string>
     <string name="prefs_manage_accounts">アカウント管理</string>
     <string name="prefs_manage_accounts">アカウント管理</string>
     <string name="prefs_recommend">友達にすすめる</string>
     <string name="prefs_recommend">友達にすすめる</string>
+    <string name="prefs_setup_e2e">end-to-end 暗号化を設定</string>
     <string name="prefs_show_hidden_files">隠しファイルを表示</string>
     <string name="prefs_show_hidden_files">隠しファイルを表示</string>
     <string name="prefs_sourcecode">ソースコードを入手</string>
     <string name="prefs_sourcecode">ソースコードを入手</string>
     <string name="prefs_storage_path">データ保存フォルダー</string>
     <string name="prefs_storage_path">データ保存フォルダー</string>
@@ -574,6 +617,7 @@
     <string name="recommend_subject">デバイスで %1$s をお試しください</string>
     <string name="recommend_subject">デバイスで %1$s をお試しください</string>
     <string name="recommend_text">あなたのデバイスで %1$s を使用してください。\nダウンロードはこちらです: %2$s</string>
     <string name="recommend_text">あなたのデバイスで %1$s を使用してください。\nダウンロードはこちらです: %2$s</string>
     <string name="recommend_urls">%1$s または %2$s</string>
     <string name="recommend_urls">%1$s または %2$s</string>
+    <string name="refresh_content">コンテンツを更新</string>
     <string name="reload">再読み込み</string>
     <string name="reload">再読み込み</string>
     <string name="remote">(リモート)</string>
     <string name="remote">(リモート)</string>
     <string name="remote_file_fetch_failed">ファイルが見つかりません!</string>
     <string name="remote_file_fetch_failed">ファイルが見つかりません!</string>
@@ -594,6 +638,7 @@
     <string name="retrieving_file">ファイルを取得中...</string>
     <string name="retrieving_file">ファイルを取得中...</string>
     <string name="richdocuments_failed_to_load_document">ドキュメントのロードに失敗しました。</string>
     <string name="richdocuments_failed_to_load_document">ドキュメントのロードに失敗しました。</string>
     <string name="scanQR_description">QRコードを用いてログイン</string>
     <string name="scanQR_description">QRコードを用いてログイン</string>
+    <string name="scan_page">ページをスキャン</string>
     <string name="screenshot_01_gridView_heading">あなたのデータを保護</string>
     <string name="screenshot_01_gridView_heading">あなたのデータを保護</string>
     <string name="screenshot_01_gridView_subline">自己ホスト型の生産性</string>
     <string name="screenshot_01_gridView_subline">自己ホスト型の生産性</string>
     <string name="screenshot_02_listView_heading">閲覧と共有</string>
     <string name="screenshot_02_listView_heading">閲覧と共有</string>
@@ -608,6 +653,7 @@
     <string name="screenshot_06_davdroid_subline">DAVx5で同期</string>
     <string name="screenshot_06_davdroid_subline">DAVx5で同期</string>
     <string name="search_error">検索結果の取得中にエラーが発生しました</string>
     <string name="search_error">検索結果の取得中にエラーが発生しました</string>
     <string name="select_all">すべて選択</string>
     <string name="select_all">すべて選択</string>
+    <string name="select_media_folder">メディア用のフォルダーの設定</string>
     <string name="select_one_template">テンプレートを選択してください</string>
     <string name="select_one_template">テンプレートを選択してください</string>
     <string name="select_template">テンプレートを選択する</string>
     <string name="select_template">テンプレートを選択する</string>
     <string name="send">送信</string>
     <string name="send">送信</string>
@@ -643,6 +689,7 @@
     <string name="share_password_title">パスワード保護</string>
     <string name="share_password_title">パスワード保護</string>
     <string name="share_permission_can_edit">編集可能</string>
     <string name="share_permission_can_edit">編集可能</string>
     <string name="share_permission_file_drop">ファイルを転送</string>
     <string name="share_permission_file_drop">ファイルを転送</string>
+    <string name="share_permission_secure_file_drop">セキュアなファイルドロップ</string>
     <string name="share_permission_view_only">閲覧のみ</string>
     <string name="share_permission_view_only">閲覧のみ</string>
     <string name="share_permissions">共有権限</string>
     <string name="share_permissions">共有権限</string>
     <string name="share_remote_clarification">%1$s (リモート)</string>
     <string name="share_remote_clarification">%1$s (リモート)</string>
@@ -662,6 +709,8 @@
     <string name="shared_icon_shared_via_link">リンク経由で共有</string>
     <string name="shared_icon_shared_via_link">リンク経由で共有</string>
     <string name="shared_with_you_by">%1$sと共有中</string>
     <string name="shared_with_you_by">%1$sと共有中</string>
     <string name="sharee_add_failed">共有を追加できませんでした</string>
     <string name="sharee_add_failed">共有を追加できませんでした</string>
+    <string name="show_images">写真を表示</string>
+    <string name="show_video">動画を表示</string>
     <string name="signup_with_provider">他のサービスでサインアップ</string>
     <string name="signup_with_provider">他のサービスでサインアップ</string>
     <string name="single_sign_on_request_token" formatted="true">%1$s があなたのNextcloudアカウント %2$s にアクセスできるようにしますか?</string>
     <string name="single_sign_on_request_token" formatted="true">%1$s があなたのNextcloudアカウント %2$s にアクセスできるようにしますか?</string>
     <string name="sort_by">ソート</string>
     <string name="sort_by">ソート</string>
@@ -716,9 +765,13 @@
     <string name="stream_not_possible_headline">内部ストリーミングは不可能</string>
     <string name="stream_not_possible_headline">内部ストリーミングは不可能</string>
     <string name="stream_not_possible_message">代わりにメディアをダウンロードするか、外部アプリを使用してください。</string>
     <string name="stream_not_possible_message">代わりにメディアをダウンロードするか、外部アプリを使用してください。</string>
     <string name="strict_mode">ストリクトモード:HTTP接続が許可されていません!</string>
     <string name="strict_mode">ストリクトモード:HTTP接続が許可されていません!</string>
+    <string name="sub_folder_rule_day">念/月/日</string>
+    <string name="sub_folder_rule_month">年/月</string>
     <string name="sub_folder_rule_year">年</string>
     <string name="sub_folder_rule_year">年</string>
     <string name="subject_shared_with_you">\"%1$s\" があなたに共有されました</string>
     <string name="subject_shared_with_you">\"%1$s\" があなたに共有されました</string>
     <string name="subject_user_shared_with_you">%1$s は \"%2$s\" をあなたと共有しました</string>
     <string name="subject_user_shared_with_you">%1$s は \"%2$s\" をあなたと共有しました</string>
+    <string name="subtitle_photos_only">写真のみ</string>
+    <string name="subtitle_videos_only">動画のみ</string>
     <string name="suggest">提案</string>
     <string name="suggest">提案</string>
     <string name="sync_conflicts_in_favourites_ticker">競合が見つかりました</string>
     <string name="sync_conflicts_in_favourites_ticker">競合が見つかりました</string>
     <string name="sync_current_folder_was_removed">フォルダー %1$s はもう存在しません</string>
     <string name="sync_current_folder_was_removed">フォルダー %1$s はもう存在しません</string>
@@ -752,6 +805,7 @@
     <string name="thumbnail">サムネイル</string>
     <string name="thumbnail">サムネイル</string>
     <string name="thumbnail_for_existing_file_description">既存ファイルのサムネイル</string>
     <string name="thumbnail_for_existing_file_description">既存ファイルのサムネイル</string>
     <string name="thumbnail_for_new_file_desc">新規ファイルのサムネイル</string>
     <string name="thumbnail_for_new_file_desc">新規ファイルのサムネイル</string>
+    <string name="timeout_richDocuments">ローディング中 期待した時間より長くかかっています</string>
     <string name="today">今日</string>
     <string name="today">今日</string>
     <string name="trashbin_activity_title">ゴミ箱</string>
     <string name="trashbin_activity_title">ゴミ箱</string>
     <string name="trashbin_empty_headline">削除されたファイルはありません</string>
     <string name="trashbin_empty_headline">削除されたファイルはありません</string>
@@ -866,6 +920,7 @@
     <string name="whats_new_skip">スキップ</string>
     <string name="whats_new_skip">スキップ</string>
     <string name="whats_new_title">%1$sの新機能</string>
     <string name="whats_new_title">%1$sの新機能</string>
     <string name="whats_your_status">あなたのステータスは?</string>
     <string name="whats_your_status">あなたのステータスは?</string>
+    <string name="widgets_not_available">ウィジェットは %1$s 25 以上でのみ利用可能です </string>
     <string name="widgets_not_available_title">利用できません</string>
     <string name="widgets_not_available_title">利用できません</string>
     <string name="write_email">メールを送信</string>
     <string name="write_email">メールを送信</string>
     <string name="wrong_storage_path">データ保存フォルダーが存在しません!</string>
     <string name="wrong_storage_path">データ保存フォルダーが存在しません!</string>
@@ -888,6 +943,18 @@
     <plurals name="found_n_duplicates">
     <plurals name="found_n_duplicates">
         <item quantity="other">重複する応募が%d件見つかりました。</item>
         <item quantity="other">重複する応募が%d件見つかりました。</item>
     </plurals>
     </plurals>
+    <plurals name="export_successful">
+        <item quantity="other">エクスポートされた %d ファイル</item>
+    </plurals>
+    <plurals name="export_failed">
+        <item quantity="other">%dファイルのエクスポートに失敗しました</item>
+    </plurals>
+    <plurals name="export_partially_failed">
+        <item quantity="other">エラーのため、%d ファイルはエクスポートされ、他のファイルはスキップされました</item>
+    </plurals>
+    <plurals name="export_start">
+        <item quantity="other">%dファイルがエクスポートされます。詳細は通知を確認してください。</item>
+    </plurals>
     <plurals name="file_list__footer__folder">
     <plurals name="file_list__footer__folder">
         <item quantity="other">%1$d フォルダ</item>
         <item quantity="other">%1$d フォルダ</item>
     </plurals>
     </plurals>

+ 1 - 1
app/src/main/res/values-ko/strings.xml

@@ -420,7 +420,7 @@
     <string name="last_backup">마지막 백업: %1$s</string>
     <string name="last_backup">마지막 백업: %1$s</string>
     <string name="link">링크</string>
     <string name="link">링크</string>
     <string name="link_name">링크 이름</string>
     <string name="link_name">링크 이름</string>
-    <string name="link_share_allow_upload_and_editing">업로드 및 편집 허용</string>
+    <string name="link_share_allow_upload_and_editing">업로드와 수정 허용</string>
     <string name="link_share_editing">글 수정</string>
     <string name="link_share_editing">글 수정</string>
     <string name="link_share_file_drop">파일 보관소 (업로드만 허용)</string>
     <string name="link_share_file_drop">파일 보관소 (업로드만 허용)</string>
     <string name="link_share_view_only">읽기 전용</string>
     <string name="link_share_view_only">읽기 전용</string>

+ 74 - 21
app/src/main/res/values-pt-rPT/strings.xml

@@ -125,7 +125,7 @@
     <string name="common_switch_to_account">Mudar para a conta</string>
     <string name="common_switch_to_account">Mudar para a conta</string>
     <string name="common_yes">Sim</string>
     <string name="common_yes">Sim</string>
     <string name="community_beta_headline">Teste a versão de desenvolvimento</string>
     <string name="community_beta_headline">Teste a versão de desenvolvimento</string>
-    <string name="community_beta_text">Inclui todas as futuras e mais recentes funcionalidades. Falhas/erros podem ocorrer, se e quando tal acontecer, por favor, reporte as suas descobertas.</string>
+    <string name="community_beta_text">Isto inclui todas as futuras e mais recentes funcionalidades. Podem ocorrer falhas/erros, se e quando tal acontecer, por favor, reporte as suas descobertas.</string>
     <string name="community_contribute_forum_forum">fórum</string>
     <string name="community_contribute_forum_forum">fórum</string>
     <string name="community_contribute_forum_text">Ajude outros em</string>
     <string name="community_contribute_forum_text">Ajude outros em</string>
     <string name="community_contribute_github_text">Reveja, emende e escreva o código, consulte %1$s para detalhes.</string>
     <string name="community_contribute_github_text">Reveja, emende e escreva o código, consulte %1$s para detalhes.</string>
@@ -137,12 +137,12 @@
     <string name="community_rc_fdroid">Obter versão candidata a lançamento a partir da aplicação F-Droid</string>
     <string name="community_rc_fdroid">Obter versão candidata a lançamento a partir da aplicação F-Droid</string>
     <string name="community_rc_play_store">Obter versão candidata a lançamento a partir da loja Google Play</string>
     <string name="community_rc_play_store">Obter versão candidata a lançamento a partir da loja Google Play</string>
     <string name="community_release_candidate_headline">Versão candidata a lançamento</string>
     <string name="community_release_candidate_headline">Versão candidata a lançamento</string>
-    <string name="community_release_candidate_text">A versão candidata a lançamento (RC) é um snapshot da próxima versão e é espectável que seja estável. Através do teste da sua configuração individual pode ajudar-nos a assegurá-lo. Para testar, inscreva-se na loja Play ou verifique a secção \"Version\" do F-Droid.</string>
+    <string name="community_release_candidate_text">A versão candidata de lançamento (RC) é um \'\'snapshot\'\' da próxima versão e é expetável que seja estável. Se testar a sua configuração individual pode ajudar-nos a assegurá-lo. Para testar, inscreva-se na loja Play ou consulte a secção \"Version\" do F-Droid.</string>
     <string name="community_testing_bug_text">Encontrou um erro? Ocorrências estranhas?</string>
     <string name="community_testing_bug_text">Encontrou um erro? Ocorrências estranhas?</string>
     <string name="community_testing_headline">Ajude-nos, testando</string>
     <string name="community_testing_headline">Ajude-nos, testando</string>
     <string name="community_testing_report_text">Reportar um problema no Github</string>
     <string name="community_testing_report_text">Reportar um problema no Github</string>
     <string name="configure_new_media_folder_detection_notifications">Configure</string>
     <string name="configure_new_media_folder_detection_notifications">Configure</string>
-    <string name="confirm_removal">Remove localmente a criptografia</string>
+    <string name="confirm_removal">Remove encriptação local</string>
     <string name="confirmation_remove_file_alert">Deseja realmente apagar %1$s?</string>
     <string name="confirmation_remove_file_alert">Deseja realmente apagar %1$s?</string>
     <string name="confirmation_remove_files_alert">Quer realmente remover os itens seleccionados?</string>
     <string name="confirmation_remove_files_alert">Quer realmente remover os itens seleccionados?</string>
     <string name="confirmation_remove_folder_alert">Deseja realmente apagar %1$s e o seu conteúdo?</string>
     <string name="confirmation_remove_folder_alert">Deseja realmente apagar %1$s e o seu conteúdo?</string>
@@ -152,7 +152,7 @@
     <string name="conflict_file_headline">Ficheiro em conflito %1$s</string>
     <string name="conflict_file_headline">Ficheiro em conflito %1$s</string>
     <string name="conflict_local_file">Ficheiro local</string>
     <string name="conflict_local_file">Ficheiro local</string>
     <string name="conflict_message_description">Se selecionou ambas as versões, o ficheiro local terá um número acrescentado ao seu nome.</string>
     <string name="conflict_message_description">Se selecionou ambas as versões, o ficheiro local terá um número acrescentado ao seu nome.</string>
-    <string name="conflict_server_file">Arquivo do servidor</string>
+    <string name="conflict_server_file">Ficheiro do servidor</string>
     <string name="contact_backup_title">Cópia de segurança dos contactos</string>
     <string name="contact_backup_title">Cópia de segurança dos contactos</string>
     <string name="contactlist_item_icon">Ícone de utilizador de lista de contactos</string>
     <string name="contactlist_item_icon">Ícone de utilizador de lista de contactos</string>
     <string name="contactlist_no_permission">Sem permissão concedida, nada para importar.</string>
     <string name="contactlist_no_permission">Sem permissão concedida, nada para importar.</string>
@@ -190,7 +190,7 @@
     <string name="delete_entries">Apagar entradas</string>
     <string name="delete_entries">Apagar entradas</string>
     <string name="delete_link">Eliminar hiperligação</string>
     <string name="delete_link">Eliminar hiperligação</string>
     <string name="deselect_all">Desseleccionado tudo</string>
     <string name="deselect_all">Desseleccionado tudo</string>
-    <string name="destination_filename">Nome do arquivo de destino</string>
+    <string name="destination_filename">Nome do ficheiro de destino</string>
     <string name="dev_version_new_version_available">Nova versão disponível</string>
     <string name="dev_version_new_version_available">Nova versão disponível</string>
     <string name="dev_version_no_information_available">Nenhuma informação disponível.</string>
     <string name="dev_version_no_information_available">Nenhuma informação disponível.</string>
     <string name="dev_version_no_new_version_available">Nenhuma nova versão disponível</string>
     <string name="dev_version_no_new_version_available">Nenhuma nova versão disponível</string>
@@ -202,12 +202,16 @@
     <string name="disable_new_media_folder_detection_notifications">Desativar</string>
     <string name="disable_new_media_folder_detection_notifications">Desativar</string>
     <string name="dismiss">Dispensar</string>
     <string name="dismiss">Dispensar</string>
     <string name="dismiss_notification_description">Dispensar notificação</string>
     <string name="dismiss_notification_description">Dispensar notificação</string>
-    <string name="displays_mnemonic">Apresenta a sua frase-chave de 12 palavras</string>
+    <string name="displays_mnemonic">Exibe a sua frase-chave de 12 palavras</string>
     <string name="dnd">Não incomodar</string>
     <string name="dnd">Não incomodar</string>
     <string name="document_scan_export_dialog_images">Múltiplas imagens</string>
     <string name="document_scan_export_dialog_images">Múltiplas imagens</string>
+    <string name="document_scan_export_dialog_pdf">Ficheiro PDF</string>
+    <string name="document_scan_export_dialog_title">Escolher tipo de exportação</string>
+    <string name="document_scan_pdf_generation_failed">Geração de PDF falhou</string>
+    <string name="document_scan_pdf_generation_in_progress">A gerar PDF...</string>
     <string name="done">Concluído</string>
     <string name="done">Concluído</string>
     <string name="dontClear">Não limpar</string>
     <string name="dontClear">Não limpar</string>
-    <string name="download_cannot_create_file">Não é possível criar ficheiro local</string>
+    <string name="download_cannot_create_file">Não é possível criar o ficheiro local</string>
     <string name="download_latest_dev_version">Transferir a última versão de desenvolvimento</string>
     <string name="download_latest_dev_version">Transferir a última versão de desenvolvimento</string>
     <string name="downloader_download_failed_content">Não foi possível transferir %1$s</string>
     <string name="downloader_download_failed_content">Não foi possível transferir %1$s</string>
     <string name="downloader_download_failed_credentials_error">A transferência falhou, inicie novamente a sessão</string>
     <string name="downloader_download_failed_credentials_error">A transferência falhou, inicie novamente a sessão</string>
@@ -238,7 +242,11 @@
     <string name="drawer_quota_unlimited">%1$s utilizado</string>
     <string name="drawer_quota_unlimited">%1$s utilizado</string>
     <string name="drawer_synced_folders">Carregamento automático</string>
     <string name="drawer_synced_folders">Carregamento automático</string>
     <string name="ecosystem_apps_display_more">Mais</string>
     <string name="ecosystem_apps_display_more">Mais</string>
+    <string name="ecosystem_apps_display_notes">Notas</string>
     <string name="ecosystem_apps_display_talk">Falar</string>
     <string name="ecosystem_apps_display_talk">Falar</string>
+    <string name="ecosystem_apps_more">Mais Aplicações Nextcloud</string>
+    <string name="ecosystem_apps_notes">Nextcloud Notes</string>
+    <string name="ecosystem_apps_talk">Nextcloud Talk</string>
     <string name="encrypted">Definir como encriptado</string>
     <string name="encrypted">Definir como encriptado</string>
     <string name="end_to_end_encryption_confirm_button">Definir encriptação</string>
     <string name="end_to_end_encryption_confirm_button">Definir encriptação</string>
     <string name="end_to_end_encryption_decrypting">Decryption…</string>
     <string name="end_to_end_encryption_decrypting">Decryption…</string>
@@ -255,15 +263,19 @@
     <string name="end_to_end_encryption_title">Definir encriptação</string>
     <string name="end_to_end_encryption_title">Definir encriptação</string>
     <string name="end_to_end_encryption_unsuccessful">Não foi possível guardar as chaves, por favor, tente novamente.</string>
     <string name="end_to_end_encryption_unsuccessful">Não foi possível guardar as chaves, por favor, tente novamente.</string>
     <string name="end_to_end_encryption_wrong_password">Erro ao encriptar. Palavra-passe errada?</string>
     <string name="end_to_end_encryption_wrong_password">Erro ao encriptar. Palavra-passe errada?</string>
+    <string name="enter_destination_filename">Inserir nome do ficheiro de destino</string>
     <string name="enter_filename">Por favor, introduza um nome para o ficheiro</string>
     <string name="enter_filename">Por favor, introduza um nome para o ficheiro</string>
     <string name="error__upload__local_file_not_copied">Não foi possível copiar %1$s para a pasta local %2$s</string>
     <string name="error__upload__local_file_not_copied">Não foi possível copiar %1$s para a pasta local %2$s</string>
     <string name="error_cant_bind_to_operations_service">Erro crítico: impossível concluir a operação</string>
     <string name="error_cant_bind_to_operations_service">Erro crítico: impossível concluir a operação</string>
     <string name="error_choosing_date">Erro ao escolher a data</string>
     <string name="error_choosing_date">Erro ao escolher a data</string>
     <string name="error_comment_file">Erro ao comentar ficheiro</string>
     <string name="error_comment_file">Erro ao comentar ficheiro</string>
     <string name="error_crash_title">%1$s crachou</string>
     <string name="error_crash_title">%1$s crachou</string>
+    <string name="error_creating_file_from_template">Erro ao criar o ficheiro com o modelo</string>
+    <string name="error_file_actions">Erro ao mostrar as ações do ficheiro</string>
     <string name="error_report_issue_action">Relatório</string>
     <string name="error_report_issue_action">Relatório</string>
     <string name="error_retrieving_file">Erro ao obter o ficheiro</string>
     <string name="error_retrieving_file">Erro ao obter o ficheiro</string>
     <string name="error_retrieving_templates">Erro ao obter modelos</string>
     <string name="error_retrieving_templates">Erro ao obter modelos</string>
+    <string name="error_showing_encryption_dialog">Erro ao mostrar a janela da configuração de encriptação!</string>
     <string name="error_starting_direct_camera_upload">Erro ao iniciar câmara</string>
     <string name="error_starting_direct_camera_upload">Erro ao iniciar câmara</string>
     <string name="etm_accounts">Contas</string>
     <string name="etm_accounts">Contas</string>
     <string name="etm_background_job_name">Nome do Trabalho</string>
     <string name="etm_background_job_name">Nome do Trabalho</string>
@@ -289,11 +301,12 @@
     <string name="etm_transfer_type_upload">Enviar</string>
     <string name="etm_transfer_type_upload">Enviar</string>
     <string name="fab_label">Adicionar ou enviar</string>
     <string name="fab_label">Adicionar ou enviar</string>
     <string name="failed_to_download">Falhou a passagem do ficheiro ao gestor de transferências</string>
     <string name="failed_to_download">Falhou a passagem do ficheiro ao gestor de transferências</string>
-    <string name="failed_to_print">Falhou a impressão do ficheiro</string>
+    <string name="failed_to_print">Não foi possível imprimir o ficheiro</string>
     <string name="failed_to_start_editor">Falha ao iniciar o editor</string>
     <string name="failed_to_start_editor">Falha ao iniciar o editor</string>
     <string name="failed_update_ui">Falha ao atualizar a IU</string>
     <string name="failed_update_ui">Falha ao atualizar a IU</string>
     <string name="favorite">Adicionar aos favoritos</string>
     <string name="favorite">Adicionar aos favoritos</string>
     <string name="favorite_icon">Favorito</string>
     <string name="favorite_icon">Favorito</string>
+    <string name="file_already_exists">O nome do ficheiro já existe</string>
     <string name="file_delete">Apagar</string>
     <string name="file_delete">Apagar</string>
     <string name="file_detail_activity_error">Erro ao obter as atividades para o ficheiro</string>
     <string name="file_detail_activity_error">Erro ao obter as atividades para o ficheiro</string>
     <string name="file_details_no_content">Falha ao carregar detalhes</string>
     <string name="file_details_no_content">Falha ao carregar detalhes</string>
@@ -307,10 +320,10 @@
     <string name="file_list_empty_headline_search">Sem resultados nesta pasta</string>
     <string name="file_list_empty_headline_search">Sem resultados nesta pasta</string>
     <string name="file_list_empty_headline_server_search">Sem resultados</string>
     <string name="file_list_empty_headline_server_search">Sem resultados</string>
     <string name="file_list_empty_moving">Não está aqui nada. Pode adicionar uma pasta.</string>
     <string name="file_list_empty_moving">Não está aqui nada. Pode adicionar uma pasta.</string>
-    <string name="file_list_empty_on_device">Ficheiros e pastas que foram descarregados aparecem aqui.</string>
+    <string name="file_list_empty_on_device">Os ficheiros e as pastas que foram transferidos serão mostrados aqui.</string>
     <string name="file_list_empty_recently_modified">Não foi encontrado nenhum arquivo modificado nos últimos 7 dias</string>
     <string name="file_list_empty_recently_modified">Não foi encontrado nenhum arquivo modificado nos últimos 7 dias</string>
     <string name="file_list_empty_search">Poderá estar numa pasta diferente?</string>
     <string name="file_list_empty_search">Poderá estar numa pasta diferente?</string>
-    <string name="file_list_empty_shared">Ficheiros e pastas que partilhou aparecem aqui.</string>
+    <string name="file_list_empty_shared">Os ficheiros e as pastas que partilhou serão mostrados aqui.</string>
     <string name="file_list_empty_shared_headline">Ainda sem partilhas</string>
     <string name="file_list_empty_shared_headline">Ainda sem partilhas</string>
     <string name="file_list_empty_unified_search_no_results">Nenhum resultado encontrado para a sua consulta</string>
     <string name="file_list_empty_unified_search_no_results">Nenhum resultado encontrado para a sua consulta</string>
     <string name="file_list_folder">pasta</string>
     <string name="file_list_folder">pasta</string>
@@ -318,6 +331,7 @@
     <string name="file_list_no_app_for_file_type">Nenhuma aplicação para usar este tipo de ficheiro.</string>
     <string name="file_list_no_app_for_file_type">Nenhuma aplicação para usar este tipo de ficheiro.</string>
     <string name="file_list_seconds_ago">há segundos</string>
     <string name="file_list_seconds_ago">há segundos</string>
     <string name="file_management_permission">Permissões necessárias</string>
     <string name="file_management_permission">Permissões necessárias</string>
+    <string name="file_management_permission_optional">Permissões de armazenamento</string>
     <string name="file_management_permission_text">%1$s precisa de permissões de gestão de ficheiros para carregar ficheiros. Pode escolher acesso total a todos os ficheiros ou acesso só de leitura a fotografias e vídeos.</string>
     <string name="file_management_permission_text">%1$s precisa de permissões de gestão de ficheiros para carregar ficheiros. Pode escolher acesso total a todos os ficheiros ou acesso só de leitura a fotografias e vídeos.</string>
     <string name="file_migration_checking_destination">Verificando destino…</string>
     <string name="file_migration_checking_destination">Verificando destino…</string>
     <string name="file_migration_cleaning">Limpando…</string>
     <string name="file_migration_cleaning">Limpando…</string>
@@ -383,11 +397,11 @@
     <string name="host_not_available">Servidor não disponível</string>
     <string name="host_not_available">Servidor não disponível</string>
     <string name="host_your_own_server">Hospede o seu próprio servidor</string>
     <string name="host_your_own_server">Hospede o seu próprio servidor</string>
     <string name="icon_for_empty_list">Ícone para lista vazia</string>
     <string name="icon_for_empty_list">Ícone para lista vazia</string>
-    <string name="icon_of_dashboard_widget">Ícone do widget do painel de controlo</string>
-    <string name="icon_of_widget_entry">Ícone do widget da entrada </string>
+    <string name="icon_of_dashboard_widget">Ícone do \'\'widget\'\' do painel de controlo</string>
+    <string name="icon_of_widget_entry">Ícone do \'\'widget\'\' da entrada </string>
     <string name="image_editor_file_edited_suffix">editado</string>
     <string name="image_editor_file_edited_suffix">editado</string>
-    <string name="image_editor_flip_horizontal">Virar horizontalmente</string>
-    <string name="image_editor_flip_vertical">Virar verticalmente</string>
+    <string name="image_editor_flip_horizontal">Inverter horizontalmente</string>
+    <string name="image_editor_flip_vertical">Inverter verticalmente</string>
     <string name="image_editor_rotate_ccw">Rodar no sentido anti-horário</string>
     <string name="image_editor_rotate_ccw">Rodar no sentido anti-horário</string>
     <string name="image_editor_rotate_cw">Rodar no sentido horário</string>
     <string name="image_editor_rotate_cw">Rodar no sentido horário</string>
     <string name="image_editor_unable_to_edit_image">Não é possível editar a imagem.</string>
     <string name="image_editor_unable_to_edit_image">Não é possível editar a imagem.</string>
@@ -404,7 +418,7 @@
     <string name="instant_upload_path">/Envio Instantâneo </string>
     <string name="instant_upload_path">/Envio Instantâneo </string>
     <string name="invalid_url">URL inválido</string>
     <string name="invalid_url">URL inválido</string>
     <string name="invisible">Invisível </string>
     <string name="invisible">Invisível </string>
-    <string name="label_empty">Nome não pode ficar em branco</string>
+    <string name="label_empty">O nome não pode ficar em branco</string>
     <string name="last_backup">Última cópia de segurança: %1$s</string>
     <string name="last_backup">Última cópia de segurança: %1$s</string>
     <string name="link">Hiperligação</string>
     <string name="link">Hiperligação</string>
     <string name="link_name">Nome da hiperligação</string>
     <string name="link_name">Nome da hiperligação</string>
@@ -418,9 +432,14 @@
     <string name="local_file_not_found_message">Ficheiros não encontrados no sistema de ficheiros local</string>
     <string name="local_file_not_found_message">Ficheiros não encontrados no sistema de ficheiros local</string>
     <string name="local_folder_friendly_path">%1$s/%2$s</string>
     <string name="local_folder_friendly_path">%1$s/%2$s</string>
     <string name="local_folder_list_empty">Não existem mais pastas.</string>
     <string name="local_folder_list_empty">Não existem mais pastas.</string>
+    <string name="locate_folder">Localizar pasta</string>
+    <string name="lock_expiration_info">Expira: %1$s</string>
     <string name="lock_file">Bloquear ficheiro </string>
     <string name="lock_file">Bloquear ficheiro </string>
+    <string name="locked_by">Bloqueado por %1$s</string>
+    <string name="locked_by_app">Bloqueado por aplicação %1$s</string>
     <string name="log_send_mail_subject">%1$s registos de aplicação Android</string>
     <string name="log_send_mail_subject">%1$s registos de aplicação Android</string>
     <string name="log_send_no_mail_app">Não foi encontrada nenhuma aplicação para o envio de registos. Por favor, Instale um cliente de correio eletrónico.</string>
     <string name="log_send_no_mail_app">Não foi encontrada nenhuma aplicação para o envio de registos. Por favor, Instale um cliente de correio eletrónico.</string>
+    <string name="logged_in_as">Autenticado como %1$s</string>
     <string name="login">Iniciar Sessão</string>
     <string name="login">Iniciar Sessão</string>
     <string name="login_url_helper_text">A hiperligação para a sua interface da Web %1$s quando a abre no seu navegador.</string>
     <string name="login_url_helper_text">A hiperligação para a sua interface da Web %1$s quando a abre no seu navegador.</string>
     <string name="logs_menu_delete">Eliminar registos</string>
     <string name="logs_menu_delete">Eliminar registos</string>
@@ -467,10 +486,14 @@
     <string name="new_media_folder_videos">vídeo</string>
     <string name="new_media_folder_videos">vídeo</string>
     <string name="new_notification">Nova notificação</string>
     <string name="new_notification">Nova notificação</string>
     <string name="new_version_was_created">Uma nova versão foi criada</string>
     <string name="new_version_was_created">Uma nova versão foi criada</string>
+    <string name="no_actions">Sem ações para este utilizador</string>
     <string name="no_browser_available">Nenhuma aplicação disponível para lidar com links</string>
     <string name="no_browser_available">Nenhuma aplicação disponível para lidar com links</string>
     <string name="no_calendar_exists">O calendário não existe</string>
     <string name="no_calendar_exists">O calendário não existe</string>
+    <string name="no_items">Sem itens</string>
     <string name="no_mutliple_accounts_allowed">Só é permitida uma conta.</string>
     <string name="no_mutliple_accounts_allowed">Só é permitida uma conta.</string>
     <string name="no_pdf_app_available">Nenhuma aplicação disponível para lidar com PDF</string>
     <string name="no_pdf_app_available">Nenhuma aplicação disponível para lidar com PDF</string>
+    <string name="no_send_app">Nenhuma aplicação disponível para enviar os ficheiros selecionados</string>
+    <string name="no_share_permission_selected">Por favor, selecione pelo menos uma permissão para partilhar.</string>
     <string name="note_confirm">Enviar</string>
     <string name="note_confirm">Enviar</string>
     <string name="note_could_not_sent">Não foi possível enviar a nota</string>
     <string name="note_could_not_sent">Não foi possível enviar a nota</string>
     <string name="note_icon_hint">Ícone de nota</string>
     <string name="note_icon_hint">Ícone de nota</string>
@@ -509,6 +532,8 @@
     <string name="permission_deny">Negar</string>
     <string name="permission_deny">Negar</string>
     <string name="permission_storage_access">Permissões adicionais são necessárias para enviar e transferir ficheiros.</string>
     <string name="permission_storage_access">Permissões adicionais são necessárias para enviar e transferir ficheiros.</string>
     <string name="picture_set_as_no_app">Nenhuma aplicação encontrada para definir a imagem</string>
     <string name="picture_set_as_no_app">Nenhuma aplicação encontrada para definir a imagem</string>
+    <string name="pin_home">Afixar no ecrã Início</string>
+    <string name="pin_shortcut_label">Abrir %1$s</string>
     <string name="placeholder_fileSize">389 KB</string>
     <string name="placeholder_fileSize">389 KB</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_media_time">12:23:45</string>
     <string name="placeholder_media_time">12:23:45</string>
@@ -548,7 +573,7 @@
     <string name="prefs_imprint">Informação</string>
     <string name="prefs_imprint">Informação</string>
     <string name="prefs_instant_behaviour_dialogTitle">O ficheiro original será…</string>
     <string name="prefs_instant_behaviour_dialogTitle">O ficheiro original será…</string>
     <string name="prefs_instant_behaviour_title">O ficheiro original será…</string>
     <string name="prefs_instant_behaviour_title">O ficheiro original será…</string>
-    <string name="prefs_instant_upload_path_use_date_subfolders_summary">Armazenar em subpastas com base na data</string>
+    <string name="prefs_instant_upload_path_use_date_subfolders_summary">Guardar nas subpastas com base na data</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Usar Subpastas</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Usar Subpastas</string>
     <string name="prefs_instant_upload_subfolder_rule_title">Opções de subpasta</string>
     <string name="prefs_instant_upload_subfolder_rule_title">Opções de subpasta</string>
     <string name="prefs_license">Licença</string>
     <string name="prefs_license">Licença</string>
@@ -561,6 +586,7 @@
     <string name="prefs_lock_using_passcode">Código</string>
     <string name="prefs_lock_using_passcode">Código</string>
     <string name="prefs_manage_accounts">Gerir contas</string>
     <string name="prefs_manage_accounts">Gerir contas</string>
     <string name="prefs_recommend">Recomendar a um amigo</string>
     <string name="prefs_recommend">Recomendar a um amigo</string>
+    <string name="prefs_remove_e2e">Remover encriptação localmente</string>
     <string name="prefs_setup_e2e">Configurar a encriptação ponta-a-ponta</string>
     <string name="prefs_setup_e2e">Configurar a encriptação ponta-a-ponta</string>
     <string name="prefs_show_ecosystem_apps">Mostrar alternador de aplicações</string>
     <string name="prefs_show_ecosystem_apps">Mostrar alternador de aplicações</string>
     <string name="prefs_show_ecosystem_apps_summary">Sugestões de aplicações Nextcloud no cabeçalho da navegação</string>
     <string name="prefs_show_ecosystem_apps_summary">Sugestões de aplicações Nextcloud no cabeçalho da navegação</string>
@@ -639,6 +665,7 @@
     <string name="set_status_message">Defina a mensagem de estado</string>
     <string name="set_status_message">Defina a mensagem de estado</string>
     <string name="setup_e2e">Durante a configuração da encriptação ponta-a-ponta, receberá uma mnemónica aleatória de 12 palavras, de que necessitará para abrir os seus ficheiros noutros dispositivos. Ela só será guardada neste dispositivo e pode ser mostrada novamente neste ecrã. Por favor, anote-a num local seguro!</string>
     <string name="setup_e2e">Durante a configuração da encriptação ponta-a-ponta, receberá uma mnemónica aleatória de 12 palavras, de que necessitará para abrir os seus ficheiros noutros dispositivos. Ela só será guardada neste dispositivo e pode ser mostrada novamente neste ecrã. Por favor, anote-a num local seguro!</string>
     <string name="share">Partilhar</string>
     <string name="share">Partilhar</string>
+    <string name="share_copy_link">Hiperligação de Partilhar e Copiar</string>
     <string name="share_dialog_title">Partilha</string>
     <string name="share_dialog_title">Partilha</string>
     <string name="share_expiration_date_format">%1$s</string>
     <string name="share_expiration_date_format">%1$s</string>
     <string name="share_expiration_date_label">Expira %1$s</string>
     <string name="share_expiration_date_label">Expira %1$s</string>
@@ -679,6 +706,8 @@
     <string name="shared_icon_shared_via_link">Partilhado via hiperligação</string>
     <string name="shared_icon_shared_via_link">Partilhado via hiperligação</string>
     <string name="shared_with_you_by">Partilhado consigo por %1$s</string>
     <string name="shared_with_you_by">Partilhado consigo por %1$s</string>
     <string name="sharee_add_failed">Adição de destinatário da partilha falhou</string>
     <string name="sharee_add_failed">Adição de destinatário da partilha falhou</string>
+    <string name="show_images">Mostrar fotografias</string>
+    <string name="show_video">Mostrar vídeos</string>
     <string name="signup_with_provider">Registar com fornecedor</string>
     <string name="signup_with_provider">Registar com fornecedor</string>
     <string name="single_sign_on_request_token" formatted="true">Permitir que %1$s aceda à sua conta Nextcloud %2$s?</string>
     <string name="single_sign_on_request_token" formatted="true">Permitir que %1$s aceda à sua conta Nextcloud %2$s?</string>
     <string name="sort_by">Ordenar por</string>
     <string name="sort_by">Ordenar por</string>
@@ -731,9 +760,13 @@
     <string name="stream">Transmita com…</string>
     <string name="stream">Transmita com…</string>
     <string name="stream_not_possible_headline">Transmissão interna não é possível</string>
     <string name="stream_not_possible_headline">Transmissão interna não é possível</string>
     <string name="stream_not_possible_message">Em vez disso, por favor, transfira mediateca ou utilize uma aplicação externa.</string>
     <string name="stream_not_possible_message">Em vez disso, por favor, transfira mediateca ou utilize uma aplicação externa.</string>
+    <string name="sub_folder_rule_day">Ano/Mês/Dia</string>
+    <string name="sub_folder_rule_month">Ano/Mês</string>
+    <string name="sub_folder_rule_year">Ano</string>
     <string name="subject_shared_with_you">\"%1$s\" foi partilhado consigo</string>
     <string name="subject_shared_with_you">\"%1$s\" foi partilhado consigo</string>
     <string name="subject_user_shared_with_you">%1$s partilhou \"%2$s\" consigo</string>
     <string name="subject_user_shared_with_you">%1$s partilhou \"%2$s\" consigo</string>
-    <string name="subtitle_photos_videos">Fotos &amp; videos</string>
+    <string name="subtitle_photos_only">Apenas fotografias</string>
+    <string name="subtitle_photos_videos">Fotografias e vídeos</string>
     <string name="subtitle_videos_only">Apenas vídeos</string>
     <string name="subtitle_videos_only">Apenas vídeos</string>
     <string name="suggest">Sugerir </string>
     <string name="suggest">Sugerir </string>
     <string name="sync_conflicts_in_favourites_ticker">Encontrados conflitos</string>
     <string name="sync_conflicts_in_favourites_ticker">Encontrados conflitos</string>
@@ -784,12 +817,12 @@ Aproveite o novo e melhorado envio automático.</string>
     <string name="unset_encrypted">Encriptação não definida</string>
     <string name="unset_encrypted">Encriptação não definida</string>
     <string name="unset_favorite">Remover dos favoritos</string>
     <string name="unset_favorite">Remover dos favoritos</string>
     <string name="unshare_link_file_error">Ocorreu um erro enquanto tentava remover a partilha deste ficheiro ou pasta. </string>
     <string name="unshare_link_file_error">Ocorreu um erro enquanto tentava remover a partilha deste ficheiro ou pasta. </string>
-    <string name="unshare_link_file_no_exist">Impossível eliminar a partilha. Verifique se o ficheiro existe.</string>
+    <string name="unshare_link_file_no_exist">Não é possível remover a partilha. Por favor, verifique se o ficheiro existe.</string>
     <string name="unshare_link_forbidden_permissions">para cancelar a partilha deste ficheiro</string>
     <string name="unshare_link_forbidden_permissions">para cancelar a partilha deste ficheiro</string>
     <string name="unsharing_failed">Cancelamento da partilha falhou</string>
     <string name="unsharing_failed">Cancelamento da partilha falhou</string>
     <string name="untrusted_domain">Acesso através de domínio não confiável. Por favor, verifique a documentação para mais informações.</string>
     <string name="untrusted_domain">Acesso através de domínio não confiável. Por favor, verifique a documentação para mais informações.</string>
-    <string name="update_link_file_error">Erro ao tentar modificar a partilha.</string>
-    <string name="update_link_file_no_exist">Impossível modificar. Verifique se o ficheiro existe.</string>
+    <string name="update_link_file_error">Ocorreu um erro enquanto tentava atualizar a partilha.</string>
+    <string name="update_link_file_no_exist">Não é possível atualizar. Por favor, verifique se o ficheiro existe.</string>
     <string name="update_link_forbidden_permissions">para atualizar esta partilha</string>
     <string name="update_link_forbidden_permissions">para atualizar esta partilha</string>
     <string name="updating_share_failed">Actualização da partilha falhou</string>
     <string name="updating_share_failed">Actualização da partilha falhou</string>
     <string name="upload_cannot_create_file">Não é possível criar ficheiro local</string>
     <string name="upload_cannot_create_file">Não é possível criar ficheiro local</string>
@@ -828,7 +861,7 @@ Aproveite o novo e melhorado envio automático.</string>
     <string name="uploader_top_message">Escolha uma pasta para envio</string>
     <string name="uploader_top_message">Escolha uma pasta para envio</string>
     <string name="uploader_upload_failed_content_single">Não foi possível enviar %1$s</string>
     <string name="uploader_upload_failed_content_single">Não foi possível enviar %1$s</string>
     <string name="uploader_upload_failed_credentials_error">O envio falhou, inicie novamente a sessão</string>
     <string name="uploader_upload_failed_credentials_error">O envio falhou, inicie novamente a sessão</string>
-    <string name="uploader_upload_failed_sync_conflict_error">Exite um conflito com o ficheiro enviado</string>
+    <string name="uploader_upload_failed_sync_conflict_error">Conflito de envio do ficheiro</string>
     <string name="uploader_upload_failed_sync_conflict_error_content">Escolha qual das versões manter %1$s</string>
     <string name="uploader_upload_failed_sync_conflict_error_content">Escolha qual das versões manter %1$s</string>
     <string name="uploader_upload_failed_ticker">Envio falhou</string>
     <string name="uploader_upload_failed_ticker">Envio falhou</string>
     <string name="uploader_upload_files_behaviour">Opção de envio:</string>
     <string name="uploader_upload_files_behaviour">Opção de envio:</string>
@@ -900,6 +933,26 @@ Aproveite o novo e melhorado envio automático.</string>
         <item quantity="many">Falha ao copiar %1$d ficheiros da pasta %2$s para</item>
         <item quantity="many">Falha ao copiar %1$d ficheiros da pasta %2$s para</item>
         <item quantity="other">Falha ao copiar %1$d ficheiros da pasta %2$s para</item>
         <item quantity="other">Falha ao copiar %1$d ficheiros da pasta %2$s para</item>
     </plurals>
     </plurals>
+    <plurals name="processed_n_entries">
+        <item quantity="one">Processada %d entrada.</item>
+        <item quantity="many">Processadas %dentradas.</item>
+        <item quantity="other">Processadas %d entradas.</item>
+    </plurals>
+    <plurals name="found_n_duplicates">
+        <item quantity="one">Encontrada %d entrada duplicada.</item>
+        <item quantity="many">Encontradas %d entradas duplicadas.</item>
+        <item quantity="other">Encontradas %d entradas duplicadas.</item>
+    </plurals>
+    <plurals name="export_successful">
+        <item quantity="one">Exportado %d ficheiro</item>
+        <item quantity="many">Exportados %d ficheiros</item>
+        <item quantity="other">Exportados %d ficheiros</item>
+    </plurals>
+    <plurals name="export_failed">
+        <item quantity="one">Falhou exportação de %d ficheiro</item>
+        <item quantity="many">Falhou exportação de %d ficheiros</item>
+        <item quantity="other">Falhou exportação de %d ficheiros</item>
+    </plurals>
     <plurals name="file_list__footer__folder">
     <plurals name="file_list__footer__folder">
         <item quantity="one">%1$d pasta</item>
         <item quantity="one">%1$d pasta</item>
         <item quantity="many">%1$d pastas</item>
         <item quantity="many">%1$d pastas</item>

+ 1 - 0
app/src/main/res/values-vi/strings.xml

@@ -653,6 +653,7 @@
     <string name="share_no_password_title">Đặt mật khẩu</string>
     <string name="share_no_password_title">Đặt mật khẩu</string>
     <string name="share_password_title">Mật khẩu được bảo vệ</string>
     <string name="share_password_title">Mật khẩu được bảo vệ</string>
     <string name="share_permission_can_edit">Có thể chỉnh sửa</string>
     <string name="share_permission_can_edit">Có thể chỉnh sửa</string>
+    <string name="share_permission_file_drop">Thả file</string>
     <string name="share_permission_view_only">Chỉ xem</string>
     <string name="share_permission_view_only">Chỉ xem</string>
     <string name="share_permissions">Quyền kho</string>
     <string name="share_permissions">Quyền kho</string>
     <string name="share_remote_clarification">%1$s (từ xa)</string>
     <string name="share_remote_clarification">%1$s (từ xa)</string>

+ 2 - 1
app/src/main/res/values/dims.xml

@@ -79,7 +79,6 @@
     <dimen name="search_users_groups_layout_list_view_margin">20dp</dimen>
     <dimen name="search_users_groups_layout_list_view_margin">20dp</dimen>
     <dimen name="share_file_layout_text_size">12sp</dimen>
     <dimen name="share_file_layout_text_size">12sp</dimen>
     <dimen name="ssl_untrusted_cert_layout_padding">20dp</dimen>
     <dimen name="ssl_untrusted_cert_layout_padding">20dp</dimen>
-    <dimen name="scroll_view_height">180dp</dimen>
     <dimen name="upload_list_item_frame_layout_width">60dp</dimen>
     <dimen name="upload_list_item_frame_layout_width">60dp</dimen>
     <dimen name="upload_list_item_text_size">12sp</dimen>
     <dimen name="upload_list_item_text_size">12sp</dimen>
     <dimen name="uploader_list_item_layout_image_margin">12dp</dimen>
     <dimen name="uploader_list_item_layout_image_margin">12dp</dimen>
@@ -134,6 +133,8 @@
     <dimen name="bottom_sheet_text_size">16sp</dimen>
     <dimen name="bottom_sheet_text_size">16sp</dimen>
     <dimen name="permission_dialog_text_size">18sp</dimen>
     <dimen name="permission_dialog_text_size">18sp</dimen>
     <dimen name="button_corner_radius">24dp</dimen>
     <dimen name="button_corner_radius">24dp</dimen>
+    <dimen name="backup_button_width">160dp</dimen>
+
     <integer name="media_grid_width">4</integer>
     <integer name="media_grid_width">4</integer>
     <dimen name="account_action_button_margin">12dp</dimen>
     <dimen name="account_action_button_margin">12dp</dimen>
     <dimen name="account_action_button_height">50dp</dimen>
     <dimen name="account_action_button_height">50dp</dimen>

+ 0 - 2
app/src/main/res/values/strings.xml

@@ -799,8 +799,6 @@
     <string name="permission_deny">Deny</string>
     <string name="permission_deny">Deny</string>
     <string name="permission_allow">Allow</string>
     <string name="permission_allow">Allow</string>
     <string name="share_send_note">Note to recipient</string>
     <string name="share_send_note">Note to recipient</string>
-    <string name="note_confirm">Send</string>
-    <string name="send_note">Send note to recipient</string>
     <string name="note_could_not_sent">Could not send note</string>
     <string name="note_could_not_sent">Could not send note</string>
     <string name="hint_note">Note</string>
     <string name="hint_note">Note</string>
     <string name="no_browser_available">No app available to handle links</string>
     <string name="no_browser_available">No app available to handle links</string>

+ 1 - 1
scripts/analysis/analysis-wrapper.sh

@@ -128,7 +128,7 @@ else
 
 
     # check for NotNull
     # check for NotNull
     if [[ $(grep org.jetbrains.annotations app/src/main/* -irl | wc -l) -gt 0 ]] ; then
     if [[ $(grep org.jetbrains.annotations app/src/main/* -irl | wc -l) -gt 0 ]] ; then
-        notNull="org.jetbrains.annotations.NotNull is used. Please use androidx.annotation.NonNull instead.<br><br>"
+        notNull="org.jetbrains.annotations.* is used. Please use androidx.annotation.* instead.<br><br>"
     fi
     fi
 
 
     bodyContent="$codacyResult $lintResult $spotbugsResult $checkLibraryMessage $lintMessage $spotbugsMessage $gplayLimitation $notNull"
     bodyContent="$codacyResult $lintResult $spotbugsResult $checkLibraryMessage $lintMessage $spotbugsMessage $gplayLimitation $notNull"

+ 1 - 1
scripts/analysis/lint-results.txt

@@ -1,2 +1,2 @@
 DO NOT TOUCH; GENERATED BY DRONE
 DO NOT TOUCH; GENERATED BY DRONE
-      <span class="mdl-layout-title">Lint Report: 80 warnings</span>
+      <span class="mdl-layout-title">Lint Report: 79 warnings</span>