瀏覽代碼

Merge branch 'master' into feature/add_cancel_button_to_upload_notification

Jonas Mayer 1 年之前
父節點
當前提交
32d1773681
共有 77 個文件被更改,包括 2065 次插入478 次删除
  1. 9 1
      .drone.yml
  2. 1 1
      .github/workflows/analysis.yml
  3. 1 1
      .github/workflows/codeql.yml
  4. 1161 0
      app/schemas/com.nextcloud.client.database.NextcloudDatabase/74.json
  5. 2 0
      app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt
  6. 7 0
      app/src/main/AndroidManifest.xml
  7. 4 3
      app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt
  8. 3 1
      app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt
  9. 11 0
      app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt
  10. 0 38
      app/src/main/java/com/nextcloud/client/database/migrations/Migration70to71.kt
  11. 2 2
      app/src/main/java/com/nextcloud/client/di/ComponentsModule.java
  12. 31 14
      app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt
  13. 14 4
      app/src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt
  14. 13 2
      app/src/main/java/com/nextcloud/client/media/PlayerService.kt
  15. 2 0
      app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  16. 2 1
      app/src/main/java/com/owncloud/android/db/ProviderMeta.java
  17. 7 1
      app/src/main/java/com/owncloud/android/files/services/FileDownloader.java
  18. 7 2
      app/src/main/java/com/owncloud/android/files/services/FileUploader.java
  19. 0 257
      app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
  20. 249 0
      app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt
  21. 4 5
      app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
  22. 4 5
      app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java
  23. 4 4
      app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java
  24. 0 105
      app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.kt
  25. 194 0
      app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt
  26. 32 6
      app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java
  27. 28 22
      app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java
  28. 3 0
      app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java
  29. 154 0
      app/src/main/res/layout/account_removal_dialog.xml
  30. 5 0
      app/src/main/res/values-ar/strings.xml
  31. 5 0
      app/src/main/res/values-b+en+001/strings.xml
  32. 1 0
      app/src/main/res/values-bg-rBG/strings.xml
  33. 1 0
      app/src/main/res/values-br/strings.xml
  34. 1 0
      app/src/main/res/values-ca/strings.xml
  35. 5 0
      app/src/main/res/values-cs-rCZ/strings.xml
  36. 1 0
      app/src/main/res/values-da/strings.xml
  37. 5 0
      app/src/main/res/values-de/strings.xml
  38. 1 0
      app/src/main/res/values-el/strings.xml
  39. 1 0
      app/src/main/res/values-eo/strings.xml
  40. 1 0
      app/src/main/res/values-es-rEC/strings.xml
  41. 1 0
      app/src/main/res/values-es-rMX/strings.xml
  42. 1 0
      app/src/main/res/values-es/strings.xml
  43. 1 0
      app/src/main/res/values-eu/strings.xml
  44. 1 0
      app/src/main/res/values-fa/strings.xml
  45. 1 0
      app/src/main/res/values-fi-rFI/strings.xml
  46. 3 0
      app/src/main/res/values-fr/strings.xml
  47. 5 0
      app/src/main/res/values-gl/strings.xml
  48. 3 2
      app/src/main/res/values-hr/strings.xml
  49. 1 0
      app/src/main/res/values-hu-rHU/strings.xml
  50. 1 0
      app/src/main/res/values-is/strings.xml
  51. 10 0
      app/src/main/res/values-it/strings.xml
  52. 1 0
      app/src/main/res/values-iw/strings.xml
  53. 1 0
      app/src/main/res/values-ja-rJP/strings.xml
  54. 1 0
      app/src/main/res/values-ko/strings.xml
  55. 1 0
      app/src/main/res/values-lt-rLT/strings.xml
  56. 1 0
      app/src/main/res/values-lv/strings.xml
  57. 1 0
      app/src/main/res/values-mk/strings.xml
  58. 1 0
      app/src/main/res/values-nb-rNO/strings.xml
  59. 1 0
      app/src/main/res/values-nl/strings.xml
  60. 1 0
      app/src/main/res/values-pl/strings.xml
  61. 1 0
      app/src/main/res/values-pt-rBR/strings.xml
  62. 1 0
      app/src/main/res/values-pt-rPT/strings.xml
  63. 1 0
      app/src/main/res/values-ro/strings.xml
  64. 1 0
      app/src/main/res/values-ru/strings.xml
  65. 1 0
      app/src/main/res/values-sc/strings.xml
  66. 1 0
      app/src/main/res/values-sk-rSK/strings.xml
  67. 1 0
      app/src/main/res/values-sl/strings.xml
  68. 5 0
      app/src/main/res/values-sr/strings.xml
  69. 5 0
      app/src/main/res/values-sv/strings.xml
  70. 1 0
      app/src/main/res/values-th-rTH/strings.xml
  71. 2 0
      app/src/main/res/values-tr/strings.xml
  72. 5 0
      app/src/main/res/values-uk/strings.xml
  73. 5 0
      app/src/main/res/values-zh-rCN/strings.xml
  74. 5 0
      app/src/main/res/values-zh-rHK/strings.xml
  75. 5 0
      app/src/main/res/values-zh-rTW/strings.xml
  76. 6 1
      app/src/main/res/values/strings.xml
  77. 6 0
      settings.gradle

+ 9 - 1
.drone.yml

@@ -36,6 +36,7 @@ services:
     commands:
       - BRANCH="$SERVER_VERSION" /usr/local/bin/initnc.sh
       - echo 127.0.0.1 server >> /etc/hosts
+      - apt-get update && apt-get install -y composer
       - su www-data -c "OC_PASS=user1 php /var/www/html/occ user:add --password-from-env --display-name='User One' user1"
       - su www-data -c "OC_PASS=user2 php /var/www/html/occ user:add --password-from-env --display-name='User Two' user2"
       - su www-data -c "OC_PASS=user3 php /var/www/html/occ user:add --password-from-env --display-name='User Three' user3"
@@ -49,6 +50,9 @@ services:
       - su www-data -c "php /var/www/html/occ app:enable text"
       - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/end_to_end_encryption.git /var/www/html/apps/end_to_end_encryption/"
       - su www-data -c "php /var/www/html/occ app:enable end_to_end_encryption"
+      - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/photos.git /var/www/html/apps/photos/"
+      - su www-data -c "cd /var/www/html/apps/photos; composer install"
+      - su www-data -c "php /var/www/html/occ app:enable -f photos"
       - /usr/local/bin/run.sh
 
 trigger:
@@ -90,6 +94,7 @@ services:
     commands:
       - /usr/local/bin/initnc.sh
       - echo 127.0.0.1 server >> /etc/hosts
+      - apt-get update && apt-get install -y composer
       - su www-data -c "OC_PASS=user1 php /var/www/html/occ user:add --password-from-env --display-name='User One' user1"
       - su www-data -c "OC_PASS=user2 php /var/www/html/occ user:add --password-from-env --display-name='User Two' user2"
       - su www-data -c "OC_PASS=user3 php /var/www/html/occ user:add --password-from-env --display-name='User Three' user3"
@@ -103,6 +108,9 @@ services:
       - su www-data -c "php /var/www/html/occ app:enable text"
       - su www-data -c "git clone -b master https://github.com/nextcloud/end_to_end_encryption/  /var/www/html/apps/end_to_end_encryption/"
       - su www-data -c "php /var/www/html/occ app:enable end_to_end_encryption"
+      - su www-data -c "git clone https://github.com/nextcloud/photos.git /var/www/html/apps/photos/"
+      - su www-data -c "cd /var/www/html/apps/photos; composer install"
+      - su www-data -c "php /var/www/html/occ app:enable -f photos"
       - /usr/local/bin/run.sh
 
 trigger:
@@ -171,6 +179,6 @@ name: GIT_TOKEN
 data: XIoa9IYq+xQ+N5iln8dlpWv0jV6ROr7HuE24ioUr4uQ8m8SjyH0yognWYLYLqnbTKrFWlFZiEMQTH/sZiWjRFvV1iL0=
 ---
 kind: signature
-hmac: b78dcc477ff74ccbd7877df011090783847f8b5215a994be6597408bd735b524
+hmac: cf7f9b6403072500d986d4979375676729a9104f98b0c53fba5198c478cccab4
 
 ...

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

@@ -38,7 +38,7 @@ jobs:
                     repository: ${{ steps.get-vars.outputs.repo }}
                     ref: ${{ steps.get-vars.outputs.branch }}
             -   name: Set up JDK 17
-                uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
+                uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
                 with:
                     distribution: "temurin"
                     java-version: 17

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

@@ -36,7 +36,7 @@ jobs:
         with:
           languages: ${{ matrix.language }}
       - name: Set up JDK 17
-        uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3.13.0
+        uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v4.0.0
         with:
           distribution: "temurin"
           java-version: 17

+ 1161 - 0
app/schemas/com.nextcloud.client.database.NextcloudDatabase/74.json

@@ -0,0 +1,1161 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 74,
+    "identityHash": "7e73c045ac6d52d6c7c1626eefbc21e9",
+    "entities": [
+      {
+        "tableName": "arbitrary_data",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `cloud_id` TEXT, `key` TEXT, `value` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "cloudId",
+            "columnName": "cloud_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "key",
+            "columnName": "key",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "value",
+            "columnName": "value",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "capabilities",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountName",
+            "columnName": "account",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMajor",
+            "columnName": "version_mayor",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMinor",
+            "columnName": "version_minor",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionMicro",
+            "columnName": "version_micro",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionString",
+            "columnName": "version_string",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "versionEditor",
+            "columnName": "version_edition",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "extendedSupport",
+            "columnName": "extended_support",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "corePollinterval",
+            "columnName": "core_pollinterval",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingApiEnabled",
+            "columnName": "sharing_api_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicEnabled",
+            "columnName": "sharing_public_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicPasswordEnforced",
+            "columnName": "sharing_public_password_enforced",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateEnabled",
+            "columnName": "sharing_public_expire_date_enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateDays",
+            "columnName": "sharing_public_expire_date_days",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicExpireDateEnforced",
+            "columnName": "sharing_public_expire_date_enforced",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicSendMail",
+            "columnName": "sharing_public_send_mail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicUpload",
+            "columnName": "sharing_public_upload",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingUserSendMail",
+            "columnName": "sharing_user_send_mail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingResharing",
+            "columnName": "sharing_resharing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingFederationOutgoing",
+            "columnName": "sharing_federation_outgoing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingFederationIncoming",
+            "columnName": "sharing_federation_incoming",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesBigfilechunking",
+            "columnName": "files_bigfilechunking",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesUndelete",
+            "columnName": "files_undelete",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesVersioning",
+            "columnName": "files_versioning",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "externalLinks",
+            "columnName": "external_links",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverName",
+            "columnName": "server_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverColor",
+            "columnName": "server_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverTextColor",
+            "columnName": "server_text_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverElementColor",
+            "columnName": "server_element_color",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverSlogan",
+            "columnName": "server_slogan",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverLogo",
+            "columnName": "server_logo",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundUrl",
+            "columnName": "background_url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "endToEndEncryption",
+            "columnName": "end_to_end_encryption",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "endToEndEncryptionKeysExist",
+            "columnName": "end_to_end_encryption_keys_exist",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "activity",
+            "columnName": "activity",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundDefault",
+            "columnName": "background_default",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverBackgroundPlain",
+            "columnName": "background_plain",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocument",
+            "columnName": "richdocument",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentMimetypeList",
+            "columnName": "richdocument_mimetype_list",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentDirectEditing",
+            "columnName": "richdocument_direct_editing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentTemplates",
+            "columnName": "richdocument_direct_templates",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentOptionalMimetypeList",
+            "columnName": "richdocument_optional_mimetype_list",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharingPublicAskForOptionalPassword",
+            "columnName": "sharing_public_ask_for_optional_password",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richdocumentProductName",
+            "columnName": "richdocument_product_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "directEditingEtag",
+            "columnName": "direct_editing_etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userStatus",
+            "columnName": "user_status",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userStatusSupportsEmoji",
+            "columnName": "user_status_supports_emoji",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etag",
+            "columnName": "etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "filesLockingVersion",
+            "columnName": "files_locking_version",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "groupfolders",
+            "columnName": "groupfolders",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "dropAccount",
+            "columnName": "drop_account",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "external_links",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `icon_url` TEXT, `language` TEXT, `type` INTEGER, `name` TEXT, `url` TEXT, `redirect` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "iconUrl",
+            "columnName": "icon_url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "language",
+            "columnName": "language",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "url",
+            "columnName": "url",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "redirect",
+            "columnName": "redirect",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "filelist",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `filename` TEXT, `encrypted_filename` TEXT, `path` TEXT, `path_decrypted` TEXT, `parent` INTEGER, `created` INTEGER, `modified` INTEGER, `content_type` TEXT, `content_length` INTEGER, `media_path` TEXT, `file_owner` TEXT, `last_sync_date` INTEGER, `last_sync_date_for_data` INTEGER, `modified_at_last_sync_for_data` INTEGER, `etag` TEXT, `etag_on_server` TEXT, `share_by_link` INTEGER, `permissions` TEXT, `remote_id` TEXT, `local_id` INTEGER NOT NULL DEFAULT -1, `update_thumbnail` INTEGER, `is_downloading` INTEGER, `favorite` INTEGER, `is_encrypted` INTEGER, `etag_in_conflict` TEXT, `shared_via_users` INTEGER, `mount_type` INTEGER, `has_preview` INTEGER, `unread_comments_count` INTEGER, `owner_id` TEXT, `owner_display_name` TEXT, `note` TEXT, `sharees` TEXT, `rich_workspace` TEXT, `metadata_size` TEXT, `locked` INTEGER, `lock_type` INTEGER, `lock_owner` TEXT, `lock_owner_display_name` TEXT, `lock_owner_editor` TEXT, `lock_timestamp` INTEGER, `lock_timeout` INTEGER, `lock_token` TEXT, `tags` TEXT, `metadata_gps` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "filename",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "encryptedName",
+            "columnName": "encrypted_filename",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "path",
+            "columnName": "path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "pathDecrypted",
+            "columnName": "path_decrypted",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "parent",
+            "columnName": "parent",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "creation",
+            "columnName": "created",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "modified",
+            "columnName": "modified",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "contentType",
+            "columnName": "content_type",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "contentLength",
+            "columnName": "content_length",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "storagePath",
+            "columnName": "media_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountOwner",
+            "columnName": "file_owner",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastSyncDate",
+            "columnName": "last_sync_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastSyncDateForData",
+            "columnName": "last_sync_date_for_data",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "modifiedAtLastSyncForData",
+            "columnName": "modified_at_last_sync_for_data",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etag",
+            "columnName": "etag",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etagOnServer",
+            "columnName": "etag_on_server",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedViaLink",
+            "columnName": "share_by_link",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "permissions",
+            "columnName": "permissions",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remoteId",
+            "columnName": "remote_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localId",
+            "columnName": "local_id",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "-1"
+          },
+          {
+            "fieldPath": "updateThumbnail",
+            "columnName": "update_thumbnail",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isDownloading",
+            "columnName": "is_downloading",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "favorite",
+            "columnName": "favorite",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isEncrypted",
+            "columnName": "is_encrypted",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "etagInConflict",
+            "columnName": "etag_in_conflict",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedWithSharee",
+            "columnName": "shared_via_users",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "mountType",
+            "columnName": "mount_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hasPreview",
+            "columnName": "has_preview",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "unreadCommentsCount",
+            "columnName": "unread_comments_count",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ownerId",
+            "columnName": "owner_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ownerDisplayName",
+            "columnName": "owner_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "note",
+            "columnName": "note",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharees",
+            "columnName": "sharees",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "richWorkspace",
+            "columnName": "rich_workspace",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "metadataSize",
+            "columnName": "metadata_size",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "locked",
+            "columnName": "locked",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockType",
+            "columnName": "lock_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwner",
+            "columnName": "lock_owner",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwnerDisplayName",
+            "columnName": "lock_owner_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockOwnerEditor",
+            "columnName": "lock_owner_editor",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockTimestamp",
+            "columnName": "lock_timestamp",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockTimeout",
+            "columnName": "lock_timeout",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lockToken",
+            "columnName": "lock_token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "tags",
+            "columnName": "tags",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "metadataGPS",
+            "columnName": "metadata_gps",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "filesystem",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `is_folder` INTEGER, `found_at` INTEGER, `upload_triggered` INTEGER, `syncedfolder_id` TEXT, `crc32` TEXT, `modified_at` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileIsFolder",
+            "columnName": "is_folder",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileFoundRecently",
+            "columnName": "found_at",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSentForUpload",
+            "columnName": "upload_triggered",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "syncedFolderId",
+            "columnName": "syncedfolder_id",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "crc32",
+            "columnName": "crc32",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileModified",
+            "columnName": "modified_at",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "ocshares",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` INTEGER, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSource",
+            "columnName": "file_source",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "itemSource",
+            "columnName": "item_source",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareType",
+            "columnName": "share_type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareWith",
+            "columnName": "shate_with",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "path",
+            "columnName": "path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "permissions",
+            "columnName": "permissions",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sharedDate",
+            "columnName": "shared_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "expirationDate",
+            "columnName": "expiration_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareWithDisplayName",
+            "columnName": "shared_with_display_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isDirectory",
+            "columnName": "is_directory",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "userId",
+            "columnName": "user_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "idRemoteShared",
+            "columnName": "id_remote_shared",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountOwner",
+            "columnName": "owner_share",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isPasswordProtected",
+            "columnName": "is_password_protected",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "note",
+            "columnName": "note",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hideDownload",
+            "columnName": "hide_download",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareLink",
+            "columnName": "share_link",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "shareLabel",
+            "columnName": "share_label",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "synced_folders",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `wifi_only` INTEGER, `charging_only` INTEGER, `existing` INTEGER, `enabled` INTEGER, `enabled_timestamp_ms` INTEGER, `subfolder_by_date` INTEGER, `account` TEXT, `upload_option` INTEGER, `name_collision_policy` INTEGER, `type` INTEGER, `hidden` INTEGER, `sub_folder_rule` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remotePath",
+            "columnName": "remote_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "wifiOnly",
+            "columnName": "wifi_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "chargingOnly",
+            "columnName": "charging_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "existing",
+            "columnName": "existing",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enabled",
+            "columnName": "enabled",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "enabledTimestampMs",
+            "columnName": "enabled_timestamp_ms",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "subfolderByDate",
+            "columnName": "subfolder_by_date",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "account",
+            "columnName": "account",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadAction",
+            "columnName": "upload_option",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "nameCollisionPolicy",
+            "columnName": "name_collision_policy",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "hidden",
+            "columnName": "hidden",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "subFolderRule",
+            "columnName": "sub_folder_rule",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "list_of_uploads",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `local_path` TEXT, `remote_path` TEXT, `account_name` TEXT, `file_size` INTEGER, `status` INTEGER, `local_behaviour` INTEGER, `upload_time` INTEGER, `name_collision_policy` INTEGER, `is_create_remote_folder` INTEGER, `upload_end_timestamp` INTEGER, `last_result` INTEGER, `is_while_charging_only` INTEGER, `is_wifi_only` INTEGER, `created_by` INTEGER, `folder_unlock_token` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localPath",
+            "columnName": "local_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remotePath",
+            "columnName": "remote_path",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "accountName",
+            "columnName": "account_name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "fileSize",
+            "columnName": "file_size",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "localBehaviour",
+            "columnName": "local_behaviour",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadTime",
+            "columnName": "upload_time",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "nameCollisionPolicy",
+            "columnName": "name_collision_policy",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isCreateRemoteFolder",
+            "columnName": "is_create_remote_folder",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "uploadEndTimestamp",
+            "columnName": "upload_end_timestamp",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastResult",
+            "columnName": "last_result",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isWhileChargingOnly",
+            "columnName": "is_while_charging_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "isWifiOnly",
+            "columnName": "is_wifi_only",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "createdBy",
+            "columnName": "created_by",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "folderUnlockToken",
+            "columnName": "folder_unlock_token",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "virtual",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `ocfile_id` INTEGER)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "ocFileId",
+            "columnName": "ocfile_id",
+            "affinity": "INTEGER",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "_id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7e73c045ac6d52d6c7c1626eefbc21e9')"
+    ]
+  }
+}

+ 2 - 0
app/src/androidTest/java/com/owncloud/android/datamodel/OCCapabilityIT.kt

@@ -37,6 +37,7 @@ class OCCapabilityIT : AbstractIT() {
         capability.etag = "123"
         capability.userStatus = CapabilityBooleanType.TRUE
         capability.userStatusSupportsEmoji = CapabilityBooleanType.TRUE
+        capability.dropAccount = CapabilityBooleanType.TRUE
 
         fileDataStorageManager.saveCapabilities(capability)
 
@@ -45,5 +46,6 @@ class OCCapabilityIT : AbstractIT() {
         assertEquals(capability.etag, newCapability.etag)
         assertEquals(capability.userStatus, newCapability.userStatus)
         assertEquals(capability.userStatusSupportsEmoji, newCapability.userStatusSupportsEmoji)
+        assertEquals(capability.dropAccount, newCapability.dropAccount)
     }
 }

+ 7 - 0
app/src/main/AndroidManifest.xml

@@ -72,6 +72,9 @@
     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
 
+    <!-- Needed for Android 14 (API level 34) -->
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
 
     <!-- Some Chromebooks don't support touch. Although not essential,
          it's a good idea to explicitly include this declaration. -->
@@ -381,15 +384,19 @@
             android:exported="false" />
         <service
             android:name=".files.services.FileDownloader"
+            android:foregroundServiceType="dataSync"
             android:exported="false" />
         <service
             android:name="com.nextcloud.client.files.downloader.FileTransferService"
+            android:foregroundServiceType="dataSync"
             android:exported="false" />
         <service
             android:name=".files.services.FileUploader"
+            android:foregroundServiceType="dataSync"
             android:exported="false" />
         <service
             android:name="com.nextcloud.client.media.PlayerService"
+            android:foregroundServiceType="mediaPlayback"
             android:exported="false" />
 
         <activity

+ 4 - 3
app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt

@@ -40,8 +40,8 @@ import com.nextcloud.client.database.entity.ShareEntity
 import com.nextcloud.client.database.entity.SyncedFolderEntity
 import com.nextcloud.client.database.entity.UploadEntity
 import com.nextcloud.client.database.entity.VirtualEntity
+import com.nextcloud.client.database.migrations.DatabaseMigrationUtil
 import com.nextcloud.client.database.migrations.Migration67to68
-import com.nextcloud.client.database.migrations.Migration70to71
 import com.nextcloud.client.database.migrations.RoomMigration
 import com.nextcloud.client.database.migrations.addLegacyMigrations
 import com.owncloud.android.db.ProviderMeta
@@ -64,8 +64,10 @@ import com.owncloud.android.db.ProviderMeta
         AutoMigration(from = 66, to = 67),
         AutoMigration(from = 68, to = 69),
         AutoMigration(from = 69, to = 70),
+        AutoMigration(from = 70, to = 71, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class),
         AutoMigration(from = 71, to = 72),
-        AutoMigration(from = 72, to = 73)
+        AutoMigration(from = 72, to = 73),
+        AutoMigration(from = 73, to = 74, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class)
     ],
     exportSchema = true
 )
@@ -95,7 +97,6 @@ abstract class NextcloudDatabase : RoomDatabase() {
                     .addLegacyMigrations(clock, context)
                     .addMigrations(RoomMigration())
                     .addMigrations(Migration67to68())
-                    .addMigrations(Migration70to71())
                     .fallbackToDestructiveMigration()
                     .build()
             }

+ 3 - 1
app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt

@@ -129,5 +129,7 @@ data class CapabilityEntity(
     @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION)
     val filesLockingVersion: String?,
     @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_GROUPFOLDERS)
-    val groupfolders: Int?
+    val groupfolders: Int?,
+    @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT)
+    val dropAccount: Int?
 )

+ 11 - 0
app/src/main/java/com/nextcloud/client/database/migrations/DatabaseMigrationUtil.kt

@@ -22,6 +22,7 @@
 
 package com.nextcloud.client.database.migrations
 
+import androidx.room.migration.AutoMigrationSpec
 import androidx.sqlite.db.SupportSQLiteDatabase
 
 object DatabaseMigrationUtil {
@@ -102,4 +103,14 @@ object DatabaseMigrationUtil {
         database.execSQL("DROP TABLE $tableName")
         database.execSQL("ALTER TABLE $newTableTempName RENAME TO $tableName")
     }
+
+    /**
+     * Room AutoMigrationSpec to reset capabilities post migration.
+     */
+    class ResetCapabilitiesPostMigration : AutoMigrationSpec {
+        override fun onPostMigrate(db: SupportSQLiteDatabase) {
+            resetCapabilities(db)
+            super.onPostMigrate(db)
+        }
+    }
 }

+ 0 - 38
app/src/main/java/com/nextcloud/client/database/migrations/Migration70to71.kt

@@ -1,38 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- *  @author Álvaro Brey
- *  Copyright (C) 2023 Álvaro Brey
- *  Copyright (C) 2023 Nextcloud GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or 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.nextcloud.client.database.migrations
-
-import androidx.room.migration.Migration
-import androidx.sqlite.db.SupportSQLiteDatabase
-
-/**
- * Migration from version 70 to 71.
- *
- * resets capabilities to show groupfolder
- */
-@Suppress("MagicNumber")
-class Migration70to71 : Migration(70, 71) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        DatabaseMigrationUtil.resetCapabilities(database)
-    }
-}

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

@@ -81,7 +81,7 @@ import com.owncloud.android.ui.activity.ToolbarActivity;
 import com.owncloud.android.ui.activity.UploadFilesActivity;
 import com.owncloud.android.ui.activity.UploadListActivity;
 import com.owncloud.android.ui.activity.UserInfoActivity;
-import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog;
+import com.owncloud.android.ui.dialog.AccountRemovalDialog;
 import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment;
 import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
@@ -270,7 +270,7 @@ abstract class ComponentsModule {
     abstract ChooseTemplateDialogFragment chooseTemplateDialogFragment();
 
     @ContributesAndroidInjector
-    abstract AccountRemovalConfirmationDialog accountRemovalConfirmationDialog();
+    abstract AccountRemovalDialog accountRemovalDialog();
 
     @ContributesAndroidInjector
     abstract ChooseRichDocumentsTemplateDialogFragment chooseRichDocumentsTemplateDialogFragment();

+ 31 - 14
app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt

@@ -30,8 +30,11 @@ import android.view.View
 import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.content.ContextCompat
 import androidx.core.graphics.drawable.DrawableCompat
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
 import com.canhub.cropper.CropImageView
 import com.nextcloud.client.di.Injectable
+import com.nextcloud.utils.extensions.getParcelableArgument
 import com.owncloud.android.R
 import com.owncloud.android.databinding.ActivityEditImageBinding
 import com.owncloud.android.datamodel.OCFile
@@ -80,7 +83,8 @@ class EditImageActivity :
         binding = ActivityEditImageBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
-        file = intent.extras?.getParcelable(EXTRA_FILE) ?: throw IllegalArgumentException("Missing file argument")
+        file = intent.extras?.getParcelableArgument(EXTRA_FILE, OCFile::class.java)
+            ?: throw IllegalArgumentException("Missing file argument")
 
         setSupportActionBar(binding.toolbar)
         supportActionBar?.apply {
@@ -88,9 +92,11 @@ class EditImageActivity :
             setDisplayHomeAsUpEnabled(true)
         }
 
+        val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
+        windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
+
         window.statusBarColor = ContextCompat.getColor(this, R.color.black)
         window.navigationBarColor = getColor(R.color.black)
-        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
 
         setupCropper()
     }
@@ -105,17 +111,19 @@ class EditImageActivity :
             " " + getString(R.string.image_editor_file_edited_suffix) +
             resultUri?.substring(resultUri.lastIndexOf('.'))
 
-        FilesUploadHelper().uploadNewFiles(
-            user = storageManager.user,
-            localPaths = arrayOf(resultUri!!),
-            remotePaths = arrayOf(file.parentRemotePath + File.separator + newFileName),
-            createRemoteFolder = false,
-            createdBy = UploadFileOperation.CREATED_BY_USER,
-            requiresWifi = false,
-            requiresCharging = false,
-            nameCollisionPolicy = NameCollisionPolicy.RENAME,
-            localBehavior = FileUploader.LOCAL_BEHAVIOUR_DELETE
-        )
+        resultUri?.let {
+            FilesUploadHelper().uploadNewFiles(
+                user = storageManager.user,
+                localPaths = arrayOf(it),
+                remotePaths = arrayOf(file.parentRemotePath + File.separator + newFileName),
+                createRemoteFolder = false,
+                createdBy = UploadFileOperation.CREATED_BY_USER,
+                requiresWifi = false,
+                requiresCharging = false,
+                nameCollisionPolicy = NameCollisionPolicy.RENAME,
+                localBehavior = FileUploader.LOCAL_BEHAVIOUR_DELETE
+            )
+        }
     }
 
     override fun onSetImageUriComplete(view: CropImageView, uri: Uri, error: Exception?) {
@@ -147,6 +155,7 @@ class EditImageActivity :
             finish()
             true
         }
+
         else -> {
             finish()
             true
@@ -184,7 +193,15 @@ class EditImageActivity :
         // determine output file format
         format = when (file.mimeType) {
             MimeType.PNG -> Bitmap.CompressFormat.PNG
-            MimeType.WEBP -> Bitmap.CompressFormat.WEBP
+            MimeType.WEBP -> {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+                    Bitmap.CompressFormat.WEBP_LOSSY
+                } else {
+                    @Suppress("DEPRECATION")
+                    Bitmap.CompressFormat.WEBP
+                }
+            }
+
             else -> Bitmap.CompressFormat.JPEG
         }
     }

+ 14 - 4
app/src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt

@@ -22,6 +22,8 @@ package com.nextcloud.client.files.downloader
 import android.app.Service
 import android.content.Context
 import android.content.Intent
+import android.content.pm.ServiceInfo
+import android.os.Build
 import android.os.IBinder
 import com.nextcloud.client.account.User
 import com.nextcloud.client.core.AsyncRunner
@@ -107,10 +109,18 @@ class FileTransferService : Service() {
         }
 
         if (!isRunning) {
-            startForeground(
-                AppNotificationManager.TRANSFER_NOTIFICATION_ID,
-                notificationsManager.buildDownloadServiceForegroundNotification()
-            )
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                startForeground(
+                    AppNotificationManager.TRANSFER_NOTIFICATION_ID,
+                    notificationsManager.buildDownloadServiceForegroundNotification(),
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
+                )
+            } else {
+                startForeground(
+                    AppNotificationManager.TRANSFER_NOTIFICATION_ID,
+                    notificationsManager.buildDownloadServiceForegroundNotification()
+                )
+            }
         }
 
         val request: Request = intent.getParcelableExtra(EXTRA_REQUEST)!!

+ 13 - 2
app/src/main/java/com/nextcloud/client/media/PlayerService.kt

@@ -22,7 +22,9 @@ package com.nextcloud.client.media
 import android.app.PendingIntent
 import android.app.Service
 import android.content.Intent
+import android.content.pm.ServiceInfo
 import android.media.AudioManager
+import android.os.Build
 import android.os.Bundle
 import android.os.IBinder
 import android.widget.MediaController
@@ -167,11 +169,20 @@ class PlayerService : Service() {
         notificationBuilder.setContentTitle(ticker)
         notificationBuilder.setContentText(content)
 
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA)
         }
 
-        startForeground(R.string.media_notif_ticker, notificationBuilder.build())
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            startForeground(
+                R.string.media_notif_ticker,
+                notificationBuilder.build(),
+                ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+            )
+        } else {
+            startForeground(R.string.media_notif_ticker, notificationBuilder.build())
+        }
+
         isRunning = true
     }
 

+ 2 - 0
app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -1947,6 +1947,7 @@ public class FileDataStorageManager {
         contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION,
                           capability.getFilesLockingVersion());
         contentValues.put(ProviderTableMeta.CAPABILITIES_GROUPFOLDERS, capability.getGroupfolders().getValue());
+        contentValues.put(ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT, capability.getDropAccount().getValue());
 
         return contentValues;
     }
@@ -2103,6 +2104,7 @@ public class FileDataStorageManager {
             capability.setFilesLockingVersion(
                 getString(cursor, ProviderTableMeta.CAPABILITIES_FILES_LOCKING_VERSION));
             capability.setGroupfolders(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_GROUPFOLDERS));
+            capability.setDropAccount(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_DROP_ACCOUNT));
         }
         return capability;
     }

+ 2 - 1
app/src/main/java/com/owncloud/android/db/ProviderMeta.java

@@ -35,7 +35,7 @@ import java.util.List;
  */
 public class ProviderMeta {
     public static final String DB_NAME = "filelist";
-    public static final int DB_VERSION = 73;
+    public static final int DB_VERSION = 74;
 
     private ProviderMeta() {
         // No instance
@@ -259,6 +259,7 @@ public class ProviderMeta {
         public static final String CAPABILITIES_USER_STATUS = "user_status";
         public static final String CAPABILITIES_USER_STATUS_SUPPORTS_EMOJI = "user_status_supports_emoji";
         public static final String CAPABILITIES_GROUPFOLDERS = "groupfolders";
+        public static final String CAPABILITIES_DROP_ACCOUNT = "drop_account";
 
         //Columns of Uploads table
         public static final String UPLOADS_LOCAL_PATH = "local_path";

+ 7 - 1
app/src/main/java/com/owncloud/android/files/services/FileDownloader.java

@@ -28,8 +28,10 @@ import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
+import android.content.pm.ServiceInfo;
 import android.graphics.BitmapFactory;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -200,7 +202,11 @@ public class FileDownloader extends Service
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log_OC.d(TAG, "Starting command with id " + startId);
 
-        startForeground(FOREGROUND_SERVICE_ID, mNotification);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            startForeground(FOREGROUND_SERVICE_ID, mNotification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
+        } else {
+            startForeground(FOREGROUND_SERVICE_ID, mNotification);
+        }
 
         if (intent == null || !intent.hasExtra(EXTRA_USER) || !intent.hasExtra(EXTRA_FILE)) {
             Log_OC.e(TAG, "Not enough information provided in intent");

+ 7 - 2
app/src/main/java/com/owncloud/android/files/services/FileUploader.java

@@ -37,6 +37,7 @@ import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ServiceInfo;
 import android.graphics.BitmapFactory;
 import android.os.Binder;
 import android.os.Build;
@@ -314,7 +315,11 @@ public class FileUploader extends Service
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log_OC.d(TAG, "Starting command with id " + startId);
 
-        startForeground(FOREGROUND_SERVICE_ID, mNotification);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            startForeground(FOREGROUND_SERVICE_ID, mNotification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
+        } else {
+            startForeground(FOREGROUND_SERVICE_ID, mNotification);
+        }
 
         if (intent == null) {
             Log_OC.e(TAG, "Intent is null");
@@ -612,7 +617,7 @@ public class FileUploader extends Service
 
     /**
      * Core upload method: sends the file(s) to upload WARNING: legacy code, must be in sync with @{{@link
-     * FilesUploadWorker#upload(UploadFileOperation, User)}
+     * FilesUploadWorker upload(UploadFileOperation, User)}
      *
      * @param uploadKey Key to access the upload to perform, contained in mPendingUploads
      */

+ 0 - 257
app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java

@@ -1,257 +0,0 @@
-/*
- * ownCloud Android client application
- *
- * @author Bartek Przybylski
- * @author David A. Velasco Copyright (C) 2012 Bartek Przybylski Copyright (C) 2016 ownCloud Inc.
- * <p>
- * 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.
- * <p>
- * 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.
- * <p/>
- * 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.activity;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.Toast;
-
-import com.nextcloud.client.account.User;
-import com.nextcloud.java.util.Optional;
-import com.owncloud.android.R;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.datamodel.UploadsStorageManager;
-import com.owncloud.android.db.OCUpload;
-import com.owncloud.android.files.services.FileDownloader;
-import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.files.services.NameCollisionPolicy;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
-import com.owncloud.android.lib.resources.files.model.RemoteFile;
-import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
-import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
-import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
-import com.owncloud.android.utils.FileStorageUtils;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-
-
-/**
- * Wrapper activity which will be launched if keep-in-sync file will be modified by external application.
- */
-public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
-    /**
-     * A nullable upload entry that must be removed when and if the conflict is resolved.
-     */
-    public static final String EXTRA_CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID";
-    /**
-     * Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
-     */
-    public static final String EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR";
-    public static final String EXTRA_EXISTING_FILE = "EXISTING_FILE";
-
-    private static final String TAG = ConflictsResolveActivity.class.getSimpleName();
-
-    @Inject UploadsStorageManager uploadsStorageManager;
-
-    private long conflictUploadId;
-    private OCFile existingFile;
-    private OCFile newFile;
-    private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET;
-    protected OnConflictDecisionMadeListener listener;
-
-    public static Intent createIntent(OCFile file,
-                                      User user,
-                                      long conflictUploadId,
-                                      Integer flag,
-                                      Context context) {
-        Intent intent = new Intent(context, ConflictsResolveActivity.class);
-        if (flag != null) {
-            intent.setFlags(intent.getFlags() | flag);
-        }
-        intent.putExtra(EXTRA_FILE, file);
-        intent.putExtra(EXTRA_USER, user);
-        intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
-
-        return intent;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState != null) {
-            conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID);
-            existingFile = savedInstanceState.getParcelable(EXTRA_EXISTING_FILE);
-            localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR);
-        } else {
-            conflictUploadId = getIntent().getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1);
-            existingFile = getIntent().getParcelableExtra(EXTRA_EXISTING_FILE);
-            localBehaviour = getIntent().getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
-        }
-
-        OCUpload upload = uploadsStorageManager.getUploadById(conflictUploadId);
-
-        if (upload != null) {
-            localBehaviour = upload.getLocalAction();
-        }
-
-        // new file was modified locally in file system
-        newFile = getFile();
-
-        listener = decision -> {
-            OCFile file = newFile; // local file got changed, so either upload it or replace it again by server
-            // version
-            User user = getUser().orElseThrow(RuntimeException::new);
-            switch (decision) {
-                case CANCEL:
-                    // nothing to do
-                    break;
-                case KEEP_LOCAL: // Upload
-                    FileUploader.uploadUpdateFile(
-                        getBaseContext(),
-                        user,
-                        file,
-                        localBehaviour,
-                        NameCollisionPolicy.OVERWRITE
-                                                 );
-
-                    uploadsStorageManager.removeUpload(upload);
-                    break;
-                case KEEP_BOTH: // Upload
-                    FileUploader.uploadUpdateFile(
-                        getBaseContext(),
-                        user,
-                        file,
-                        localBehaviour,
-                        NameCollisionPolicy.RENAME
-                                                 );
-
-                    uploadsStorageManager.removeUpload(upload);
-                    break;
-                case KEEP_SERVER: // Download
-                    if (!shouldDeleteLocal()) {
-                        // Overwrite local file
-                        Intent intent = new Intent(getBaseContext(), FileDownloader.class);
-                        intent.putExtra(FileDownloader.EXTRA_USER, getUser().orElseThrow(RuntimeException::new));
-                        intent.putExtra(FileDownloader.EXTRA_FILE, file);
-                        intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
-                        startService(intent);
-                    } else {
-                        uploadsStorageManager.removeUpload(upload);
-                    }
-                    break;
-            }
-
-            finish();
-        };
-    }
-
-    @Override
-    protected void onSaveInstanceState(@NonNull Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId);
-        outState.putParcelable(EXTRA_EXISTING_FILE, existingFile);
-        outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour);
-    }
-
-    @Override
-    public void conflictDecisionMade(Decision decision) {
-        listener.conflictDecisionMade(decision);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (getAccount() == null) {
-            finish();
-            return;
-        }
-
-        if (newFile == null) {
-            Log_OC.e(TAG, "No file received");
-            finish();
-            return;
-        }
-
-        if (existingFile == null) {
-            // fetch info of existing file from server
-            ReadFileRemoteOperation operation = new ReadFileRemoteOperation(newFile.getRemotePath());
-
-            new Thread(() -> {
-                try {
-                    RemoteOperationResult result = operation.execute(getAccount(), this);
-
-                    if (result.isSuccess()) {
-                        existingFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
-                        existingFile.setLastSyncDateForProperties(System.currentTimeMillis());
-
-                        startDialog();
-                    } else {
-                        Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.getHttpCode());
-                        showErrorAndFinish();
-                    }
-                } catch (Exception e) {
-                    Log_OC.e(TAG, "Error when trying to fetch remote file", e);
-                    showErrorAndFinish();
-                }
-
-
-            }).start();
-        } else {
-            startDialog();
-        }
-    }
-
-    private void startDialog() {
-        Optional<User> userOptional = getUser();
-
-        if (!userOptional.isPresent()) {
-            Log_OC.e(TAG, "User not present");
-            showErrorAndFinish();
-        }
-
-        // Check whether the file is contained in the current Account
-        Fragment prev = getSupportFragmentManager().findFragmentByTag("conflictDialog");
-
-        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
-        if (prev != null) {
-            fragmentTransaction.remove(prev);
-        }
-
-        if (existingFile != null && getStorageManager().fileExists(newFile.getRemotePath())) {
-            ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
-                                                                               newFile,
-                                                                               userOptional.get());
-            dialog.show(fragmentTransaction, "conflictDialog");
-        } else {
-            // Account was changed to a different one - just finish
-            Log_OC.e(TAG, "Account was changed, finishing");
-            showErrorAndFinish();
-        }
-    }
-
-    private void showErrorAndFinish() {
-        runOnUiThread(() -> Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show());
-        finish();
-    }
-
-    /**
-     * @return whether the local version of the files is to be deleted.
-     */
-    private boolean shouldDeleteLocal() {
-        return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE;
-    }
-}

+ 249 - 0
app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt

@@ -0,0 +1,249 @@
+/*
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco Copyright (C) 2012 Bartek Przybylski Copyright (C) 2016 ownCloud Inc.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p/>
+ * 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.activity
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.Toast
+import com.nextcloud.client.account.User
+import com.nextcloud.utils.extensions.getParcelableArgument
+import com.owncloud.android.R
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.datamodel.UploadsStorageManager
+import com.owncloud.android.db.OCUpload
+import com.owncloud.android.files.services.FileDownloader
+import com.owncloud.android.files.services.FileUploader
+import com.owncloud.android.files.services.NameCollisionPolicy
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation
+import com.owncloud.android.lib.resources.files.model.RemoteFile
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener
+import com.owncloud.android.utils.FileStorageUtils
+import javax.inject.Inject
+
+/**
+ * Wrapper activity which will be launched if keep-in-sync file will be modified by external application.
+ */
+class ConflictsResolveActivity : FileActivity(), OnConflictDecisionMadeListener {
+
+    @JvmField
+    @Inject
+    var uploadsStorageManager: UploadsStorageManager? = null
+
+    private var conflictUploadId: Long = 0
+    private var existingFile: OCFile? = null
+    private var newFile: OCFile? = null
+    private var localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET
+
+    @JvmField
+    var listener: OnConflictDecisionMadeListener? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        getArguments(savedInstanceState)
+
+        val upload = uploadsStorageManager?.getUploadById(conflictUploadId)
+        if (upload != null) {
+            localBehaviour = upload.localAction
+        }
+
+        // new file was modified locally in file system
+        newFile = file
+        setupOnConflictDecisionMadeListener(upload)
+    }
+
+    private fun getArguments(savedInstanceState: Bundle?) {
+        if (savedInstanceState != null) {
+            conflictUploadId = savedInstanceState.getLong(EXTRA_CONFLICT_UPLOAD_ID)
+            existingFile = savedInstanceState.getParcelableArgument(EXTRA_EXISTING_FILE, OCFile::class.java)
+            localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR)
+        } else {
+            conflictUploadId = intent.getLongExtra(EXTRA_CONFLICT_UPLOAD_ID, -1)
+            existingFile = intent.getParcelableExtra(EXTRA_EXISTING_FILE)
+            localBehaviour = intent.getIntExtra(EXTRA_LOCAL_BEHAVIOUR, localBehaviour)
+        }
+    }
+
+    private fun setupOnConflictDecisionMadeListener(upload: OCUpload?) {
+        listener = OnConflictDecisionMadeListener { decision: Decision? ->
+            val file = newFile // local file got changed, so either upload it or replace it again by server
+            // version
+            val user = user.orElseThrow { RuntimeException() }
+            when (decision) {
+                Decision.CANCEL -> {}
+                Decision.KEEP_LOCAL -> {
+                    FileUploader.uploadUpdateFile(
+                        baseContext,
+                        user,
+                        file,
+                        localBehaviour,
+                        NameCollisionPolicy.OVERWRITE
+                    )
+                    uploadsStorageManager!!.removeUpload(upload)
+                }
+
+                Decision.KEEP_BOTH -> {
+                    FileUploader.uploadUpdateFile(
+                        baseContext,
+                        user,
+                        file,
+                        localBehaviour,
+                        NameCollisionPolicy.RENAME
+                    )
+                    uploadsStorageManager!!.removeUpload(upload)
+                }
+
+                Decision.KEEP_SERVER -> if (!shouldDeleteLocal()) {
+                    // Overwrite local file
+                    val intent = Intent(baseContext, FileDownloader::class.java)
+                    intent.putExtra(FileDownloader.EXTRA_USER, getUser().orElseThrow { RuntimeException() })
+                    intent.putExtra(FileDownloader.EXTRA_FILE, file)
+                    intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
+                    startService(intent)
+                } else {
+                    uploadsStorageManager!!.removeUpload(upload)
+                }
+
+                else -> {}
+            }
+            finish()
+        }
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+        outState.putLong(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
+        outState.putParcelable(EXTRA_EXISTING_FILE, existingFile)
+        outState.putInt(EXTRA_LOCAL_BEHAVIOUR, localBehaviour)
+    }
+
+    override fun conflictDecisionMade(decision: Decision) {
+        listener?.conflictDecisionMade(decision)
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if (account == null) {
+            finish()
+            return
+        }
+        if (newFile == null) {
+            Log_OC.e(TAG, "No file received")
+            finish()
+            return
+        }
+        if (existingFile == null) {
+            // fetch info of existing file from server
+            val operation = ReadFileRemoteOperation(newFile!!.remotePath)
+
+            @Suppress("TooGenericExceptionCaught")
+            Thread {
+                try {
+                    val result = operation.execute(account, this)
+                    if (result.isSuccess) {
+                        existingFile = FileStorageUtils.fillOCFile(result.data[0] as RemoteFile)
+                        existingFile?.lastSyncDateForProperties = System.currentTimeMillis()
+                        startDialog()
+                    } else {
+                        Log_OC.e(TAG, "ReadFileRemoteOp returned failure with code: " + result.httpCode)
+                        showErrorAndFinish()
+                    }
+                } catch (e: Exception) {
+                    Log_OC.e(TAG, "Error when trying to fetch remote file", e)
+                    showErrorAndFinish()
+                }
+            }.start()
+        } else {
+            startDialog()
+        }
+    }
+
+    private fun startDialog() {
+        val userOptional = user
+        if (!userOptional.isPresent) {
+            Log_OC.e(TAG, "User not present")
+            showErrorAndFinish()
+        }
+
+        // Check whether the file is contained in the current Account
+        val prev = supportFragmentManager.findFragmentByTag("conflictDialog")
+        val fragmentTransaction = supportFragmentManager.beginTransaction()
+        if (prev != null) {
+            fragmentTransaction.remove(prev)
+        }
+        if (existingFile != null && storageManager.fileExists(newFile!!.remotePath)) {
+            val dialog = ConflictsResolveDialog.newInstance(
+                existingFile,
+                newFile,
+                userOptional.get()
+            )
+            dialog.show(fragmentTransaction, "conflictDialog")
+        } else {
+            // Account was changed to a different one - just finish
+            Log_OC.e(TAG, "Account was changed, finishing")
+            showErrorAndFinish()
+        }
+    }
+
+    private fun showErrorAndFinish() {
+        runOnUiThread { Toast.makeText(this, R.string.conflict_dialog_error, Toast.LENGTH_LONG).show() }
+        finish()
+    }
+
+    /**
+     * @return whether the local version of the files is to be deleted.
+     */
+    private fun shouldDeleteLocal(): Boolean {
+        return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE
+    }
+
+    companion object {
+        /**
+         * A nullable upload entry that must be removed when and if the conflict is resolved.
+         */
+        const val EXTRA_CONFLICT_UPLOAD_ID = "CONFLICT_UPLOAD_ID"
+
+        /**
+         * Specify the upload local behaviour when there is no CONFLICT_UPLOAD.
+         */
+        const val EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR"
+        const val EXTRA_EXISTING_FILE = "EXISTING_FILE"
+        private val TAG = ConflictsResolveActivity::class.java.simpleName
+
+        @JvmStatic
+        fun createIntent(
+            file: OCFile?,
+            user: User?,
+            conflictUploadId: Long,
+            flag: Int?,
+            context: Context?
+        ): Intent {
+            val intent = Intent(context, ConflictsResolveActivity::class.java)
+            if (flag != null) {
+                intent.flags = intent.flags or flag
+            }
+            intent.putExtra(EXTRA_FILE, file)
+            intent.putExtra(EXTRA_USER, user)
+            intent.putExtra(EXTRA_CONFLICT_UPLOAD_ID, conflictUploadId)
+            return intent
+        }
+    }
+}

+ 4 - 5
app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -479,7 +479,6 @@ public abstract class DrawerActivity extends ToolbarActivity
         unsetAllDrawerMenuItems();
     }
 
-
     private void onNavigationItemClicked(final MenuItem menuItem) {
         setDrawerMenuItemChecked(menuItem.getItemId());
 
@@ -506,7 +505,7 @@ public abstract class DrawerActivity extends ToolbarActivity
             handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH),
                                menuItem.getItemId());
         } else if (itemId == R.id.nav_gallery) {
-            startPhotoSearch(menuItem);
+            startPhotoSearch(menuItem.getItemId());
         } else if (itemId == R.id.nav_on_device) {
             EventBus.getDefault().post(new ChangeMenuEvent());
             showFiles(true);
@@ -527,7 +526,7 @@ public abstract class DrawerActivity extends ToolbarActivity
             menuItem.setChecked(false);
             final Optional<User> optionalUser = getUser();
             if (optionalUser.isPresent()) {
-                UserInfoActivity.openAccountRemovalConfirmationDialog(optionalUser.get(), getSupportFragmentManager());
+                UserInfoActivity.openAccountRemovalDialog(optionalUser.get(), getSupportFragmentManager());
             }
         } else if (itemId == R.id.nav_shared) {
             startSharedSearch(menuItem);
@@ -598,11 +597,11 @@ public abstract class DrawerActivity extends ToolbarActivity
         launchActivityForSearch(searchEvent, menuItem.getItemId());
     }
 
-    private void startPhotoSearch(MenuItem menuItem) {
+    public void startPhotoSearch(int id) {
         SearchEvent searchEvent = new SearchEvent("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH);
         MainApp.showOnlyFilesOnDevice(false);
 
-        launchActivityForSearch(searchEvent, menuItem.getItemId());
+        launchActivityForSearch(searchEvent, id);
     }
 
     private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) {

+ 4 - 5
app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java

@@ -58,7 +58,7 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.ui.adapter.UserListAdapter;
 import com.owncloud.android.ui.adapter.UserListItem;
-import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog;
+import com.owncloud.android.ui.dialog.AccountRemovalDialog;
 import com.owncloud.android.ui.events.AccountRemovedEvent;
 import com.owncloud.android.ui.helpers.FileOperationsHelper;
 
@@ -470,9 +470,8 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
         }
     }
 
-    public static void openAccountRemovalConfirmationDialog(User user, FragmentManager fragmentManager) {
-        AccountRemovalConfirmationDialog dialog =
-            AccountRemovalConfirmationDialog.newInstance(user);
+    public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) {
+        AccountRemovalDialog dialog = AccountRemovalDialog.newInstance(user);
         dialog.show(fragmentManager, "dialog");
     }
 
@@ -509,7 +508,7 @@ public class ManageAccountsActivity extends FileActivity implements UserListAdap
                 if (itemId == R.id.action_open_account) {
                     accountClicked(user.hashCode());
                 } else if (itemId == R.id.action_delete_account) {
-                    openAccountRemovalConfirmationDialog(user, getSupportFragmentManager());
+                    openAccountRemovalDialog(user, getSupportFragmentManager());
                 } else {
                     openAccount(user);
                 }

+ 4 - 4
app/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java

@@ -56,7 +56,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
-import com.owncloud.android.ui.dialog.AccountRemovalConfirmationDialog;
+import com.owncloud.android.ui.dialog.AccountRemovalDialog;
 import com.owncloud.android.ui.events.TokenPushEvent;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.PushUtils;
@@ -173,7 +173,7 @@ public class UserInfoActivity extends DrawerActivity implements Injectable {
         } else if (itemId == R.id.action_open_account) {
             accountClicked(user.hashCode());
         } else if (itemId == R.id.action_delete_account) {
-            openAccountRemovalConfirmationDialog(user, getSupportFragmentManager());
+            openAccountRemovalDialog(user, getSupportFragmentManager());
         } else {
             retval = super.onOptionsItemSelected(item);
         }
@@ -302,8 +302,8 @@ public class UserInfoActivity extends DrawerActivity implements Injectable {
         }
     }
 
-    public static void openAccountRemovalConfirmationDialog(User user, FragmentManager fragmentManager) {
-        AccountRemovalConfirmationDialog dialog = AccountRemovalConfirmationDialog.newInstance(user);
+    public static void openAccountRemovalDialog(User user, FragmentManager fragmentManager) {
+        AccountRemovalDialog dialog = AccountRemovalDialog.newInstance(user);
         dialog.show(fragmentManager, "dialog");
     }
 

+ 0 - 105
app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalConfirmationDialog.kt

@@ -1,105 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Tobias Kaminsky
- * Copyright (C) 2020 Tobias Kaminsky
- * Copyright (C) 2020 Nextcloud GmbH
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.dialog
-
-import android.app.Dialog
-import android.content.DialogInterface
-import android.os.Build
-import android.os.Bundle
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.DialogFragment
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-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.utils.theme.ViewThemeUtils
-import javax.inject.Inject
-
-class AccountRemovalConfirmationDialog : DialogFragment(), Injectable {
-    @JvmField
-    @Inject
-    var backgroundJobManager: BackgroundJobManager? = null
-
-    @JvmField
-    @Inject
-    var viewThemeUtils: ViewThemeUtils? = null
-
-    private var user: User? = null
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        user = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-            requireArguments().getParcelable(KEY_USER, User::class.java)
-        } else {
-            @Suppress("DEPRECATION")
-            requireArguments().getParcelable(KEY_USER)
-        }
-    }
-
-    override fun onStart() {
-        super.onStart()
-
-        val alertDialog = dialog as AlertDialog?
-
-        if (alertDialog != null) {
-            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)
-        }
-    }
-
-    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
-        val builder = MaterialAlertDialogBuilder(requireActivity())
-            .setTitle(R.string.delete_account)
-            .setMessage(resources.getString(R.string.delete_account_warning, user!!.accountName))
-            .setIcon(R.drawable.ic_warning)
-            .setPositiveButton(R.string.common_ok) { _: DialogInterface?, _: Int ->
-                backgroundJobManager?.startAccountRemovalJob(
-                    user!!.accountName,
-                    false
-                )
-            }
-            .setNegativeButton(R.string.common_cancel, null)
-
-        viewThemeUtils?.dialog?.colorMaterialAlertDialogBackground(requireActivity(), builder)
-
-        return builder.create()
-    }
-
-    companion object {
-
-        private const val KEY_USER = "USER"
-
-        @JvmStatic
-        fun newInstance(user: User?): AccountRemovalConfirmationDialog {
-            val bundle = Bundle()
-            bundle.putParcelable(KEY_USER, user)
-            val dialog = AccountRemovalConfirmationDialog()
-            dialog.arguments = bundle
-            return dialog
-        }
-    }
-}

+ 194 - 0
app/src/main/java/com/owncloud/android/ui/dialog/AccountRemovalDialog.kt

@@ -0,0 +1,194 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author ZetaTom
+ * @author Tobias Kaminsky
+ * Copyright (C) 2023 ZetaTom
+ * Copyright (C) 2020 Tobias Kaminsky
+ * Copyright (C) 2020 Nextcloud GmbH
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.dialog
+
+import android.app.Dialog
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.nextcloud.client.account.User
+import com.nextcloud.client.account.UserAccountManager
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.jobs.BackgroundJobManager
+import com.nextcloud.utils.extensions.getParcelableArgument
+import com.owncloud.android.R
+import com.owncloud.android.databinding.AccountRemovalDialogBinding
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.utils.DisplayUtils
+import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import javax.inject.Inject
+
+class AccountRemovalDialog : DialogFragment(), AvatarGenerationListener, Injectable {
+
+    @Inject
+    lateinit var backgroundJobManager: BackgroundJobManager
+
+    @Inject
+    lateinit var viewThemeUtils: ViewThemeUtils
+
+    private var user: User? = null
+    private lateinit var alertDialog: AlertDialog
+    private var _binding: AccountRemovalDialogBinding? = null
+    private val binding get() = _binding!!
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        user = requireArguments().getParcelableArgument(KEY_USER, User::class.java)
+    }
+
+    override fun onStart() {
+        super.onStart()
+
+        // disable positive button and apply theming
+        alertDialog = dialog as AlertDialog
+        alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
+
+        viewThemeUtils.platform.themeRadioButton(binding.radioLocalRemove)
+        viewThemeUtils.platform.themeRadioButton(binding.radioRequestDeletion)
+        viewThemeUtils.material.colorMaterialButtonPrimaryTonal(
+            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton
+        )
+        viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(
+            alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE) as MaterialButton
+        )
+
+        binding.userName.text = UserAccountManager.getDisplayName(user)
+        binding.account.text = user?.let { DisplayUtils.convertIdn(it.accountName, false) }
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        _binding = AccountRemovalDialogBinding.inflate(layoutInflater)
+
+        // start avatar generation
+        setAvatar()
+
+        // hide second option when plug-in isn't installed
+        if (hasDropAccount()) {
+            binding.requestDeletion.visibility = View.VISIBLE
+        }
+
+        val builder =
+            MaterialAlertDialogBuilder(requireActivity())
+                .setTitle(R.string.delete_account)
+                .setView(binding.root)
+                .setNegativeButton(R.string.common_cancel) { _, _ -> }
+                .setPositiveButton(R.string.delete_account) { _, _ -> removeAccount() }
+
+        // allow selection by clicking on list element
+        binding.localRemove.setOnClickListener {
+            binding.radioLocalRemove.performClick()
+        }
+        binding.requestDeletion.setOnClickListener {
+            binding.radioRequestDeletion.performClick()
+        }
+
+        // set listeners for custom radio button list
+        binding.radioLocalRemove.setOnClickListener {
+            binding.radioRequestDeletion.isChecked = false
+            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply {
+                text = getText(R.string.delete_account)
+                isEnabled = true
+            }
+        }
+        binding.radioRequestDeletion.setOnClickListener {
+            binding.radioLocalRemove.isChecked = false
+            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).apply {
+                text = getString(R.string.request_account_deletion_button)
+                isEnabled = true
+            }
+        }
+
+        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireActivity(), builder)
+
+        return builder.create()
+    }
+
+    /**
+     * Get value of `drop-account` capability.
+     */
+    private fun hasDropAccount(): Boolean {
+        val capability = FileDataStorageManager(user, context?.contentResolver).getCapability(user)
+        return capability.dropAccount.isTrue
+    }
+
+    /**
+     * Start removal of account. Depending on which option is checked, either a browser will open to request deletion,
+     * or the local account will be removed immediately.
+     */
+    private fun removeAccount() {
+        user?.let { user ->
+            if (binding.radioRequestDeletion.isChecked) {
+                DisplayUtils.startLinkIntent(activity, user.server.uri.toString() + DROP_ACCOUNT_URI)
+            } else {
+                backgroundJobManager.startAccountRemovalJob(user.accountName, false)
+            }
+        }
+    }
+
+    /**
+     * Start avatar generation.
+     */
+    private fun setAvatar() {
+        try {
+            val imageView = binding.userIcon
+            imageView.tag = user!!.accountName
+            DisplayUtils.setAvatar(
+                user!!,
+                this,
+                resources.getDimension(R.dimen.list_item_avatar_icon_radius),
+                resources,
+                imageView,
+                context
+            )
+        } catch (_: Exception) {
+        }
+    }
+
+    override fun avatarGenerated(avatarDrawable: Drawable?, callContext: Any?) {
+        avatarDrawable?.let {
+            binding.userIcon.setImageDrawable(it)
+        }
+    }
+
+    override fun shouldCallGeneratedCallback(tag: String?, callContext: Any?): Boolean {
+        return binding.userIcon.tag == tag
+    }
+
+    companion object {
+        private const val KEY_USER = "USER"
+        private const val DROP_ACCOUNT_URI = "/settings/user/drop_account"
+
+        @JvmStatic
+        fun newInstance(user: User) = AccountRemovalDialog().apply {
+            arguments = Bundle().apply {
+                putParcelable(KEY_USER, user)
+            }
+        }
+    }
+}

+ 32 - 6
app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java

@@ -23,7 +23,10 @@
 
 package com.owncloud.android.ui.fragment;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -54,6 +57,7 @@ import javax.inject.Inject;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -64,6 +68,8 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme
     private static final int MAX_ITEMS_PER_ROW = 10;
     private static final String FRAGMENT_TAG_BOTTOM_SHEET = "data";
 
+    public static final String REFRESH_SEARCH_EVENT_RECEIVER = "refreshSearchEventReceiver";
+
     private boolean photoSearchQueryRunning = false;
     private AsyncTask<Void, Void, GallerySearchTask.Result> photoSearchTask;
     private long endDate;
@@ -103,6 +109,28 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme
         } else {
             columnSize = maxColumnSizePortrait;
         }
+
+        registerRefreshSearchEventReceiver();
+    }
+
+    private void registerRefreshSearchEventReceiver() {
+        IntentFilter filter = new IntentFilter(REFRESH_SEARCH_EVENT_RECEIVER);
+        LocalBroadcastManager.getInstance(requireContext()).registerReceiver(refreshSearchEventReceiver, filter);
+    }
+
+    private final BroadcastReceiver refreshSearchEventReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (getActivity() instanceof FileDisplayActivity fileDisplayActivity) {
+                fileDisplayActivity.startPhotoSearch(R.id.nav_gallery);
+            }
+        }
+    };
+
+    @Override
+    public void onDestroyView() {
+        LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(refreshSearchEventReceiver);
+        super.onDestroyView();
     }
 
     @Override
@@ -200,10 +228,9 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme
     @Override
     public void onResume() {
         super.onResume();
+
         setLoading(this.isPhotoSearchQueryRunning());
-        final FragmentActivity activity = getActivity();
-        if (activity instanceof FileDisplayActivity) {
-            FileDisplayActivity fileDisplayActivity = ((FileDisplayActivity) activity);
+        if (getActivity() instanceof FileDisplayActivity fileDisplayActivity) {
             fileDisplayActivity.updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_gallery));
             fileDisplayActivity.setMainFabVisible(false);
         }
@@ -247,7 +274,7 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme
             setEmptyListMessage(SearchType.GALLERY_SEARCH);
         }
 
-        if(!emptySearch) {
+        if (!emptySearch) {
             this.showAllGalleryItems();
         }
 
@@ -317,8 +344,7 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme
     }
 
     private void loadMoreWhenEndReached(@NonNull RecyclerView recyclerView, int dy) {
-        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
-            GridLayoutManager gridLayoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
+        if (recyclerView.getLayoutManager() instanceof GridLayoutManager gridLayoutManager) {
 
             // scroll down
             if (dy > 0 && !this.isPhotoSearchQueryRunning()) {

+ 28 - 22
app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -57,6 +57,7 @@ import com.owncloud.android.operations.SynchronizeFileOperation;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.fragment.GalleryFragment;
 import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.utils.MimeTypeUtil;
 
@@ -186,6 +187,33 @@ public class PreviewImageActivity extends FileActivity implements
         }
     }
 
+    @Override
+    public void onBackPressed() {
+        sendRefreshSearchEventBroadcast();
+        super.onBackPressed();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            sendRefreshSearchEventBroadcast();
+
+            if (isDrawerOpen()) {
+                closeDrawer();
+            } else {
+                backToDisplayActivity();
+            }
+            return true;
+        } else {
+            return onOptionsItemSelected(item);
+        }
+    }
+
+    private void sendRefreshSearchEventBroadcast() {
+        Intent intent = new Intent(GalleryFragment.REFRESH_SEARCH_EVENT_RECEIVER);
+        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+    }
+
     @Override
     public void onStart() {
         super.onStart();
@@ -307,28 +335,6 @@ public class PreviewImageActivity extends FileActivity implements
         super.onDestroy();
     }
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        boolean returnValue = false;
-
-        switch(item.getItemId()){
-        case android.R.id.home:
-            if (isDrawerOpen()) {
-                closeDrawer();
-            } else {
-                backToDisplayActivity();
-            }
-            returnValue = true;
-            break;
-        default:
-        	returnValue = super.onOptionsItemSelected(item);
-            break;
-        }
-
-        return returnValue;
-    }
-
-
     @Override
     protected void onResume() {
         super.onResume();

+ 3 - 0
app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java

@@ -437,6 +437,9 @@ public final class ErrorMessageAdapter {
             } else if (result.getCode() == ResultCode.ACCOUNT_NOT_THE_SAME) {
                 message = res.getString(R.string.auth_account_not_the_same);
 
+            } else if (result.getCode() == ResultCode.QUOTA_EXCEEDED) {
+                message = res.getString(R.string.upload_quota_exceeded);
+
             }
 
             else if (!TextUtils.isEmpty(result.getHttpPhrase())) {

+ 154 - 0
app/src/main/res/layout/account_removal_dialog.xml

@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Nextcloud Android client application
+
+ @author ZetaTom
+ Copyright (C) 2023 ZetaTom
+ Copyright (C) 2023 Nextcloud GmbH
+
+ 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 <https://www.gnu.org/licenses/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:padding="12dp"
+        android:paddingHorizontal="24dp">
+
+        <ImageView
+            android:id="@+id/user_icon"
+            android:layout_width="@dimen/user_icon_size"
+            android:layout_height="@dimen/user_icon_size"
+            android:layout_margin="6dp"
+            android:contentDescription="@string/avatar"
+            android:src="@drawable/ic_user" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6dp"
+            android:orientation="vertical">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/user_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="middle"
+                android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
+                tools:text="Alice Muster" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/account"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="middle"
+                tools:text="alice@cloud.nextcloud.com" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <com.google.android.material.divider.MaterialDivider
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:id="@+id/local_remove"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackground"
+        android:clickable="true"
+        android:focusable="true"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingHorizontal="24dp"
+        android:paddingVertical="8dp">
+
+        <androidx.appcompat.widget.AppCompatRadioButton
+            android:id="@+id/radio_local_remove"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            tools:checked="true" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/remove_local_account"
+                android:textAppearance="@style/TextAppearance.Material3.LabelLarge" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/remove_local_account_details" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/request_deletion"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackground"
+        android:clickable="true"
+        android:focusable="true"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingHorizontal="24dp"
+        android:paddingVertical="8dp"
+        android:visibility="gone"
+        tools:visibility="visible">
+
+        <androidx.appcompat.widget.AppCompatRadioButton
+            android:id="@+id/radio_request_deletion"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request_account_deletion"
+                android:textAppearance="@style/TextAppearance.Material3.LabelLarge" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request_account_deletion_details" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <com.google.android.material.divider.MaterialDivider
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>

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

@@ -655,12 +655,17 @@
     <string name="remove_e2e">يُمكنك إلغاء التشفير المحلي لهذا العميل</string>
     <string name="remove_e2e_message">يُمكنك إلغاء التشفير المحلي لهذا العميل. هذا الملف المُشفّر سوف يبقى على الخادوم لكن لن تتم مزامنته مع هذا الحاسوب بعد الآن.</string>
     <string name="remove_fail_msg">فشلت عملية الحذف</string>
+    <string name="remove_local_account">إزالة الحساب المحلي</string>
+    <string name="remove_local_account_details">إزِل الحساب من الجهاز و احذُف كل الملفات المحلية</string>
     <string name="remove_notification_failed">فشل في إزالة الإخطار.</string>
     <string name="remove_push_notification">حذف</string>
     <string name="remove_success_msg">تم حذفه</string>
     <string name="rename_dialog_title">أدخل اسما جديداً</string>
     <string name="rename_local_fail_msg">تعذرت إعادة تسمية النسخة المحلية ، حاول استخدام اسم مختلف</string>
     <string name="rename_server_fail_msg">إعادة التسمية غير ممكنة ، الاسم مأخوذ بالفعل</string>
+    <string name="request_account_deletion">طلب حذف حساب</string>
+    <string name="request_account_deletion_button">طلب الإزالة</string>
+    <string name="request_account_deletion_details">طلب الإزالة الدائمة للحساب من قِبل مُزوّد الخدمة</string>
     <string name="reshare_not_allowed">لا يسمح بعملية إعادة المشاركة</string>
     <string name="resharing_is_not_allowed">لا يسمح بعملية إعادة المشاركة</string>
     <string name="resized_image_not_possible_download">لا توجد صورة بحجمها متاح. تنزيل الصورة الكاملة؟</string>

+ 5 - 0
app/src/main/res/values-b+en+001/strings.xml

@@ -650,12 +650,17 @@
     <string name="remove_e2e">You can remove end-to-end encryption locally on this client</string>
     <string name="remove_e2e_message">You can remove end-to-end encryption locally on this client. The encrypted files will remain on server, but will not be synced to this computer any longer.</string>
     <string name="remove_fail_msg">Deletion failed</string>
+    <string name="remove_local_account">Remove local account</string>
+    <string name="remove_local_account_details">Remove account from device and delete all local files</string>
     <string name="remove_notification_failed">Failed to remove notification.</string>
     <string name="remove_push_notification">Remove</string>
     <string name="remove_success_msg">Deleted</string>
     <string name="rename_dialog_title">Enter a new name</string>
     <string name="rename_local_fail_msg">Could not rename local copy, try a different name</string>
     <string name="rename_server_fail_msg">Renaming not possible, name already taken</string>
+    <string name="request_account_deletion">Request account deletion</string>
+    <string name="request_account_deletion_button">Request deletion</string>
+    <string name="request_account_deletion_details">Request permanent deletion of account by service provider</string>
     <string name="reshare_not_allowed">Resharing is not allowed</string>
     <string name="resharing_is_not_allowed">Resharing is not allowed</string>
     <string name="resized_image_not_possible_download">No resized image available. Download full image?</string>

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

@@ -627,6 +627,7 @@
     <string name="rename_dialog_title">Въведете ново име</string>
     <string name="rename_local_fail_msg">Локалното копие не може да бъде преименувано; опитайте с друго име</string>
     <string name="rename_server_fail_msg">Сървърът не може да бъде преименуван</string>
+    <string name="request_account_deletion">Заявка за изтриване на профил</string>
     <string name="reshare_not_allowed">Повторното споделяне е забранено</string>
     <string name="resharing_is_not_allowed">Повторното споделяне е забранено</string>
     <string name="resized_image_not_possible_download">Няма налично оразмерено изображение. Да се свали ли изображението?</string>

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

@@ -518,6 +518,7 @@
     <string name="rename_dialog_title">Lakaat un anv nevez</string>
     <string name="rename_local_fail_msg">Dibosuple adenvel an eilenn diabarzh, klaskit un anv all</string>
     <string name="rename_server_fail_msg">Dibosuple adenvel, anv kemeret dija</string>
+    <string name="request_account_deletion">Goulenn lemel ar c\'hont</string>
     <string name="reshare_not_allowed">N\'eo ket aotreet an adrannan</string>
     <string name="resharing_is_not_allowed">N\'eo ket aotreet an adrannan</string>
     <string name="resized_image_not_possible_download">Skeudenn admentet ebet. Pellgargañ ar skeudenn glot ?</string>

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

@@ -618,6 +618,7 @@
     <string name="rename_dialog_title">Introduiu un nom nou</string>
     <string name="rename_local_fail_msg">No s\'ha pogut canviar el nom de la còpia local, intenteu-ho amb amb un altre nom</string>
     <string name="rename_server_fail_msg">El canvi de nom no és possible, el nom ja es troba en ús</string>
+    <string name="request_account_deletion">Sol·licitud de supressió de compte</string>
     <string name="reshare_not_allowed">No es pot tornar a compartir</string>
     <string name="resharing_is_not_allowed">No està permesa la re-compartició</string>
     <string name="resized_image_not_possible_download">No hi ha imatge redimensionada disponible. Voleu baixar la imatge a la resolució original?</string>

+ 5 - 0
app/src/main/res/values-cs-rCZ/strings.xml

@@ -650,12 +650,17 @@
     <string name="remove_e2e">Je možné šifrování mezi koncovými body odebrat lokálně na tomto klientovi</string>
     <string name="remove_e2e_message">Je možné šifrování mezi koncovými body odebrat lokálně na tomto klientovi. Šifrované soubory zůstanou na serveru, ale už nadále nebudou synchronizovány s tímto počítačem.</string>
     <string name="remove_fail_msg">Odstranění se nezdařilo</string>
+    <string name="remove_local_account">Odebrat místní účet</string>
+    <string name="remove_local_account_details">Odebrat účet ze zařízení a smazat veškeré místní soubory</string>
     <string name="remove_notification_failed">Upozornění se nepodařilo odebrat.</string>
     <string name="remove_push_notification">Odebrat</string>
     <string name="remove_success_msg">Smazáno</string>
     <string name="rename_dialog_title">Zadejte nový název</string>
     <string name="rename_local_fail_msg">Nepodařilo se přejmenovat místní kopii – zkuste použít jiný název</string>
     <string name="rename_server_fail_msg">Přejmenování není možné – název už je použit</string>
+    <string name="request_account_deletion">Vyžádat smazání účtu</string>
+    <string name="request_account_deletion_button">Vyžádat si smazání</string>
+    <string name="request_account_deletion_details">Vyžádat si trvalé vymazání účtu u poskytovatele služby</string>
     <string name="reshare_not_allowed">Příjemcům tohoto sdílení není dovoleno ho nasdílet dál dalším</string>
     <string name="resharing_is_not_allowed">Příjemcům tohoto sdílení není dovoleno ho nasdílet dál dalším</string>
     <string name="resized_image_not_possible_download">Zmenšená verze obrázku není k dispozici. Stáhnout obrázek v plné velikosti?</string>

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

@@ -656,6 +656,7 @@ Enheds legitimationsoplysninger er sat op
     <string name="rename_dialog_title">Indtast et nyt navn</string>
     <string name="rename_local_fail_msg">Lokal kopi kunne ikke omdøbes, prøv et andet navn</string>
     <string name="rename_server_fail_msg">Omdøbning ikke mulig, navnet eksisterer</string>
+    <string name="request_account_deletion">Anmodning om sletning af konto</string>
     <string name="reshare_not_allowed">Gendeling er ikke tilladt</string>
     <string name="resharing_is_not_allowed">Videredeling ikke tilladt</string>
     <string name="resized_image_not_possible_download">Intet skaleret billede tilgængeligt. Hent fuldt billede?</string>

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

@@ -650,12 +650,17 @@
     <string name="remove_e2e">Sie können die Ende-zu-Ende-Verschlüsselung lokal auf diesem Client entfernen</string>
     <string name="remove_e2e_message">Sie können die Ende-zu-Ende-Verschlüsselung lokal auf diesem Client entfernen. Die verschlüsselten Dateien bleiben auf dem Server, werden aber nicht mehr mit diesem Computer synchronisiert.</string>
     <string name="remove_fail_msg">Löschung fehlgeschlagen</string>
+    <string name="remove_local_account">Lokales Konto entfernen</string>
+    <string name="remove_local_account_details">Das Konto vom Gerät entfernen und alle lokalen Dateien löschen</string>
     <string name="remove_notification_failed">Entfernen der Benachrichtigungen fehlgeschlagen.</string>
     <string name="remove_push_notification">Entfernen</string>
     <string name="remove_success_msg">Gelöscht</string>
     <string name="rename_dialog_title">Geben Sie einen neuen Namen ein</string>
     <string name="rename_local_fail_msg">Die lokale Kopie konnte nicht umbenannt werden. Versuche es mit einem anderen Namen.</string>
     <string name="rename_server_fail_msg">Umbenennen nicht möglich. Der Name wird bereits verwendet.</string>
+    <string name="request_account_deletion">Kontolöschung anfordern</string>
+    <string name="request_account_deletion_button">Löschung anfordern</string>
+    <string name="request_account_deletion_details">Beim Diensteanbieter die dauerhafte Löschung des Kontos anfordern</string>
     <string name="reshare_not_allowed">Das Weiterverteilen ist nicht erlaubt </string>
     <string name="resharing_is_not_allowed">Resharing/Wiederteilen ist nicht erlaubt.</string>
     <string name="resized_image_not_possible_download">Kein verkleinertes Bild verfügbar. Vollbild herunterladen?</string>

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

@@ -633,6 +633,7 @@
     <string name="rename_dialog_title">Εισάγετε νέο όνομα</string>
     <string name="rename_local_fail_msg">Αδυναμία μετονομασίας τοπικού αντιγράφου, δοκιμάστε διαφορετικό όνομα</string>
     <string name="rename_server_fail_msg">Αδύνατη μετονομασία, το όνομα έχει ήδη δοθεί</string>
+    <string name="request_account_deletion">Αίτηση διαγραφής λογαριασμού</string>
     <string name="reshare_not_allowed">Δεν επιτρέπεται ο επαναμοιρασμός</string>
     <string name="resharing_is_not_allowed">Δεν επιτρέπεται ο διαμοιρασμός</string>
     <string name="resized_image_not_possible_download">Δεν υπάρχει διαθέσιμη εικόνα με αλλαγμένο μέγεθος. Κατέβασμα της πλήρους εικόνας;</string>

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

@@ -457,6 +457,7 @@
     <string name="rename_dialog_title">Enigu novan nomon</string>
     <string name="rename_local_fail_msg">Ne eblis alinomi lokan kopion, provu uzi alian nomon</string>
     <string name="rename_server_fail_msg">Ne eblis alinomi: la nomo jam uziĝas</string>
+    <string name="request_account_deletion">Peti kontoforigadon</string>
     <string name="reshare_not_allowed">Rekunhavigo ne estas permesata</string>
     <string name="resharing_is_not_allowed">Re-kunhavigi ne estas permesita</string>
     <string name="resized_image_not_possible_download">Neniu regrandigita bildo disponeblas. Ĉu elŝuti originan bildon?</string>

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

@@ -635,6 +635,7 @@
     <string name="rename_dialog_title">Ingresa un nombre nuevo</string>
     <string name="rename_local_fail_msg">No se pudo renombrar la copia local, intenta con un nombre diferente </string>
     <string name="rename_server_fail_msg">No fue posible renombrar, el nombre ya está ocupado</string>
+    <string name="request_account_deletion">Solicitar eliminación de cuenta</string>
     <string name="reshare_not_allowed">No se permite volver a compartir</string>
     <string name="resharing_is_not_allowed">No está permitido recompartir</string>
     <string name="resized_image_not_possible_download">No hay una imagen a escala disponible. ¿Descargar la imagen completa? </string>

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

@@ -474,6 +474,7 @@
     <string name="rename_dialog_title">Ingresa un nombre nuevo</string>
     <string name="rename_local_fail_msg">No se pudo renombrar la copia local, intenta con un nombre diferente </string>
     <string name="rename_server_fail_msg">No fue posible renombrar, el nombre ya está ocupado</string>
+    <string name="request_account_deletion">Solicitar borrado de cuenta</string>
     <string name="reshare_not_allowed">No está permitido recompartir</string>
     <string name="resharing_is_not_allowed">No está permitido recompartir</string>
     <string name="resized_image_not_possible_download">No hay una imagen a escala disponible. ¿Descargar la imagen completa? </string>

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

@@ -656,6 +656,7 @@
     <string name="rename_dialog_title">Introduce un nombre nuevo</string>
     <string name="rename_local_fail_msg">No se ha podido cambiar el nombre de la copia local, prueba un nombre diferente</string>
     <string name="rename_server_fail_msg">No se ha podido dar un nombre nuevo, el nombre ya está tomado</string>
+    <string name="request_account_deletion">Solicitar la eliminación de la cuenta</string>
     <string name="reshare_not_allowed">No se permite volver a compartir</string>
     <string name="resharing_is_not_allowed">No se permite compartir de nuevo</string>
     <string name="resized_image_not_possible_download">No se dispone de la imagen en otros tamaños. ¿Descargar en tamaño completo?</string>

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

@@ -653,6 +653,7 @@
     <string name="rename_dialog_title">Idatzi izen berri bat</string>
     <string name="rename_local_fail_msg">Ezin izan da kopia lokala berrizendatu, saiatu beste izen batekin</string>
     <string name="rename_server_fail_msg">Ezin izan da berrizendatu, izena hartua zegoen</string>
+    <string name="request_account_deletion">Eskatu kontuaren ezabaketa</string>
     <string name="reshare_not_allowed">Birpartekatzea ez da onartzen</string>
     <string name="resharing_is_not_allowed">Birpartekatzea ez da onartzen</string>
     <string name="resized_image_not_possible_download">Ez dago tamainaz aldatutako irudirik eskuragarri. Irudi osoa deskargatu nahi duzu?</string>

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

@@ -653,6 +653,7 @@
     <string name="rename_dialog_title">نام جدید وارد کنید</string>
     <string name="rename_local_fail_msg">تغییر نام نسخه محلی امکان پذیر نیست ، نام دیگری را امتحان کنید</string>
     <string name="rename_server_fail_msg">تغییر نام ممکن نیست، این نام قبلا استفاده شده است</string>
+    <string name="request_account_deletion">درخواست حذف اکانت</string>
     <string name="reshare_not_allowed">اشتراک‌گذاری مجدد مجاز نمی باشد</string>
     <string name="resharing_is_not_allowed">اشتراک‌گذاری مجدد مجاز نمی باشد</string>
     <string name="resized_image_not_possible_download">هیچ تصویری تغییر اندازه شده‌ای در دسترس نیست. تصویر کامل را بارگیری می کنید؟</string>

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

@@ -622,6 +622,7 @@ GNU yleinen lisenssi, versio 2</string>
     <string name="rename_dialog_title">Anna uusi nimi</string>
     <string name="rename_local_fail_msg">Paikallisen kopion nimeä ei voitu muuttaa, yritä eri nimeä</string>
     <string name="rename_server_fail_msg">Nimen muutos epäonnistui, nimi on jo käytössä</string>
+    <string name="request_account_deletion">Pyydä tilin poistoa</string>
     <string name="reshare_not_allowed">Uudellenjako ei ole salluttu</string>
     <string name="resharing_is_not_allowed">Uudelleenjakaminen ei ole sallittu</string>
     <string name="resized_image_not_possible_download">Uuden kokoinen kuva ei ole saatavilla. Ladataanko täysikokoinen kuva?</string>

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

@@ -651,12 +651,15 @@ Attention, la suppression est irréversible.</string>
     <string name="remove_e2e">Vous pouvez retirer le chiffrement de bout en bout localement sur ce client</string>
     <string name="remove_e2e_message">Vous pouvez retirer le chiffrement de bout en bout localement sur ce client. Les fichiers chiffrés resteront sur le serveur, mais ne seront plus synchronisés avec cet ordinateur.</string>
     <string name="remove_fail_msg">Échec de la suppression</string>
+    <string name="remove_local_account">Supprimer le compte local</string>
+    <string name="remove_local_account_details">Supprimer le compte de l\'appareil et supprimer tous les fichiers locaux</string>
     <string name="remove_notification_failed">Erreur lors de la suppression des notifications.</string>
     <string name="remove_push_notification">Supprimer</string>
     <string name="remove_success_msg">Supprimé</string>
     <string name="rename_dialog_title">Entrez un nouveau nom</string>
     <string name="rename_local_fail_msg">Impossible de renommer la version locale; veuillez réessayer avec un nom différent</string>
     <string name="rename_server_fail_msg">Impossible de renommer, le nom est déjà utilisé</string>
+    <string name="request_account_deletion">Demander la suppression du compte</string>
     <string name="reshare_not_allowed">Le repartage n\'est pas autorisé</string>
     <string name="resharing_is_not_allowed">Le repartage est interdit</string>
     <string name="resized_image_not_possible_download">Aucune image réduite disponible. Télécharger l\'image originale ?</string>

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

@@ -651,12 +651,17 @@
     <string name="remove_e2e">Pode retirar o cifrado de extremo a extremo localmente neste cliente</string>
     <string name="remove_e2e_message">Pode retirar o cifrado de extremo a extremo localmente neste cliente. Os ficheiros cifrados permanecerán no servidor, mais xa non se sincronizarán con este computador.</string>
     <string name="remove_fail_msg">Produciuse un fallo na eliminación</string>
+    <string name="remove_local_account">Retirar a conta local</string>
+    <string name="remove_local_account_details">Retirar a conta do dispositivo e eliminar todos os ficheiros locais</string>
     <string name="remove_notification_failed">Produciuse un fallo ao retirar a notificación.</string>
     <string name="remove_push_notification">Retirar</string>
     <string name="remove_success_msg">Eliminado</string>
     <string name="rename_dialog_title">Introduza un nome novo</string>
     <string name="rename_local_fail_msg">Non foi posíbel renomear a copia local, ténteo cun un nome diferente</string>
     <string name="rename_server_fail_msg">Non foi posíbel renomear, o nome xa está ocupado</string>
+    <string name="request_account_deletion">Solicitar a eliminación da conta </string>
+    <string name="request_account_deletion_button">Solicitat a eliminación</string>
+    <string name="request_account_deletion_details">Solicitar a eliminación definitiva da conta polo provedor de servizos</string>
     <string name="reshare_not_allowed">Non se permite volver compartir</string>
     <string name="resharing_is_not_allowed">Non se permite volver compartir</string>
     <string name="resized_image_not_possible_download">No se dispón da imaxe noutros tamaños. Descargala a tamaño real?</string>

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

@@ -596,6 +596,7 @@
     <string name="rename_dialog_title">Unesite novi naziv</string>
     <string name="rename_local_fail_msg">Nije moguće preimenovati lokalnu kopiju, pokušajte s drugim nazivom</string>
     <string name="rename_server_fail_msg">Preimenovanje nije moguće, naziv je već zauzet</string>
+    <string name="request_account_deletion">Zatraži brisanje računa</string>
     <string name="reshare_not_allowed">Ponovno dijeljenje nije dopušteno</string>
     <string name="resharing_is_not_allowed">Ponovno dijeljenje nije dopušteno</string>
     <string name="resized_image_not_possible_download">Nema dostupne slike promijenjene veličine. Preuzeti punu sliku?</string>
@@ -732,8 +733,8 @@
     <string name="stream_not_possible_message">Umjesto toga preuzmite medijske datoteke ili se koristite vanjskom aplikacijom.</string>
     <string name="strict_mode">Ograničeni način rada: nije dopuštena HTTP veza!</string>
     <string name="sub_folder_rule_year">Godina</string>
-    <string name="subject_shared_with_you">\\"%1$s\\" je dijeljen s vama</string>
-    <string name="subject_user_shared_with_you">%1$s dijeli \\"%2$s\\" s vama</string>
+    <string name="subject_shared_with_you">\"%1$s\" je dijeljen s vama</string>
+    <string name="subject_user_shared_with_you">%1$s dijeli \"%2$s\" s vama</string>
     <string name="subtitle_photos_only">Samo fotografije</string>
     <string name="subtitle_photos_videos">Fotografije &amp; video materijali</string>
     <string name="subtitle_videos_only">Samo video materijali</string>

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

@@ -654,6 +654,7 @@
     <string name="rename_dialog_title">Adjon meg egy új nevet</string>
     <string name="rename_local_fail_msg">A helyi másolat nem nevezhető át, próbáljon meg egy másik nevet</string>
     <string name="rename_server_fail_msg">Nem nevezhető át, ez a név már foglalt</string>
+    <string name="request_account_deletion">Fióktörlés kérése</string>
     <string name="reshare_not_allowed">Az újra megosztás nem engedélyezett</string>
     <string name="resharing_is_not_allowed">Az újra megosztás nem engedélyezett</string>
     <string name="resized_image_not_possible_download">Nincs elérhető átméretezett kép. Letölti a teljes képet?</string>

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

@@ -541,6 +541,7 @@
     <string name="rename_dialog_title">Settu inn nýtt nafn</string>
     <string name="rename_local_fail_msg">Gat endurnefnt staðvært afrit, prófaðu annað heiti</string>
     <string name="rename_server_fail_msg">Ekki hægt að endurnefna, heitið er frátekið</string>
+    <string name="request_account_deletion">Biðja um að aðgangi sé eytt</string>
     <string name="reshare_not_allowed">Endurdeiling er ekki leyfð</string>
     <string name="resharing_is_not_allowed">Endurdeiling er ekki leyfð</string>
     <string name="resized_image_not_possible_download">Engin stærðarbreytt mynd í boði. Sækja alla myndina?</string>

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

@@ -18,6 +18,7 @@
     <string name="actionbar_copy">Copia</string>
     <string name="actionbar_mkdir">Nuova cartella</string>
     <string name="actionbar_move">Sposta</string>
+    <string name="actionbar_move_or_copy">Sposta o copia</string>
     <string name="actionbar_open_with">Apri con</string>
     <string name="actionbar_search">Cerca</string>
     <string name="actionbar_see_details">Dettagli</string>
@@ -159,6 +160,7 @@
     <string name="conflict_message_description">Se selezioni entrambe le versioni, il file locale ha un numero aggiunto al suo nome.</string>
     <string name="conflict_server_file">File su server</string>
     <string name="contact_backup_title">Backup contatti</string>
+    <string name="contact_no_permission">Autorizzazione Contatti richiesta</string>
     <string name="contactlist_item_icon">Icona utente della lista contatti</string>
     <string name="contactlist_no_permission">Nessun permesso specificato, nessun elemento importato.</string>
     <string name="contacts">Contatti</string>
@@ -257,6 +259,7 @@
     <string name="ecosystem_apps_more">Altre app di Nextcloud</string>
     <string name="ecosystem_apps_notes">Nextcloud Notes</string>
     <string name="ecosystem_apps_talk">Nextcloud Talk</string>
+    <string name="email_pick_failed">Impossibile scegliere l\'indirizzo di posta.</string>
     <string name="encrypted">Imposta come cifrato</string>
     <string name="end_to_end_encryption_confirm_button">Configura la cifratura</string>
     <string name="end_to_end_encryption_decrypting">Decifratura in corso…</string>
@@ -647,12 +650,17 @@
     <string name="remove_e2e">Su questo client puoi rimuovere localmente la cifratura end-to-end</string>
     <string name="remove_e2e_message">Su questo client puoi rimuovere localmente la cifratura end-to-end. I file cifrati rimarranno sul server, ma non verranno più sincronizzati su questo computer.</string>
     <string name="remove_fail_msg">Eliminazione non riuscita</string>
+    <string name="remove_local_account">Rimuovi account locale</string>
+    <string name="remove_local_account_details">Rimuovi account dal dispositivo e elimina tutti i file locali</string>
     <string name="remove_notification_failed">Rimozione notifica non riuscita.</string>
     <string name="remove_push_notification">Rimuovi</string>
     <string name="remove_success_msg">Eliminato</string>
     <string name="rename_dialog_title">Digita un nuovo nome</string>
     <string name="rename_local_fail_msg">La copia locale non può essere rinominata, prova un nome diverso</string>
     <string name="rename_server_fail_msg">Rinomina non consentita, nome già utilizzato</string>
+    <string name="request_account_deletion">Richiesta di eliminazione account</string>
+    <string name="request_account_deletion_button">Richiedi eliminazione</string>
+    <string name="request_account_deletion_details">Richiedi l\'eliminazione permanente dell\'account al fornitore del servizio</string>
     <string name="reshare_not_allowed">La ri-condivisione non è consentita</string>
     <string name="resharing_is_not_allowed">La ri-condivisione non è consentita</string>
     <string name="resized_image_not_possible_download">Immagine ridimensionata non disponibile. Scaricare l\'immagine completa?</string>
@@ -944,7 +952,9 @@
     <string name="wait_a_moment">Attendi…</string>
     <string name="wait_checking_credentials">Controllo delle credenziali memorizzate</string>
     <string name="wait_for_tmp_copy_from_private_storage">Copia file dall\'archiviazione privata</string>
+    <string name="webview_version_check_alert_dialog_message">Aggiorna l\'app System WebView di Android per accedere</string>
     <string name="webview_version_check_alert_dialog_positive_button_title">Aggiorna</string>
+    <string name="webview_version_check_alert_dialog_title">Aggiorna System WebView di Android</string>
     <string name="what_s_new_image">Immagine Cosa c\'è di nuovo</string>
     <string name="whats_new_skip">Salta</string>
     <string name="whats_new_title">Prima volta su %1$s</string>

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

@@ -540,6 +540,7 @@
     <string name="rename_dialog_title">נא להזין שם חדש</string>
     <string name="rename_local_fail_msg">לא ניתן לשנות שם של עותק מקומי, נא לנסות שם אחר</string>
     <string name="rename_server_fail_msg">אי אפשר לשנות שם, השם כבר תפוס</string>
+    <string name="request_account_deletion">בקשת מחיקת חשבון</string>
     <string name="reshare_not_allowed">שיתוף מחדש אסור</string>
     <string name="resharing_is_not_allowed">אסור לשתף מחדש</string>
     <string name="resized_image_not_possible_download">אין תמונה מוקטנת. להוריד את התמונה המלאה?</string>

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

@@ -641,6 +641,7 @@
     <string name="rename_dialog_title">新しい名前を入力</string>
     <string name="rename_local_fail_msg">ローカルコピーの名前が変更できません。別の名前を試してください</string>
     <string name="rename_server_fail_msg">名前の変更ができません。その名前は使われています</string>
+    <string name="request_account_deletion">アカウントの削除をリクエストする</string>
     <string name="reshare_not_allowed">再共有は許可されていません</string>
     <string name="resharing_is_not_allowed">再共有は許可されていません</string>
     <string name="resized_image_not_possible_download">サイズ変更された画像はありません。 フルイメージをダウンロードしますか?</string>

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

@@ -634,6 +634,7 @@
     <string name="rename_dialog_title">새 이름 입력</string>
     <string name="rename_local_fail_msg">로컬 파일의 이름을 변경할 수 없습니다. 다른 이름을 입력하십시오</string>
     <string name="rename_server_fail_msg">이름을 변경할 수 없음. 이름이 이미 사용중입니다.</string>
+    <string name="request_account_deletion">계정 삭제 요청</string>
     <string name="reshare_not_allowed">다시 공유할 수 없음</string>
     <string name="resharing_is_not_allowed">다시 공유할 수 없습니다</string>
     <string name="resized_image_not_possible_download">크기 조정된 사진을 사용할 수 없습니다. 원본 크기로 다운로드하시겠습니까?</string>

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

@@ -581,6 +581,7 @@
     <string name="rename_dialog_title">Įveskite naują pavadinimą</string>
     <string name="rename_local_fail_msg">Nepavyko pervadinti vietinės kopijos, pabandykite kitą pavadinimą</string>
     <string name="rename_server_fail_msg">Pervadinimas neįmanomas, pavadinimas jau užimtas</string>
+    <string name="request_account_deletion">Užklausti paskyros ištrynimo</string>
     <string name="reshare_not_allowed">Bendrinimas iš naujo yra neleidžiamas</string>
     <string name="resharing_is_not_allowed">Bendrinimas iš naujo yra neleidžiamas</string>
     <string name="resized_image_not_possible_download">Nėra prieinamas pakeisto dydžio paveikslas. Atsisiųsti pilno dydžio paveikslą?</string>

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

@@ -381,6 +381,7 @@
     <string name="rename_dialog_title">Ievadīt jaunu nosaukumu</string>
     <string name="rename_local_fail_msg">Nevarēja pārsaukt lokālo kopiju, pamēģiniet citu nosaukumu</string>
     <string name="rename_server_fail_msg">Pārsaukšana nav iespējama, nosaukums jau izmantots</string>
+    <string name="request_account_deletion">Pieprasīt konta dzēšanu</string>
     <string name="reshare_not_allowed">Atkārtota koplietošana nav atļauta</string>
     <string name="resharing_is_not_allowed">Atkārtota koplietošana nav atļauta</string>
     <string name="restore">Atjaunot datni</string>

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

@@ -550,6 +550,7 @@
     <string name="rename_dialog_title">Внеси ново име</string>
     <string name="rename_local_fail_msg">Локалната копија не може да се преименува; пробајте со друго име</string>
     <string name="rename_server_fail_msg">Преименувањето не е возможно, тоа име веќе е зафатено</string>
+    <string name="request_account_deletion">Барање за бришење на сметката</string>
     <string name="reshare_not_allowed">Повторно споделување не е дозволено</string>
     <string name="resharing_is_not_allowed">Повторно споделување не е дозволено</string>
     <string name="resized_image_not_possible_download">Нема достапно смалена слика. Превземете ја во целосната големина?</string>

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

@@ -654,6 +654,7 @@
     <string name="rename_dialog_title">Skriv inn et nytt navn</string>
     <string name="rename_local_fail_msg">Kunne ikke endre navn på lokal kopi, prøv et annet navn</string>
     <string name="rename_server_fail_msg">Endring av navn ikke mulig, navnet er allerde benyttet</string>
+    <string name="request_account_deletion">Request account deletion</string>
     <string name="reshare_not_allowed">Videre deling er ikke tillatt</string>
     <string name="resharing_is_not_allowed">Videredeling er ikke tillatt</string>
     <string name="resized_image_not_possible_download">Inget mindre bilde tilgjengelig. Last ned fullversjon?</string>

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

@@ -607,6 +607,7 @@
     <string name="rename_dialog_title">Voer een nieuwe naam in</string>
     <string name="rename_local_fail_msg">Kon lokale kopie niet hernoemen, probeer een andere naam</string>
     <string name="rename_server_fail_msg">Hernoemen niet mogelijk, naam is al in gebruik</string>
+    <string name="request_account_deletion">Verwijdering van account aanvragen </string>
     <string name="reshare_not_allowed">Verder delen niet toegestaan</string>
     <string name="resharing_is_not_allowed">Verder delen is niet toegestaan</string>
     <string name="resized_image_not_possible_download">Geen verkleinde afbeelding beschikbaar. Volledige afbeelding downloaden?</string>

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

@@ -656,6 +656,7 @@
     <string name="rename_dialog_title">Wprowadź nową nazwę</string>
     <string name="rename_local_fail_msg">Nie można zmienić nazwy lokalnej kopii, użyj innej nazwy</string>
     <string name="rename_server_fail_msg">Nie można zmienić nazwy, nazwa jest już zajęta</string>
+    <string name="request_account_deletion">Zażądaj usunięcia konta</string>
     <string name="reshare_not_allowed">Udostępnianie dalej jest niedozwolone</string>
     <string name="resharing_is_not_allowed">Udostępnianie dalej jest niedozwolone</string>
     <string name="resized_image_not_possible_download">Brak obrazu o zmienionym rozmiarze. Pobrać pełny obraz?</string>

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

@@ -653,6 +653,7 @@
     <string name="rename_dialog_title">Digite um novo nome</string>
     <string name="rename_local_fail_msg">Não foi possível renomear a cópia local, tente um nome diferente</string>
     <string name="rename_server_fail_msg">Não foi possível renomear pois o nome já existe</string>
+    <string name="request_account_deletion">Solicitar a exclusão da conta</string>
     <string name="reshare_not_allowed">Recompartilhamento não é permitido</string>
     <string name="resharing_is_not_allowed">Recompartilhamento não é permitido</string>
     <string name="resized_image_not_possible_download">Não há imagem redimensionada disponível. Baixar a imagem completa?</string>

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

@@ -636,6 +636,7 @@
     <string name="rename_dialog_title">Introduza um novo nome</string>
     <string name="rename_local_fail_msg">Não foi possível renomear a cópia local, tente um nome diferente</string>
     <string name="rename_server_fail_msg">Renomeação impossível, nome já utilizado</string>
+    <string name="request_account_deletion">Solicitar eliminação da conta</string>
     <string name="reshare_not_allowed">Voltar a partilhar não é permitido</string>
     <string name="resharing_is_not_allowed">Não é permitido voltar a partilhar</string>
     <string name="resized_image_not_possible_download">Nenhuma imagem redimensionada disponível. Deseja transferir a imagem completa?</string>

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

@@ -628,6 +628,7 @@
     <string name="rename_dialog_title">Introduceţi un nou nume</string>
     <string name="rename_local_fail_msg">Nu s-a putut redenumi copia locală, încercați un alt nume</string>
     <string name="rename_server_fail_msg">Redenumirea nu este posibilă, numele este deja luat</string>
+    <string name="request_account_deletion">Cerere ștergere cont</string>
     <string name="reshare_not_allowed">Repartajarea nu este permisă</string>
     <string name="resharing_is_not_allowed">Repartajarea nu este permisă</string>
     <string name="resized_image_not_possible_download">Nu există o imagine mai mică. Descărcați imaginea completă?</string>

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

@@ -655,6 +655,7 @@
     <string name="rename_dialog_title">Введите новое имя</string>
     <string name="rename_local_fail_msg">Невозможно переименовать локальную копию, попробуйте другое имя</string>
     <string name="rename_server_fail_msg">Переименование невозможно, имя занято</string>
+    <string name="request_account_deletion">Запрос удаления аккаунта</string>
     <string name="reshare_not_allowed">Повторное открытие доступа запрещено</string>
     <string name="resharing_is_not_allowed">Повторное открытие доступа запрещено</string>
     <string name="resized_image_not_possible_download">Оптимизированное изображение отсутствует. Скачать изображение исходного размера?</string>

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

@@ -558,6 +558,7 @@
     <string name="rename_dialog_title">Inserta unu nùmene nou</string>
     <string name="rename_local_fail_msg">No at fatu a torrare a numenare sa còpia locale, proa cun un\'àteru nùmene</string>
     <string name="rename_server_fail_msg">Impossìbile a torrare a numenare, su nùmene esistit giai</string>
+    <string name="request_account_deletion">Rechede de cantzellare su contu</string>
     <string name="reshare_not_allowed">Non faghet a torrare a cumpartzire</string>
     <string name="resharing_is_not_allowed">Non faghet a torrare a cumpartzire</string>
     <string name="resized_image_not_possible_download">Impossìbile a modificare sa mannària de s\'immàgine. Dda boles iscarrigare intrea?</string>

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

@@ -613,6 +613,7 @@
     <string name="rename_dialog_title">Zadajte nové meno</string>
     <string name="rename_local_fail_msg">Nepodarilo sa premenovať lokálnu kópiu, skúste iné meno</string>
     <string name="rename_server_fail_msg">Premenovanie sa nepodarilo, meno sa už používa</string>
+    <string name="request_account_deletion">Žiadosť o zmazanie účtu</string>
     <string name="reshare_not_allowed">Opätovné sprístupňovanie nie je povolené</string>
     <string name="resharing_is_not_allowed">Sprístupnenie už sprístupnenej položky nie je povolené</string>
     <string name="resized_image_not_possible_download">Obrázok v inom rozlíšení nedostupný.  Stiahnuť pôvodný obrázok?</string>

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

@@ -656,6 +656,7 @@
     <string name="rename_dialog_title">Vnesite novo ime</string>
     <string name="rename_local_fail_msg">Ni mogoče preimenovati krajevne kopije. Poskusite vpisati drugačno ime.</string>
     <string name="rename_server_fail_msg">Preimenovanje ni mogoče. Ime je že uporabljeno.</string>
+    <string name="request_account_deletion">Zahtevaj izbris računa</string>
     <string name="reshare_not_allowed">Osveževanje ni dovoljena</string>
     <string name="resharing_is_not_allowed">Osveževanje ni dovoljeno</string>
     <string name="resized_image_not_possible_download">Slika prilagojene velikosti ni na voljo. Ali želite prejeti sliko polne velikosti?</string>

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

@@ -650,12 +650,17 @@
     <string name="remove_e2e">Шифрирање од почетка-до-краја можете локално да уклоните на овом клијенту</string>
     <string name="remove_e2e_message">Шифрирање од почетка-до-краја можете локално да уклоните на овом клијенту. Шифрирани фајлови ће остати на серверу, али се више неће синхронизовати са овим компјутером.</string>
     <string name="remove_fail_msg">Брисање није успело</string>
+    <string name="remove_local_account">Уклони локални налог</string>
+    <string name="remove_local_account_details">Укњања локални налог са уређаја у брише све локалне фајлове</string>
     <string name="remove_notification_failed">Грешка при уклањању обавештења.</string>
     <string name="remove_push_notification">Уклони</string>
     <string name="remove_success_msg">Обрисано</string>
     <string name="rename_dialog_title">Унесите нов назив</string>
     <string name="rename_local_fail_msg">Не могу да преименујем локалну копију. Пробајте други назив</string>
     <string name="rename_server_fail_msg">Преименовање није могуће. Назив већ постоји</string>
+    <string name="request_account_deletion">Захтевајте брисање налога</string>
+    <string name="request_account_deletion_button">Захтев за брисање</string>
+    <string name="request_account_deletion_details">Захтев пружаоцу сервиса да неповратно обрише налог</string>
     <string name="reshare_not_allowed">Поновно дељење није дозвољено</string>
     <string name="resharing_is_not_allowed">Поновно дељење није дозвољено</string>
     <string name="resized_image_not_possible_download">Нема доступне умањене сличице. Скинути пуну слику?</string>

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

@@ -651,12 +651,17 @@
     <string name="remove_e2e">Du kan ta bort end-to-end-kryptering lokalt på denna klient</string>
     <string name="remove_e2e_message">Du kan ta bort end-to-end-kryptering lokalt på denna klienten. Krypterade filer kommer finnas kvar på servern, men kommer inte synkroniseras med denna enheten längre.</string>
     <string name="remove_fail_msg">Radering misslyckades</string>
+    <string name="remove_local_account">Ta bort lokalt konto</string>
+    <string name="remove_local_account_details">Ta bort kontot från enheten och radera alla lokala filer</string>
     <string name="remove_notification_failed">Kunde inte radera notifiering.</string>
     <string name="remove_push_notification">Ta bort</string>
     <string name="remove_success_msg">Borttagen</string>
     <string name="rename_dialog_title">Ange ett nytt namn</string>
     <string name="rename_local_fail_msg">Det gick inte att byta namn på lokal kopia, prova ett annat namn</string>
     <string name="rename_server_fail_msg">Namnbytning är inte möjligt, namnet är redan taget</string>
+    <string name="request_account_deletion">Begär att radera ditt konto</string>
+    <string name="request_account_deletion_button">Begäran om radering</string>
+    <string name="request_account_deletion_details">Begär permanent borttagning av konto av tjänsteleverantör</string>
     <string name="reshare_not_allowed">Dela vidare är inte tillåtet</string>
     <string name="resharing_is_not_allowed">Att dela på nytt är inte tillåtet</string>
     <string name="resized_image_not_possible_download">Ingen storleksändrad bild tillgänglig. Hämta hela bilden?</string>

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

@@ -493,6 +493,7 @@
     <string name="remove_push_notification">ลบออก</string>
     <string name="remove_success_msg">ลบแล้ว</string>
     <string name="rename_dialog_title">กรอกชื่อใหม่</string>
+    <string name="request_account_deletion">ขอลบบัญชี</string>
     <string name="reshare_not_allowed">ไม่อนุญาตให้แชร์ซ้ำ</string>
     <string name="resharing_is_not_allowed">ไม่อนุญาตให้แชร์ซ้ำ</string>
     <string name="scanQR_description">เข้าสู่ระบบผ่านคิวอาร์โค้ด</string>

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

@@ -650,12 +650,14 @@
     <string name="remove_e2e">Bu istemciden uçtan uca şifrelemeyi yerel olarak kaldırabilirsiniz</string>
     <string name="remove_e2e_message">Bu istemcide yerel olarak uçtan uca şifrelemeyi kaldırabilirsiniz. Şifrelenmiş dosyalar sunucuda kalır, ancak artık bu bilgisayarla eşitlenmez.</string>
     <string name="remove_fail_msg">Silinemedi</string>
+    <string name="remove_local_account">Yerel hesabı sil</string>
     <string name="remove_notification_failed">Bildirim silinemedi.</string>
     <string name="remove_push_notification">Sil</string>
     <string name="remove_success_msg">Silindi</string>
     <string name="rename_dialog_title">Yeni bir ad yazın</string>
     <string name="rename_local_fail_msg">Yerel kopyanın adı değiştirilemedi. Lütfen başka bir ad deneyin</string>
     <string name="rename_server_fail_msg">Ad zaten kullanıldığından yeniden adlandırılamadı</string>
+    <string name="request_account_deletion">Hesabınızın silinmesini isteyin</string>
     <string name="reshare_not_allowed">Yeniden paylaşıma izin verilmiyor</string>
     <string name="resharing_is_not_allowed">Yeniden paylaşıma izin verilmiyor</string>
     <string name="resized_image_not_possible_download">Boyutu değiştirilmiş bir görsel yok. Tam boyuttaki görsel indirilsin mi?</string>

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

@@ -640,12 +640,17 @@
     <string name="remove_e2e">Ви можете вилучити наскрізне шифрування на пристрої на цьому клієнті</string>
     <string name="remove_e2e_message">Ви можете вилучити наскрізне шифрування на пристрої на цьому клієнті. Зашифровані файли залишатимуться на сервері, але більше не синхронізуватимуться з цим комп\'ютером.</string>
     <string name="remove_fail_msg">Помилка під час вилучення</string>
+    <string name="remove_local_account">Вилучити обліковий запис на пристрої</string>
+    <string name="remove_local_account_details">Вилучити обліковий запис з пристрою та всі файли на пристрої</string>
     <string name="remove_notification_failed">Неможливо зняти сповіщення.</string>
     <string name="remove_push_notification">Вилучити</string>
     <string name="remove_success_msg">Вилучено</string>
     <string name="rename_dialog_title">Введіть нове ім\'я</string>
     <string name="rename_local_fail_msg">Неможливо перейменувати локальну копію, спробуйте інше ім\'я</string>
     <string name="rename_server_fail_msg">Неможливо перейменувати, таке ім\'я уже існує</string>
+    <string name="request_account_deletion">Запит на вилучення облікового запису</string>
+    <string name="request_account_deletion_button">Запит на вилучення</string>
+    <string name="request_account_deletion_details">Запит на вилучення облікового запису з боку постачальника послуг</string>
     <string name="reshare_not_allowed">Надання спільного доступу іншим не дозволяється</string>
     <string name="resharing_is_not_allowed">Надання спільного доступу іншим не дозволяється</string>
     <string name="resized_image_not_possible_download">Відсутнє зображення зі зменшеними розмірами. Звантажити повне зображення?</string>

+ 5 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -650,12 +650,17 @@
     <string name="remove_e2e">您可以在该客户端上本地删除端对端加密</string>
     <string name="remove_e2e_message">您可以在该客户端上本地删除端对端加密。加密文件将保留在服务器上,但不会再同步到该电脑。</string>
     <string name="remove_fail_msg">删除失败</string>
+    <string name="remove_local_account">移除本地帐户</string>
+    <string name="remove_local_account_details">从设备中移除账户并删除所有本地文件</string>
     <string name="remove_notification_failed">移除通知失败</string>
     <string name="remove_push_notification">移除</string>
     <string name="remove_success_msg">已删除</string>
     <string name="rename_dialog_title">输入一个新的名称</string>
     <string name="rename_local_fail_msg">本地副本无法重命名,尝试其他名称</string>
     <string name="rename_server_fail_msg">无法重命名,名字已经被占用</string>
+    <string name="request_account_deletion">请求删除账号</string>
+    <string name="request_account_deletion_button">请求删除</string>
+    <string name="request_account_deletion_details">请求服务商永久删除账户</string>
     <string name="reshare_not_allowed">不允许再次共享</string>
     <string name="resharing_is_not_allowed">不允许二次共享</string>
     <string name="resized_image_not_possible_download">重整大小的图片不可用。下载完整图片?</string>

+ 5 - 0
app/src/main/res/values-zh-rHK/strings.xml

@@ -650,12 +650,17 @@
     <string name="remove_e2e">您可以在此客戶端上本地刪除端到端加密</string>
     <string name="remove_e2e_message">您可以在此客戶端上本地刪除端到端加密。加密檔案將保留在伺服器上,但不會再同步到此電腦。</string>
     <string name="remove_fail_msg">刪除失敗</string>
+    <string name="remove_local_account">移除近端賬戶</string>
+    <string name="remove_local_account_details">從裝置中刪除賬戶並刪除所有近端檔案</string>
     <string name="remove_notification_failed">移除通知失敗</string>
     <string name="remove_push_notification">移除</string>
     <string name="remove_success_msg">已刪除</string>
     <string name="rename_dialog_title">輸入新名稱</string>
     <string name="rename_local_fail_msg">無法重新命名近端複本,請試用不同名稱</string>
     <string name="rename_server_fail_msg">不可重新命名,名稱已存在</string>
+    <string name="request_account_deletion">請求賬戶刪除</string>
+    <string name="request_account_deletion_button">請求刪除</string>
+    <string name="request_account_deletion_details">請求服務提供者永久刪除賬戶</string>
     <string name="reshare_not_allowed">不允許重新分享</string>
     <string name="resharing_is_not_allowed">不允許重新分享</string>
     <string name="resized_image_not_possible_download">沒有可用調整過的圖像,下載完整圖像?</string>

+ 5 - 0
app/src/main/res/values-zh-rTW/strings.xml

@@ -650,12 +650,17 @@
     <string name="remove_e2e">您可以在此客戶端上本機移除端到端加密</string>
     <string name="remove_e2e_message">您可以在此客戶端上移除本機端到端加密。已加密的檔案將會保留在伺服器上,但不會再同步到此電腦。</string>
     <string name="remove_fail_msg">刪除失敗</string>
+    <string name="remove_local_account">移除本機帳號</string>
+    <string name="remove_local_account_details">從裝置移除帳號並刪除所有本機檔案</string>
     <string name="remove_notification_failed">移除通知失敗</string>
     <string name="remove_push_notification">移除</string>
     <string name="remove_success_msg">已刪除</string>
     <string name="rename_dialog_title">輸入新名稱</string>
     <string name="rename_local_fail_msg">無法重新命名本機副本,請嘗試不同的名稱</string>
     <string name="rename_server_fail_msg">無法重新命名,該名稱已被使用</string>
+    <string name="request_account_deletion">請求刪除帳號</string>
+    <string name="request_account_deletion_button">請求刪除</string>
+    <string name="request_account_deletion_details">請求服務提供者永久刪除帳號</string>
     <string name="reshare_not_allowed">不允許重新分享</string>
     <string name="resharing_is_not_allowed">不允許重新分享</string>
     <string name="resized_image_not_possible_download">沒有可用調整過的圖片。下載完整圖片?</string>

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

@@ -129,8 +129,12 @@
     <string name="common_skip">Skip</string>
     <string name="common_copy">Copy</string>
     <string name="about_title">About</string>
+    <string name="remove_local_account">Remove local account</string>
+    <string name="remove_local_account_details">Remove account from device and delete all local files</string>
+    <string name="request_account_deletion">Request account deletion</string>
+    <string name="request_account_deletion_button">Request deletion</string>
+    <string name="request_account_deletion_details">Request permanent deletion of account by service provider</string>
     <string name="delete_account">Remove account</string>
-    <string name="delete_account_warning">Remove account %s and delete all local files?\n\nDeletion cannot be undone.</string>
     <string name="avatar">Avatar</string>
     <string name="active_user">Active user</string>
     <string name="upload_chooser_title">Upload from…</string>
@@ -827,6 +831,7 @@
     <string name="upload_sync_conflict">Sync conflict, please resolve manually</string>
     <string name="upload_cannot_create_file">Cannot create local file</string>
     <string name="upload_local_storage_not_copied">File could not be copied to local storage</string>
+    <string name="upload_quota_exceeded">Storage quota exceeded</string>
     <string name="host_not_available">Server not available</string>
     <string name="delete_entries">Delete entries</string>
     <string name="dismiss_notification_description">Dismiss notification</string>

+ 6 - 0
settings.gradle

@@ -8,3 +8,9 @@ include ':appscan'
 //        substitute module('com.github.nextcloud.android-common:ui') using project(':ui')
 //    }
 //}
+
+//includeBuild('../android-library') {
+//    dependencySubstitution {
+//        substitute module('com.github.nextcloud:android-library') using project(':library')
+//    }
+//}