Browse Source

Merge master (cant rebase due to multiple git conflicts)

Signed-off-by: alperozturk <alper_ozturk@proton.me>
alperozturk 1 year ago
parent
commit
3ee19ac189
100 changed files with 1121 additions and 825 deletions
  1. 9 1
      .drone.yml
  2. 1 1
      .github/workflows/assembleFlavors.yml
  3. 1 1
      .github/workflows/check.yml
  4. 1 1
      .github/workflows/detectWrongSettings.yml
  5. 1 1
      .github/workflows/qa.yml
  6. 1 1
      .github/workflows/screenShotTest.yml
  7. 1 1
      .github/workflows/unit-tests.yml
  8. 4 0
      app/build.gradle
  9. 10 0
      app/src/main/AndroidManifest.xml
  10. 31 14
      app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt
  11. 14 4
      app/src/main/java/com/nextcloud/client/files/downloader/FileTransferService.kt
  12. 16 0
      app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt
  13. 13 2
      app/src/main/java/com/nextcloud/client/media/PlayerService.kt
  14. 67 46
      app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt
  15. 6 1
      app/src/main/java/com/owncloud/android/db/UploadResult.java
  16. 12 1
      app/src/main/java/com/owncloud/android/files/services/FileDownloader.java
  17. 58 6
      app/src/main/java/com/owncloud/android/files/services/FileUploader.java
  18. 0 257
      app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java
  19. 249 0
      app/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.kt
  20. 3 4
      app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
  21. 8 6
      app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  22. 11 7
      app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  23. 89 70
      app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java
  24. 2 0
      app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
  25. 176 0
      app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt
  26. 3 0
      app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java
  27. 0 156
      app/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java
  28. 2 6
      app/src/main/java/com/owncloud/android/ui/events/AccountRemovedEvent.kt
  29. 3 4
      app/src/main/java/com/owncloud/android/ui/events/ChangeMenuEvent.kt
  30. 2 9
      app/src/main/java/com/owncloud/android/ui/events/CommentsEvent.kt
  31. 3 4
      app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.kt
  32. 0 37
      app/src/main/java/com/owncloud/android/ui/events/EncryptionEvent.java
  33. 30 0
      app/src/main/java/com/owncloud/android/ui/events/EncryptionEvent.kt
  34. 0 33
      app/src/main/java/com/owncloud/android/ui/events/FavoriteEvent.java
  35. 6 9
      app/src/main/java/com/owncloud/android/ui/events/FavoriteEvent.kt
  36. 0 35
      app/src/main/java/com/owncloud/android/ui/events/InitiateSyncedFolder.java
  37. 0 33
      app/src/main/java/com/owncloud/android/ui/events/MenuItemClickEvent.java
  38. 2 3
      app/src/main/java/com/owncloud/android/ui/events/TokenPushEvent.kt
  39. 22 0
      app/src/main/java/com/owncloud/android/ui/events/VCardToggleEvent.kt
  40. 1 1
      app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
  41. 8 4
      app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java
  42. 2 15
      app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java
  43. 32 6
      app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java
  44. 9 9
      app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  45. 1 1
      app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListFragment.java
  46. 28 22
      app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java
  47. 2 0
      app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
  48. 2 0
      app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java
  49. 2 0
      app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java
  50. 3 0
      app/src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java
  51. 2 1
      app/src/main/res/layout/receive_external_files.xml
  52. 6 0
      app/src/main/res/values-ar/strings.xml
  53. 5 0
      app/src/main/res/values-b+en+001/strings.xml
  54. 1 0
      app/src/main/res/values-bg-rBG/strings.xml
  55. 1 0
      app/src/main/res/values-br/strings.xml
  56. 1 0
      app/src/main/res/values-ca/strings.xml
  57. 6 0
      app/src/main/res/values-cs-rCZ/strings.xml
  58. 1 0
      app/src/main/res/values-da/strings.xml
  59. 6 0
      app/src/main/res/values-de/strings.xml
  60. 1 0
      app/src/main/res/values-el/strings.xml
  61. 1 0
      app/src/main/res/values-eo/strings.xml
  62. 1 0
      app/src/main/res/values-es-rEC/strings.xml
  63. 1 0
      app/src/main/res/values-es-rMX/strings.xml
  64. 1 0
      app/src/main/res/values-es/strings.xml
  65. 1 0
      app/src/main/res/values-eu/strings.xml
  66. 1 0
      app/src/main/res/values-fa/strings.xml
  67. 1 0
      app/src/main/res/values-fi-rFI/strings.xml
  68. 3 0
      app/src/main/res/values-fr/strings.xml
  69. 6 0
      app/src/main/res/values-gl/strings.xml
  70. 3 2
      app/src/main/res/values-hr/strings.xml
  71. 1 0
      app/src/main/res/values-hu-rHU/strings.xml
  72. 1 0
      app/src/main/res/values-is/strings.xml
  73. 10 0
      app/src/main/res/values-it/strings.xml
  74. 1 0
      app/src/main/res/values-iw/strings.xml
  75. 1 0
      app/src/main/res/values-ja-rJP/strings.xml
  76. 1 0
      app/src/main/res/values-ko/strings.xml
  77. 1 0
      app/src/main/res/values-lt-rLT/strings.xml
  78. 1 0
      app/src/main/res/values-lv/strings.xml
  79. 1 0
      app/src/main/res/values-mk/strings.xml
  80. 1 0
      app/src/main/res/values-nb-rNO/strings.xml
  81. 1 0
      app/src/main/res/values-nl/strings.xml
  82. 1 0
      app/src/main/res/values-pl/strings.xml
  83. 1 0
      app/src/main/res/values-pt-rBR/strings.xml
  84. 1 0
      app/src/main/res/values-pt-rPT/strings.xml
  85. 1 0
      app/src/main/res/values-ro/strings.xml
  86. 1 0
      app/src/main/res/values-ru/strings.xml
  87. 1 0
      app/src/main/res/values-sc/strings.xml
  88. 57 0
      app/src/main/res/values-sk-rSK/strings.xml
  89. 1 0
      app/src/main/res/values-sl/strings.xml
  90. 6 0
      app/src/main/res/values-sr/strings.xml
  91. 6 0
      app/src/main/res/values-sv/strings.xml
  92. 1 0
      app/src/main/res/values-th-rTH/strings.xml
  93. 2 0
      app/src/main/res/values-tr/strings.xml
  94. 7 2
      app/src/main/res/values-uk/strings.xml
  95. 6 0
      app/src/main/res/values-zh-rCN/strings.xml
  96. 5 0
      app/src/main/res/values-zh-rHK/strings.xml
  97. 6 0
      app/src/main/res/values-zh-rTW/strings.xml
  98. 1 0
      app/src/main/res/values/strings.xml
  99. 0 7
      app/src/main/res/values/styles.xml
  100. 1 1
      build.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/assembleFlavors.yml

@@ -21,7 +21,7 @@ jobs:
         steps:
             -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: set up JDK 17
-                uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
+                uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
                 with:
                     distribution: "temurin"
                     java-version: 17

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

@@ -21,7 +21,7 @@ jobs:
         steps:
             -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: Set up JDK 17
-                uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
+                uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
                 with:
                     distribution: "temurin"
                     java-version: 17

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

@@ -18,7 +18,7 @@ jobs:
         steps:
             -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
             -   name: Set up JDK 17
-                uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
+                uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
                 with:
                     distribution: "temurin"
                     java-version: 17

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

@@ -22,7 +22,7 @@ jobs:
             -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
                 if: ${{ steps.check-secrets.outputs.ok == 'true' }}
             -   name: set up JDK 17
-                uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
+                uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
                 if: ${{ steps.check-secrets.outputs.ok == 'true' }}
                 with:
                     distribution: "temurin"

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

@@ -40,7 +40,7 @@ jobs:
                         ~/.android/adb*
                     key: avd-${{ matrix.api-level }}
 
-            -   uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3
+            -   uses: actions/setup-java@387ac29b308b003ca37ba93a6cab5eb57c8f5f93 # v3
                 with:
                     distribution: "temurin"
                     java-version: 17

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

@@ -20,7 +20,7 @@ jobs:
         steps:
             -   uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
             -   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

+ 4 - 0
app/build.gradle

@@ -126,6 +126,10 @@ android {
             }
         }
 
+        buildFeatures {
+            buildConfig = true
+        }
+
         productFlavors {
             // used for f-droid
             generic {

+ 10 - 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. -->
@@ -166,6 +169,9 @@
         <receiver
             android:name="com.nextcloud.client.jobs.NotificationWork$NotificationReceiver"
             android:exported="false" />
+        <receiver
+            android:name="com.owncloud.android.files.services.FileUploader$UploadNotificationActionReceiver"
+            android:exported="false" />
         <receiver
             android:name="com.nextcloud.client.widget.DashboardWidgetProvider"
             android:exported="false">
@@ -378,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

+ 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)!!

+ 16 - 0
app/src/main/java/com/nextcloud/client/jobs/FilesUploadWorker.kt

@@ -43,6 +43,7 @@ import com.owncloud.android.datamodel.FileDataStorageManager
 import com.owncloud.android.datamodel.ThumbnailsCacheManager
 import com.owncloud.android.datamodel.UploadsStorageManager
 import com.owncloud.android.db.OCUpload
+import com.owncloud.android.files.services.FileUploader
 import com.owncloud.android.lib.common.OwnCloudAccount
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
 import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
@@ -197,6 +198,18 @@ class FilesUploadWorker(
      * adapted from [com.owncloud.android.files.services.FileUploader.notifyUploadStart]
      */
     private fun createNotification(uploadFileOperation: UploadFileOperation) {
+        val notificationActionIntent = Intent(context, FileUploader.UploadNotificationActionReceiver::class.java)
+        notificationActionIntent.putExtra(FileUploader.EXTRA_ACCOUNT_NAME, uploadFileOperation.user.accountName)
+        notificationActionIntent.putExtra(FileUploader.EXTRA_REMOTE_PATH, uploadFileOperation.remotePath)
+        notificationActionIntent.action = FileUploader.ACTION_CANCEL_BROADCAST
+
+        val pendingIntent = PendingIntent.getBroadcast(
+            context,
+            SecureRandom().nextInt(),
+            notificationActionIntent,
+            PendingIntent.FLAG_IMMUTABLE
+        )
+
         notificationBuilder
             .setOngoing(true)
             .setSmallIcon(R.drawable.notification_icon)
@@ -209,6 +222,8 @@ class FilesUploadWorker(
                     uploadFileOperation.fileName
                 )
             )
+            .clearActions() // to make sure there is only one action
+            .addAction(R.drawable.ic_action_cancel_grey, context.getString(R.string.common_cancel), pendingIntent)
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD)
@@ -275,6 +290,7 @@ class FilesUploadWorker(
                 .setAutoCancel(true)
                 .setOngoing(false)
                 .setProgress(0, 0, false)
+                .clearActions()
 
             val content = ErrorMessageAdapter.getErrorCauseMessage(uploadResult, uploadFileOperation, context.resources)
 

+ 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
     }
 

+ 67 - 46
app/src/main/java/com/nextcloud/client/widget/DashboardWidgetService.kt

@@ -86,7 +86,7 @@ class StackRemoteViewsFactory(
     val userAccountManager: UserAccountManager,
     val clientFactory: ClientFactory,
     val intent: Intent,
-    val widgetRepository: WidgetRepository
+    private val widgetRepository: WidgetRepository
 ) : RemoteViewsService.RemoteViewsFactory {
 
     private lateinit var widgetConfiguration: WidgetConfiguration
@@ -163,58 +163,20 @@ class StackRemoteViewsFactory(
 
     // we will switch soon to coil and then streamline all of this
     // Kotlin cannot catch multiple exception types at same time
-    @Suppress("NestedBlockDepth", "TooGenericExceptionCaught")
+    @Suppress("NestedBlockDepth")
     private fun createItemView(position: Int): RemoteViews {
         return RemoteViews(context.packageName, R.layout.widget_item).apply {
+            if (widgetItems.isEmpty()) {
+                return@apply
+            }
+
             val widgetItem = widgetItems[position]
 
-            // icon bitmap/svg
             if (widgetItem.iconUrl.isNotEmpty()) {
-                val glide: FutureTarget<Bitmap>
-                if (Uri.parse(widgetItem.iconUrl).encodedPath!!.endsWith(".svg")) {
-                    glide = Glide.with(context)
-                        .using(
-                            CustomGlideUriLoader(userAccountManager.user, clientFactory),
-                            InputStream::class.java
-                        )
-                        .from(Uri::class.java)
-                        .`as`(SVGorImage::class.java)
-                        .transcode(SvgOrImageBitmapTranscoder(SVG_SIZE, SVG_SIZE), Bitmap::class.java)
-                        .sourceEncoder(StreamEncoder())
-                        .cacheDecoder(FileToStreamDecoder(SvgOrImageDecoder()))
-                        .decoder(SvgOrImageDecoder())
-                        .diskCacheStrategy(DiskCacheStrategy.SOURCE)
-                        .load(Uri.parse(widgetItem.iconUrl))
-                        .into(SVG_SIZE, SVG_SIZE)
-                } else {
-                    glide = Glide.with(context)
-                        .using(CustomGlideStreamLoader(widgetConfiguration.user.get(), clientFactory))
-                        .load(widgetItem.iconUrl)
-                        .asBitmap()
-                        .into(SVG_SIZE, SVG_SIZE)
-                }
-
-                try {
-                    if (widgetConfiguration.roundIcon) {
-                        setImageViewBitmap(R.id.icon, BitmapUtils.roundBitmap(glide.get()))
-                    } else {
-                        setImageViewBitmap(R.id.icon, glide.get())
-                    }
-                } catch (e: Exception) {
-                    Log_OC.d(TAG, "Error setting icon", e)
-                    setImageViewResource(R.id.icon, R.drawable.ic_dashboard)
-                }
+                loadIcon(widgetItem, this)
             }
 
-            // text
-            setTextViewText(R.id.title, widgetItem.title)
-
-            if (widgetItem.subtitle.isNotEmpty()) {
-                setViewVisibility(R.id.subtitle, View.VISIBLE)
-                setTextViewText(R.id.subtitle, widgetItem.subtitle)
-            } else {
-                setViewVisibility(R.id.subtitle, View.GONE)
-            }
+            updateTexts(widgetItem, this)
 
             if (widgetItem.link.isNotEmpty()) {
                 val clickIntent = Intent(Intent.ACTION_VIEW, Uri.parse(widgetItem.link))
@@ -223,6 +185,65 @@ class StackRemoteViewsFactory(
         }
     }
 
+    @Suppress("TooGenericExceptionCaught")
+    private fun loadIcon(widgetItem: DashboardWidgetItem, remoteViews: RemoteViews) {
+        val isIconSVG = Uri.parse(widgetItem.iconUrl).encodedPath!!.endsWith(".svg")
+        val source: FutureTarget<Bitmap> = if (isIconSVG) {
+            loadSVGIcon(widgetItem)
+        } else {
+            loadBitmapIcon(widgetItem)
+        }
+
+        try {
+            val bitmap: Bitmap = if (widgetConfiguration.roundIcon) {
+                BitmapUtils.roundBitmap(source.get())
+            } else {
+                source.get()
+            }
+
+            remoteViews.setImageViewBitmap(R.id.icon, bitmap)
+        } catch (e: Exception) {
+            Log_OC.d(TAG, "Error setting icon", e)
+            remoteViews.setImageViewResource(R.id.icon, R.drawable.ic_dashboard)
+        }
+    }
+
+    private fun loadSVGIcon(widgetItem: DashboardWidgetItem): FutureTarget<Bitmap> {
+        return Glide.with(context)
+            .using(
+                CustomGlideUriLoader(userAccountManager.user, clientFactory),
+                InputStream::class.java
+            )
+            .from(Uri::class.java)
+            .`as`(SVGorImage::class.java)
+            .transcode(SvgOrImageBitmapTranscoder(SVG_SIZE, SVG_SIZE), Bitmap::class.java)
+            .sourceEncoder(StreamEncoder())
+            .cacheDecoder(FileToStreamDecoder(SvgOrImageDecoder()))
+            .decoder(SvgOrImageDecoder())
+            .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+            .load(Uri.parse(widgetItem.iconUrl))
+            .into(SVG_SIZE, SVG_SIZE)
+    }
+
+    private fun loadBitmapIcon(widgetItem: DashboardWidgetItem): FutureTarget<Bitmap> {
+        return Glide.with(context)
+            .using(CustomGlideStreamLoader(widgetConfiguration.user.get(), clientFactory))
+            .load(widgetItem.iconUrl)
+            .asBitmap()
+            .into(SVG_SIZE, SVG_SIZE)
+    }
+
+    private fun updateTexts(widgetItem: DashboardWidgetItem, remoteViews: RemoteViews) {
+        remoteViews.setTextViewText(R.id.title, widgetItem.title)
+
+        if (widgetItem.subtitle.isNotEmpty()) {
+            remoteViews.setViewVisibility(R.id.subtitle, View.VISIBLE)
+            remoteViews.setTextViewText(R.id.subtitle, widgetItem.subtitle)
+        } else {
+            remoteViews.setViewVisibility(R.id.subtitle, View.GONE)
+        }
+    }
+
     override fun getLoadingView(): RemoteViews? {
         return null
     }

+ 6 - 1
app/src/main/java/com/owncloud/android/db/UploadResult.java

@@ -44,7 +44,8 @@ public enum UploadResult {
     OLD_ANDROID_API(18),
     SYNC_CONFLICT(19),
     CANNOT_CREATE_FILE(20),
-    LOCAL_STORAGE_NOT_COPIED(21);
+    LOCAL_STORAGE_NOT_COPIED(21),
+    QUOTA_EXCEEDED(22);
 
     private final int value;
 
@@ -104,6 +105,8 @@ public enum UploadResult {
                 return CANNOT_CREATE_FILE;
             case 21:
                 return LOCAL_STORAGE_NOT_COPIED;
+            case 22:
+                return QUOTA_EXCEEDED;
         }
         return UNKNOWN;
     }
@@ -162,6 +165,8 @@ public enum UploadResult {
                 return VIRUS_DETECTED;
             case CANNOT_CREATE_FILE:
                 return CANNOT_CREATE_FILE;
+            case QUOTA_EXCEEDED:
+                return QUOTA_EXCEEDED;
             default:
                 return UNKNOWN;
         }

+ 12 - 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;
@@ -81,6 +83,7 @@ import java.util.Vector;
 import javax.inject.Inject;
 
 import androidx.core.app.NotificationCompat;
+import androidx.core.app.ServiceCompat;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import dagger.android.AndroidInjection;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -200,7 +203,15 @@ 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) {
+            ServiceCompat.startForeground(
+                this,
+                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");

+ 58 - 6
app/src/main/java/com/owncloud/android/files/services/FileUploader.java

@@ -34,8 +34,10 @@ import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 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;
@@ -127,6 +129,10 @@ public class FileUploader extends Service
     public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO";
     public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
 
+    public static final String EXTRA_ACCOUNT_NAME = "ACCOUNT_NAME";
+    public static final String ACTION_CANCEL_BROADCAST = "CANCEL";
+    public static final String ACTION_PAUSE_BROADCAST = "PAUSE";
+
     private static final int FOREGROUND_SERVICE_ID = 411;
 
     public static final String KEY_FILE = "FILE";
@@ -197,11 +203,13 @@ public class FileUploader extends Service
     private Notification mNotification;
     private Looper mServiceLooper;
     private ServiceHandler mServiceHandler;
-    private IBinder mBinder;
+    private static IBinder mBinder;
     private OwnCloudClient mUploadClient;
     private Account mCurrentAccount;
     private FileDataStorageManager mStorageManager;
 
+    private SecureRandom secureRandomGenerator = new SecureRandom();
+
     @Inject UserAccountManager accountManager;
     @Inject UploadsStorageManager mUploadsStorageManager;
     @Inject ConnectivityService connectivityService;
@@ -232,6 +240,7 @@ public class FileUploader extends Service
     /**
      * Service initialization
      */
+    @SuppressFBWarnings("ST")
     @Override
     public void onCreate() {
         super.onCreate();
@@ -279,6 +288,7 @@ public class FileUploader extends Service
     /**
      * Service clean up
      */
+    @SuppressFBWarnings("ST")
     @Override
     public void onDestroy() {
         Log_OC.v(TAG, "Destroying service");
@@ -305,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");
@@ -603,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
      */
@@ -703,6 +717,12 @@ public class FileUploader extends Service
      */
     private void notifyUploadStart(UploadFileOperation upload) {
         // / create status notification with a progress bar
+        Intent notificationActionIntent = new Intent(getApplicationContext(),UploadNotificationActionReceiver.class);
+        notificationActionIntent.putExtra(EXTRA_ACCOUNT_NAME,upload.getUser().getAccountName());
+        notificationActionIntent.putExtra(EXTRA_REMOTE_PATH,upload.getRemotePath());
+        notificationActionIntent.setAction(ACTION_CANCEL_BROADCAST);
+
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),secureRandomGenerator.nextInt(),notificationActionIntent, PendingIntent.FLAG_IMMUTABLE);
         mLastPercent = 0;
         mNotificationBuilder = NotificationUtils.newNotificationBuilder(this, viewThemeUtils);
         mNotificationBuilder
@@ -713,7 +733,10 @@ public class FileUploader extends Service
             .setProgress(100, 0, false)
             .setContentText(
                 String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName())
-                           );
+                           )
+            .clearActions() // to make sure there is only one action
+            .addAction(R.drawable.ic_action_cancel_grey,getApplicationContext().getString(R.string.common_cancel),pendingIntent);
+
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
             mNotificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD);
@@ -806,7 +829,8 @@ public class FileUploader extends Service
                 .setContentTitle(getString(tickerId))
                 .setAutoCancel(true)
                 .setOngoing(false)
-                .setProgress(0, 0, false);
+                .setProgress(0, 0, false)
+                .clearActions();
 
             content = ErrorMessageAdapter.getErrorCauseMessage(uploadResult, upload, getResources());
 
@@ -854,7 +878,7 @@ public class FileUploader extends Service
 
             mNotificationBuilder.setContentText(content);
             if (!uploadResult.isSuccess()) {
-                mNotificationManager.notify((new SecureRandom()).nextInt(), mNotificationBuilder.build());
+                mNotificationManager.notify(secureRandomGenerator.nextInt(), mNotificationBuilder.build());
             }
         }
     }
@@ -1403,4 +1427,32 @@ public class FileUploader extends Service
             mService.stopSelf(msg.arg1);
         }
     }
+
+
+    /**
+     * When cancel action in upload notification is pressed, cancel upload of item
+     */
+    public static class UploadNotificationActionReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
+            String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
+            String action = intent.getAction();
+
+            if (ACTION_CANCEL_BROADCAST.equals(action)) {
+                Log_OC.d(TAG, "Cancel broadcast received for file " + remotePath + " at " + System.currentTimeMillis());
+
+                if (accountName == null || remotePath == null) return;
+
+                FileUploaderBinder uploadBinder = (FileUploaderBinder) mBinder;
+                uploadBinder.cancel(accountName, remotePath, null);
+            }else if(ACTION_PAUSE_BROADCAST.equals(action)){
+
+            } else {
+                Log_OC.d(TAG, "Unknown action to perform as UploadNotificationActionReceiver.");
+            }
+        }
+    }
 }

+ 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
+        }
+    }
+}

+ 3 - 4
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);
@@ -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) {

+ 8 - 6
app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -66,6 +66,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.files.model.ServerFileInterface;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.operations.CreateShareViaLinkOperation;
@@ -709,7 +710,7 @@ public abstract class FileActivity extends DrawerActivity
         snackbar.show();
     }
 
-    public static void showShareLinkDialog(FileActivity activity, OCFile file, String link) {
+    public static void showShareLinkDialog(FileActivity activity, ServerFileInterface file, String link) {
         // Create dialog to allow the user choose an app to send the link
         Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
 
@@ -780,9 +781,11 @@ public abstract class FileActivity extends DrawerActivity
     }
 
     public void refreshList() {
-        final Fragment fileListFragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
-        if (fileListFragment != null)  {
-            ((OCFileListFragment) fileListFragment).onRefresh();
+        final Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (fragment instanceof OCFileListFragment listFragment) {
+            listFragment.onRefresh();
+        } else if (fragment instanceof FileDetailFragment detailFragment) {
+            detailFragment.goBackToOCFileListFragment();
         }
     }
 
@@ -864,8 +867,7 @@ public abstract class FileActivity extends DrawerActivity
 
         if (fragment instanceof FileDetailSharingFragment) {
             return (FileDetailSharingFragment) fragment;
-        } else if (fragment instanceof FileDetailFragment) {
-            FileDetailFragment fileDetailFragment = (FileDetailFragment) fragment;
+        } else if (fragment instanceof FileDetailFragment fileDetailFragment) {
             return fileDetailFragment.getFileDetailSharingFragment();
         } else {
             return null;

+ 11 - 7
app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -351,14 +351,18 @@ public class FileDisplayActivity extends FileActivity
     public void onConfigurationChanged(@NonNull Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        StoragePermissionDialogFragment fragment = (StoragePermissionDialogFragment) getSupportFragmentManager().findFragmentByTag(PERMISSION_CHOICE_DIALOG_TAG);
-        if (fragment != null) {
-            Dialog dialog = fragment.getDialog();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            StoragePermissionDialogFragment fragment =
+                (StoragePermissionDialogFragment) getSupportFragmentManager()
+                    .findFragmentByTag(PERMISSION_CHOICE_DIALOG_TAG);
+            if (fragment != null) {
+                Dialog dialog = fragment.getDialog();
 
-            if (dialog != null && dialog.isShowing()) {
-                dialog.dismiss();
-                getSupportFragmentManager().beginTransaction().remove(fragment).commitNowAllowingStateLoss();
-                PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils);
+                if (dialog != null && dialog.isShowing()) {
+                    dialog.dismiss();
+                    getSupportFragmentManager().beginTransaction().remove(fragment).commitNowAllowingStateLoss();
+                    PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils);
+                }
             }
         }
     }

+ 89 - 70
app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -41,10 +41,21 @@ import android.os.Looper;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
 import android.view.WindowManager.LayoutParams;
-import android.widget.*;
+import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
 
 import com.google.android.material.button.MaterialButton;
 import com.nextcloud.client.account.User;
@@ -66,13 +77,21 @@ import com.owncloud.android.operations.CreateFolderOperation;
 import com.owncloud.android.operations.RefreshFolderOperation;
 import com.owncloud.android.operations.UploadFileOperation;
 import com.owncloud.android.syncadapter.FileSyncAdapter;
-import com.owncloud.android.ui.adapter.UploaderAdapter;
+import com.owncloud.android.ui.adapter.ReceiveExternalFilesAdapter;
 import com.owncloud.android.ui.asynctasks.CopyAndUploadContentUrisTask;
-import com.owncloud.android.ui.dialog.*;
+import com.owncloud.android.ui.dialog.AccountChooserInterface;
+import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
+import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
+import com.owncloud.android.ui.dialog.MultipleAccountsDialog;
+import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
 import com.owncloud.android.ui.fragment.TaskRetainerFragment;
 import com.owncloud.android.ui.helpers.FileOperationsHelper;
 import com.owncloud.android.ui.helpers.UriUploader;
-import com.owncloud.android.utils.*;
+import com.owncloud.android.utils.DataHolderUtil;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.ErrorMessageAdapter;
+import com.owncloud.android.utils.FileSortOrder;
+import com.owncloud.android.utils.MimeType;
 import com.owncloud.android.utils.theme.ViewThemeUtils;
 
 import java.io.File;
@@ -80,7 +99,12 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.nio.charset.Charset;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
 
 import javax.inject.Inject;
 
@@ -96,6 +120,7 @@ import androidx.core.view.MenuItemCompat;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.FragmentManager;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
 
 import static com.owncloud.android.utils.DisplayUtils.openSortingOrderDialogFragment;
 
@@ -103,8 +128,8 @@ import static com.owncloud.android.utils.DisplayUtils.openSortingOrderDialogFrag
  * This can be used to upload things to an ownCloud instance.
  */
 public class ReceiveExternalFilesActivity extends FileActivity
-    implements OnItemClickListener, View.OnClickListener, CopyAndUploadContentUrisTask.OnCopyTmpFilesTaskListener,
-    SortingOrderDialogFragment.OnSortingOrderListener, Injectable, AccountChooserInterface {
+    implements View.OnClickListener, CopyAndUploadContentUrisTask.OnCopyTmpFilesTaskListener,
+    SortingOrderDialogFragment.OnSortingOrderListener, Injectable, AccountChooserInterface, ReceiveExternalFilesAdapter.OnItemClickListener {
 
     private static final String TAG = ReceiveExternalFilesActivity.class.getSimpleName();
 
@@ -125,6 +150,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
     private OCFile mFile;
 
     private SyncBroadcastReceiver mSyncBroadcastReceiver;
+    private ReceiveExternalFilesAdapter receiveExternalFilesAdapter;
     private boolean mSyncInProgress;
 
     private final static int REQUEST_CODE__SETUP_ACCOUNT = REQUEST_CODE__LAST_SHARED + 1;
@@ -273,6 +299,22 @@ public class ReceiveExternalFilesActivity extends FileActivity
         populateDirectoryList();
     }
 
+    @Override
+    public void selectFile(OCFile file) {
+        if (file.isFolder()) {
+            if (file.isEncrypted() &&
+                !FileOperationsHelper.isEndToEndEncryptionSetup(this, getUser().orElseThrow(IllegalAccessError::new))) {
+                DisplayUtils.showSnackMessage(this, R.string.e2e_not_yet_setup);
+
+                return;
+            }
+
+            startSyncFolderOperation(file);
+            mParents.push(file.getFileName());
+            populateDirectoryList();
+        }
+    }
+
     public static class DialogNoAccount extends DialogFragment {
         @NonNull
         @Override
@@ -611,39 +653,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
         }
     }
 
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        // click on folder in the list
-        Log_OC.d(TAG, "on item click");
-        List<OCFile> tmpFiles = getStorageManager().getFolderContent(mFile, false);
-        tmpFiles = sortFileList(tmpFiles);
-
-        if (tmpFiles.isEmpty()) {
-            return;
-        }
-        // filter on dirtype
-        Vector<OCFile> files = new Vector<>();
-        files.addAll(tmpFiles);
-
-        if (files.size() < position) {
-            throw new IndexOutOfBoundsException("Incorrect item selected");
-        }
-        OCFile ocFile = files.get(position);
-        if (ocFile.isFolder()) {
-            if (ocFile.isEncrypted() &&
-                !FileOperationsHelper.isEndToEndEncryptionSetup(this, getUser().orElseThrow(IllegalAccessError::new))) {
-                DisplayUtils.showSnackMessage(this, R.string.e2e_not_yet_setup);
-
-                return;
-            }
-
-            OCFile folderToEnter = files.get(position);
-            startSyncFolderOperation(folderToEnter);
-            mParents.push(folderToEnter.getFileName());
-            populateDirectoryList();
-        }
-    }
-
     @Override
     public void onClick(View v) {
         // click on button
@@ -740,29 +749,10 @@ public class ReceiveExternalFilesActivity extends FileActivity
                 binding.list.setVisibility(View.GONE);
             } else {
                 mEmptyListContainer.setVisibility(View.GONE);
-
                 files = sortFileList(files);
-
-                List<Map<String, Object>> data = new LinkedList<>();
-                for (OCFile f : files) {
-                    Map<String, Object> h = new HashMap<>();
-                    h.put("dirname", f);
-                    data.add(h);
-                }
-
-                UploaderAdapter sa = new UploaderAdapter(this,
-                                                         data,
-                                                         R.layout.uploader_list_item_layout,
-                                                         new String[]{"dirname"},
-                                                         new int[]{R.id.filename},
-                                                         getStorageManager(),
-                                                         getUser().get(),
-                                                         syncedFolderProvider,
-                                                         viewThemeUtils);
-
-                binding.list.setAdapter(sa);
-                binding.list.setVisibility(View.VISIBLE);
+                setupReceiveExternalFilesAdapter(files);
             }
+
             MaterialButton btnChooseFolder = binding.uploaderChooseFolder;
             viewThemeUtils.material.colorMaterialButtonPrimaryFilled(btnChooseFolder);
             btnChooseFolder.setOnClickListener(this);
@@ -774,8 +764,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
             viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.uploaderCancel);
             binding.uploaderCancel.setOnClickListener(this);
 
-            binding.list.setOnItemClickListener(this);
-
             sortButton = binding.toolbarLayout.sortButton;
             FileSortOrder sortOrder = preferences.getSortOrderByFolder(mFile);
             sortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder));
@@ -783,6 +771,21 @@ public class ReceiveExternalFilesActivity extends FileActivity
         }
     }
 
+    private void setupReceiveExternalFilesAdapter(List<OCFile> files) {
+        receiveExternalFilesAdapter = new ReceiveExternalFilesAdapter(files,
+                                                                      this,
+                                                                      getUser().get(),
+                                                                      getStorageManager(),
+                                                                      viewThemeUtils,
+                                                                      syncedFolderProvider,
+                                                                      this);
+
+
+        binding.list.setLayoutManager(new LinearLayoutManager(this));
+        binding.list.setAdapter(receiveExternalFilesAdapter);
+        binding.list.setVisibility(View.VISIBLE);
+    }
+
     protected void setupEmptyList() {
         mEmptyListContainer = binding.emptyView.emptyListView;
         mEmptyListMessage = binding.emptyView.emptyListViewText;
@@ -1016,19 +1019,35 @@ public class ReceiveExternalFilesActivity extends FileActivity
             menu.findItem(R.id.action_create_dir).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         }
 
-        // tint search event
-        final MenuItem searchMenuItem = menu.findItem(R.id.action_search);
-        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
+        setupSearchView(menu);
 
         MenuItem newFolderMenuItem = menu.findItem(R.id.action_create_dir);
         newFolderMenuItem.setEnabled(mFile.canWrite());
 
-        // hacky as no default way is provided
-        viewThemeUtils.androidx.themeToolbarSearchView(searchView);
-
         return true;
     }
 
+    private void setupSearchView(Menu menu) {
+        final MenuItem searchMenuItem = menu.findItem(R.id.action_search);
+
+        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
+        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+            @Override
+            public boolean onQueryTextSubmit(String query) {
+                receiveExternalFilesAdapter.filter(query);
+                return false;
+            }
+
+            @Override
+            public boolean onQueryTextChange(String newText) {
+                receiveExternalFilesAdapter.filter(newText);
+                return false;
+            }
+        });
+
+        viewThemeUtils.androidx.themeToolbarSearchView(searchView);
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         boolean retval = true;

+ 2 - 0
app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

@@ -178,6 +178,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
                                                     viewThemeUtils,
                                                     syncedFolderProvider);
 
+        setHasStableIds(true);
+
         // initialise thumbnails cache on background thread
         new ThumbnailsCacheManager.InitDiskCacheTask().execute();
     }

+ 176 - 0
app/src/main/java/com/owncloud/android/ui/adapter/ReceiveExternalFilesAdapter.kt

@@ -0,0 +1,176 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Alper Ozturk
+ * Copyright (C) 2023 Alper Ozturk
+ * 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/>.
+ */
+
+package com.owncloud.android.ui.adapter
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.recyclerview.widget.RecyclerView
+import com.nextcloud.client.account.User
+import com.owncloud.android.databinding.UploaderListItemLayoutBinding
+import com.owncloud.android.datamodel.FileDataStorageManager
+import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.datamodel.SyncedFolderProvider
+import com.owncloud.android.datamodel.ThumbnailsCacheManager
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTask
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTaskObject
+import com.owncloud.android.utils.DisplayUtils
+import com.owncloud.android.utils.MimeTypeUtil
+import com.owncloud.android.utils.theme.ViewThemeUtils
+
+@Suppress("LongParameterList")
+class ReceiveExternalFilesAdapter(
+    private val files: List<OCFile>,
+    private val context: Context,
+    private val user: User,
+    private val storageManager: FileDataStorageManager,
+    private val viewThemeUtils: ViewThemeUtils,
+    private val syncedFolderProvider: SyncedFolderProvider,
+    private val onItemClickListener: OnItemClickListener
+) : RecyclerView.Adapter<ReceiveExternalFilesAdapter.ReceiveExternalViewHolder>() {
+
+    private var filteredFiles: List<OCFile> = files
+
+    interface OnItemClickListener {
+        fun selectFile(file: OCFile)
+    }
+
+    inner class ReceiveExternalViewHolder(val binding: UploaderListItemLayoutBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+        init {
+            binding.root.setOnClickListener {
+                val position = bindingAdapterPosition
+                if (position != RecyclerView.NO_POSITION) {
+                    onItemClickListener.selectFile(filteredFiles[position])
+                }
+            }
+        }
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    fun filter(query: String) {
+        filteredFiles = if (query.isEmpty()) {
+            files
+        } else {
+            files.filter { file ->
+                file.fileName.contains(query, ignoreCase = true)
+            }
+        }
+        notifyDataSetChanged()
+    }
+
+    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ReceiveExternalViewHolder {
+        val binding = UploaderListItemLayoutBinding
+            .inflate(LayoutInflater.from(viewGroup.context), viewGroup, false)
+        return ReceiveExternalViewHolder(binding)
+    }
+
+    override fun onBindViewHolder(viewHolder: ReceiveExternalViewHolder, position: Int) {
+        val file = filteredFiles[position]
+
+        viewHolder.binding.filename.text = file.fileName
+        viewHolder.binding.lastMod.text = DisplayUtils.getRelativeTimestamp(context, file.modificationTimestamp)
+
+        if (!file.isFolder) {
+            viewHolder.binding.fileSize.text = DisplayUtils.bytesToHumanReadable(file.fileLength)
+        }
+
+        viewHolder.binding.fileSize.visibility = if (file.isFolder) {
+            View.GONE
+        } else {
+            View.VISIBLE
+        }
+        viewHolder.binding.fileSeparator.visibility = if (file.isFolder) {
+            View.GONE
+        } else {
+            View.VISIBLE
+        }
+
+        val thumbnailImageView = viewHolder.binding.thumbnail
+        setupThumbnail(thumbnailImageView, file)
+    }
+
+    private fun setupThumbnail(thumbnailImageView: ImageView, file: OCFile) {
+        thumbnailImageView.tag = file.fileId
+
+        if (file.isFolder) {
+            setupThumbnailForFolder(thumbnailImageView, file)
+        } else if (MimeTypeUtil.isImage(file) && file.remoteId != null) {
+            setupThumbnailForImage(thumbnailImageView, file)
+        } else {
+            setupDefaultThumbnail(thumbnailImageView, file)
+        }
+    }
+
+    private fun setupThumbnailForFolder(thumbnailImageView: ImageView, file: OCFile) {
+        val isAutoUploadFolder = SyncedFolderProvider.isAutoUploadFolder(syncedFolderProvider, file, user)
+        val isDarkModeActive = syncedFolderProvider.preferences.isDarkModeEnabled
+        val overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder)
+        val icon = MimeTypeUtil.getFileIcon(isDarkModeActive, overlayIconId, context, viewThemeUtils)
+        thumbnailImageView.setImageDrawable(icon)
+    }
+
+    @Suppress("NestedBlockDepth")
+    private fun setupThumbnailForImage(thumbnailImageView: ImageView, file: OCFile) {
+        var thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(file.remoteId.toString())
+        if (thumbnail != null && !file.isUpdateThumbnailNeeded) {
+            thumbnailImageView.setImageBitmap(thumbnail)
+        } else {
+            // generate new Thumbnail
+            if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailImageView)) {
+                val task = ThumbnailGenerationTask(thumbnailImageView, storageManager, user)
+                if (thumbnail == null) {
+                    thumbnail = if (MimeTypeUtil.isVideo(file)) {
+                        ThumbnailsCacheManager.mDefaultVideo
+                    } else {
+                        ThumbnailsCacheManager.mDefaultImg
+                    }
+                }
+                val asyncDrawable = AsyncThumbnailDrawable(
+                    context.resources,
+                    thumbnail,
+                    task
+                )
+                thumbnailImageView.setImageDrawable(asyncDrawable)
+
+                @Suppress("DEPRECATION")
+                task.execute(ThumbnailGenerationTaskObject(file, file.remoteId))
+            }
+        }
+    }
+
+    private fun setupDefaultThumbnail(thumbnailImageView: ImageView, file: OCFile) {
+        val icon = MimeTypeUtil.getFileTypeIcon(
+            file.mimeType,
+            file.fileName,
+            context,
+            viewThemeUtils
+        )
+        thumbnailImageView.setImageDrawable(icon)
+    }
+
+    override fun getItemCount() = filteredFiles.size
+}

+ 3 - 0
app/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

@@ -711,6 +711,9 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
             case LOCAL_STORAGE_NOT_COPIED:
                 status = parentActivity.getString(R.string.upload_local_storage_not_copied);
                 break;
+            case QUOTA_EXCEEDED:
+                status = parentActivity.getString(R.string.upload_quota_exceeded);
+                break;
             default:
                 status = parentActivity.getString(R.string.upload_unknown_error);
                 break;

+ 0 - 156
app/src/main/java/com/owncloud/android/ui/adapter/UploaderAdapter.java

@@ -1,156 +0,0 @@
-/*
- *   ownCloud Android client application
- *
- *   @author Tobias Kaminsky
- *   Copyright (C) 2016 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.adapter;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.SimpleAdapter;
-import android.widget.TextView;
-
-import com.nextcloud.client.account.User;
-import com.nextcloud.client.preferences.DarkMode;
-import com.owncloud.android.R;
-import com.owncloud.android.datamodel.FileDataStorageManager;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.datamodel.SyncedFolderProvider;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class UploaderAdapter extends SimpleAdapter {
-
-    private final Context mContext;
-    private final User user;
-    private final FileDataStorageManager mStorageManager;
-    private final LayoutInflater inflater;
-    private final ViewThemeUtils viewThemeUtils;
-    private SyncedFolderProvider syncedFolderProvider;
-
-    public UploaderAdapter(Context context,
-                           List<? extends Map<String, ?>> data,
-                           int resource,
-                           String[] from,
-                           int[] to,
-                           FileDataStorageManager storageManager,
-                           User user,
-                           SyncedFolderProvider syncedFolderProvider,
-                           ViewThemeUtils viewThemeUtils) {
-        super(context, data, resource, from, to);
-        this.user = user;
-        mStorageManager = storageManager;
-        mContext = context;
-        this.syncedFolderProvider = syncedFolderProvider;
-        inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        this.viewThemeUtils = viewThemeUtils;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View vi = convertView;
-        if (convertView == null) {
-            vi = inflater.inflate(R.layout.uploader_list_item_layout, parent, false);
-        }
-
-        HashMap<String, OCFile> data = (HashMap<String, OCFile>) getItem(position);
-        OCFile file = data.get("dirname");
-
-        TextView filename = vi.findViewById(R.id.filename);
-        filename.setText(file.getFileName());
-
-        ImageView fileIcon = vi.findViewById(R.id.thumbnail);
-        fileIcon.setTag(file.getFileId());
-
-        TextView lastModV = vi.findViewById(R.id.last_mod);
-        lastModV.setText(DisplayUtils.getRelativeTimestamp(mContext, file.getModificationTimestamp()));
-
-        TextView fileSizeV = vi.findViewById(R.id.file_size);
-        TextView fileSizeSeparatorV = vi.findViewById(R.id.file_separator);
-
-        if(!file.isFolder()) {
-            fileSizeV.setVisibility(View.VISIBLE);
-            fileSizeSeparatorV.setVisibility(View.VISIBLE);
-            fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
-        } else {
-            fileSizeV.setVisibility(View.GONE);
-            fileSizeSeparatorV.setVisibility(View.GONE);
-        }
-
-        if (file.isFolder()) {
-            boolean isAutoUploadFolder = SyncedFolderProvider.isAutoUploadFolder(syncedFolderProvider, file, user);
-            boolean isDarkModeActive = syncedFolderProvider.getPreferences().isDarkModeEnabled();
-
-            Integer overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder);
-            final LayerDrawable icon = MimeTypeUtil.getFileIcon(isDarkModeActive, overlayIconId, mContext, viewThemeUtils);
-            fileIcon.setImageDrawable(icon);
-        } else {
-            // get Thumbnail if file is image
-            if (MimeTypeUtil.isImage(file) && file.getRemoteId() != null) {
-                // Thumbnail in Cache?
-                Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
-                        String.valueOf(file.getRemoteId())
-                );
-                if (thumbnail != null && !file.isUpdateThumbnailNeeded()) {
-                    fileIcon.setImageBitmap(thumbnail);
-                } else {
-                    // generate new Thumbnail
-                    if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
-                        final ThumbnailsCacheManager.ThumbnailGenerationTask task =
-                                new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, mStorageManager, user);
-                        if (thumbnail == null) {
-                            if (MimeTypeUtil.isVideo(file)) {
-                                thumbnail = ThumbnailsCacheManager.mDefaultVideo;
-                            } else {
-                                thumbnail = ThumbnailsCacheManager.mDefaultImg;
-                            }
-                        }
-                        final AsyncThumbnailDrawable asyncDrawable = new AsyncThumbnailDrawable(
-                                mContext.getResources(),
-                                thumbnail,
-                                task
-                        );
-                        fileIcon.setImageDrawable(asyncDrawable);
-                        task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, file.getRemoteId()));
-                    }
-                }
-            } else {
-                final Drawable icon = MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
-                                                                   file.getFileName(),
-                                                                   mContext,
-                                                                   viewThemeUtils);
-                fileIcon.setImageDrawable(icon);
-            }
-        }
-
-        return vi;
-    }
-}

+ 2 - 6
app/src/main/java/com/owncloud/android/ui/events/AccountRemovedEvent.java → app/src/main/java/com/owncloud/android/ui/events/AccountRemovedEvent.kt

@@ -18,13 +18,9 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
 /**
  * Event that notifies that an account was removed
  */
-
-public class AccountRemovedEvent {
-
-}
+class AccountRemovedEvent

+ 3 - 4
app/src/main/java/com/owncloud/android/ui/events/ChangeMenuEvent.java → app/src/main/java/com/owncloud/android/ui/events/ChangeMenuEvent.kt

@@ -15,12 +15,11 @@
  * 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/>.
+ * along with this program.  If not, see <http:></http:>//www.gnu.org/licenses/>.
  */
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
 /**
  * Currently a dummy event to restore grid view, sort, and search
  */
-public class ChangeMenuEvent {
-}
+class ChangeMenuEvent

+ 2 - 9
app/src/main/java/com/owncloud/android/ui/events/CommentsEvent.java → app/src/main/java/com/owncloud/android/ui/events/CommentsEvent.kt

@@ -18,16 +18,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  */
-
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
 /**
  * Event for refreshing comment state of a file
  */
-public class CommentsEvent {
-    public final String remoteId;
-
-    public CommentsEvent(String remoteId) {
-        this.remoteId = remoteId;
-    }
-}
+class CommentsEvent(val remoteId: String)

+ 3 - 4
app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.java → app/src/main/java/com/owncloud/android/ui/events/DummyDrawerEvent.kt

@@ -15,12 +15,11 @@
  * 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/>.
+ * along with this program.  If not, see <http:></http:>//www.gnu.org/licenses/>.
  */
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
 /**
  * Dummy drawer event
  */
-public class DummyDrawerEvent {
-}
+class DummyDrawerEvent

+ 0 - 37
app/src/main/java/com/owncloud/android/ui/events/EncryptionEvent.java

@@ -1,37 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Tobias Kaminsky
- * Copyright (C) 2017 Tobias Kaminsky
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.events;
-
-/**
- * Event for set folder as encrypted/decrypted
- */
-public class EncryptionEvent {
-    public final long localId;
-    public final String remotePath;
-    public final String remoteId;
-    public final boolean shouldBeEncrypted;
-
-    public EncryptionEvent(long localId, String remoteId, String remotePath, boolean shouldBeEncrypted) {
-        this.localId = localId;
-        this.remoteId = remoteId;
-        this.remotePath = remotePath;
-        this.shouldBeEncrypted = shouldBeEncrypted;
-    }
-}

+ 30 - 0
app/src/main/java/com/owncloud/android/ui/events/EncryptionEvent.kt

@@ -0,0 +1,30 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.events
+
+/**
+ * Event for set folder as encrypted/decrypted
+ */
+class EncryptionEvent(
+    val localId: Long,
+    val remoteId: String,
+    val remotePath: String,
+    val shouldBeEncrypted: Boolean
+)

+ 0 - 33
app/src/main/java/com/owncloud/android/ui/events/FavoriteEvent.java

@@ -1,33 +0,0 @@
-/**
- * Nextcloud Android client application
- *
- * @author Mario Danic
- * Copyright (C) 2017 Mario Danic
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.events;
-
-/**
- * Event for making favoriting work
- */
-public class FavoriteEvent {
-    public final String remotePath;
-    public final boolean shouldFavorite;
-
-    public FavoriteEvent(String remotePath, boolean shouldFavorite) {
-        this.remotePath = remotePath;
-        this.shouldFavorite = shouldFavorite;
-    }
-}

+ 6 - 9
app/src/main/java/com/owncloud/android/ui/events/VCardToggleEvent.java → app/src/main/java/com/owncloud/android/ui/events/FavoriteEvent.kt

@@ -15,14 +15,11 @@
  * 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/>.
+ * along with this program.  If not, see <http:></http:>//www.gnu.org/licenses/>.
  */
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
-public class VCardToggleEvent {
-    public boolean showRestoreButton;
-
-    public VCardToggleEvent(boolean showRestore) {
-        this.showRestoreButton = showRestore;
-    }
-}
+/**
+ * Event for making favoriting work
+ */
+class FavoriteEvent(val remotePath: String, val shouldFavorite: Boolean)

+ 0 - 35
app/src/main/java/com/owncloud/android/ui/events/InitiateSyncedFolder.java

@@ -1,35 +0,0 @@
-/**
- * Nextcloud Android client application
- *
- * @author Mario Danic
- * Copyright (C) 2017 Mario Danic
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.events;
-
-import com.owncloud.android.datamodel.SyncedFolder;
-
-public class InitiateSyncedFolder {
-    private final SyncedFolder syncedFolder;
-
-
-    public InitiateSyncedFolder(SyncedFolder syncedFolder) {
-        this.syncedFolder = syncedFolder;
-    }
-
-    public SyncedFolder getSyncedFolder() {
-        return syncedFolder;
-    }
-}

+ 0 - 33
app/src/main/java/com/owncloud/android/ui/events/MenuItemClickEvent.java

@@ -1,33 +0,0 @@
-/**
- * Nextcloud Android client application
- *
- * @author Mario Danic
- * Copyright (C) 2017 Mario Danic
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package com.owncloud.android.ui.events;
-
-import android.view.MenuItem;
-
-/**
- * Menu item click event
- */
-public class MenuItemClickEvent {
-    public final MenuItem menuItem;
-
-    public MenuItemClickEvent(MenuItem menuItem) {
-        this.menuItem = menuItem;
-    }
-}

+ 2 - 3
app/src/main/java/com/owncloud/android/ui/events/TokenPushEvent.java → app/src/main/java/com/owncloud/android/ui/events/TokenPushEvent.kt

@@ -17,10 +17,9 @@
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-package com.owncloud.android.ui.events;
+package com.owncloud.android.ui.events
 
 /**
  * Event to send push token where it belongs
  */
-public class TokenPushEvent {
-}
+class TokenPushEvent 

+ 22 - 0
app/src/main/java/com/owncloud/android/ui/events/VCardToggleEvent.kt

@@ -0,0 +1,22 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http:></http:>//www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.events
+
+class VCardToggleEvent(var showRestoreButton: Boolean)

+ 1 - 1
app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -467,7 +467,7 @@ public class ExtendedListFragment extends Fragment implements
     private void scrollToPosition(int position) {
         LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
 
-        if (mRecyclerView != null) {
+        if (linearLayoutManager != null) {
             int visibleItemCount = linearLayoutManager.findLastCompletelyVisibleItemPosition() -
                 linearLayoutManager.findFirstCompletelyVisibleItemPosition();
             linearLayoutManager.scrollToPositionWithOffset(position, (visibleItemCount / 2) * mHeightCell);

+ 8 - 4
app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -195,6 +195,10 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
         return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailActivitiesFragment();
     }
 
+    public void goBackToOCFileListFragment() {
+        requireActivity().onBackPressed();
+    }
+
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
@@ -783,13 +787,13 @@ public class FileDetailFragment extends FileFragment implements OnClickListener,
             OwnCloudClient client = clientFactory.create(user);
 
             ToggleFavoriteRemoteOperation toggleFavoriteOperation = new ToggleFavoriteRemoteOperation(
-                event.shouldFavorite, event.remotePath);
+                event.getShouldFavorite(), event.getRemotePath());
             RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(client);
 
             if (remoteOperationResult.isSuccess()) {
-                getFile().setFavorite(event.shouldFavorite);
-                OCFile file = storageManager.getFileByEncryptedRemotePath(event.remotePath);
-                file.setFavorite(event.shouldFavorite);
+                getFile().setFavorite(event.getShouldFavorite());
+                OCFile file = storageManager.getFileByEncryptedRemotePath(event.getRemotePath());
+                file.setFavorite(event.getShouldFavorite());
                 storageManager.saveFile(file);
             }
 

+ 2 - 15
app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java

@@ -445,21 +445,8 @@ public class FileDetailSharingFragment extends Fragment implements ShareeListAda
                                                                                    ShareType.PUBLIC_LINK,
                                                                                    "");
 
-//
-//        boolean supportsSecureFiledrop = file.isEncrypted() &&
-//            capabilities.getVersion().isNewerOrEqual(NextcloudVersion.nextcloud_26);
-//
-//        if (publicShares.isEmpty() &&
-//            containsNoNewPublicShare(adapter.getShares()) &&
-//            (!file.isEncrypted() || supportsSecureFiledrop)) {
-//            final OCShare ocShare = new OCShare();
-//            ocShare.setShareType(ShareType.NEW_PUBLIC_LINK);
-//            publicShares.add(ocShare);
-//        } else {
-//            adapter.removeNewPublicShare();
-//        }
-
-        if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares())) {
+        if (publicShares.isEmpty() && containsNoNewPublicShare(adapter.getShares()) &&
+            (!file.isEncrypted() || capabilities.getEndToEndEncryption().isTrue())) {
             final OCShare ocShare = new OCShare();
             ocShare.setShareType(ShareType.NEW_PUBLIC_LINK);
             publicShares.add(ocShare);

+ 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()) {

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

@@ -1588,7 +1588,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent(CommentsEvent event) {
-        mAdapter.refreshCommentsCount(event.remoteId);
+        mAdapter.refreshCommentsCount(event.getRemoteId());
     }
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
@@ -1598,13 +1598,13 @@ public class OCFileListFragment extends ExtendedListFragment implements
             OwnCloudClient client = clientFactory.create(user);
 
             ToggleFavoriteRemoteOperation toggleFavoriteOperation = new ToggleFavoriteRemoteOperation(
-                event.shouldFavorite, event.remotePath);
+                event.getShouldFavorite(), event.getRemotePath());
             RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(client);
 
             if (remoteOperationResult.isSuccess()) {
-                boolean removeFromList = currentSearchType == SearchType.FAVORITE_SEARCH && !event.shouldFavorite;
+                boolean removeFromList = currentSearchType == SearchType.FAVORITE_SEARCH && !event.getShouldFavorite();
                 setEmptyListMessage(SearchType.FAVORITE_SEARCH);
-                mAdapter.setFavoriteAttributeForItemID(event.remotePath, event.shouldFavorite, removeFromList);
+                mAdapter.setFavoriteAttributeForItemID(event.getRemotePath(), event.getShouldFavorite(), removeFromList);
             }
 
         } catch (ClientFactory.CreationException e) {
@@ -1692,7 +1692,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
         String privateKey = arbitraryDataProvider.getValue(user, EncryptionUtils.PRIVATE_KEY);
 
         FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
-        OCFile file = storageManager.getFileByRemoteId(event.remoteId);
+        OCFile file = storageManager.getFileByRemoteId(event.getRemoteId());
 
         if (publicKey.isEmpty() || privateKey.isEmpty()) {
             Log_OC.d(TAG, "no public key for " + user.getAccountName());
@@ -1706,10 +1706,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
             dialog.show(getParentFragmentManager(), SETUP_ENCRYPTION_DIALOG_TAG);
         } else {
             encryptFolder(file,
-                          event.localId,
-                          event.remoteId,
-                          event.remotePath,
-                          event.shouldBeEncrypted,
+                          event.getLocalId(),
+                          event.getRemoteId(),
+                          event.getRemotePath(),
+                          event.getShouldBeEncrypted(),
                           publicKey,
                           privateKey);
         }

+ 1 - 1
app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupListFragment.java

@@ -270,7 +270,7 @@ public class BackupListFragment extends FileFragment implements Injectable {
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onMessageEvent(VCardToggleEvent event) {
-        if (event.showRestoreButton) {
+        if (event.getShowRestoreButton()) {
             binding.contactlistRestoreSelectedContainer.setVisibility(View.VISIBLE);
         } else {
             binding.contactlistRestoreSelectedContainer.setVisibility(View.GONE);

+ 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 super.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();

+ 2 - 0
app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -412,6 +412,8 @@ public class PreviewImageFragment extends FileFragment implements Injectable {
             seeDetails();
         } else if (itemId == R.id.action_download_file || itemId == R.id.action_sync_file) {
             containerActivity.getFileOperationsHelper().syncFile(getFile());
+        }else if(itemId == R.id.action_cancel_sync){
+            containerActivity.getFileOperationsHelper().cancelTransference(getFile());
         } else if (itemId == R.id.action_set_as_wallpaper) {
             containerActivity.getFileOperationsHelper().setPictureAs(getFile(), getImageView());
         } else if (itemId == R.id.action_export_file) {

+ 2 - 0
app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java

@@ -450,6 +450,8 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene
             seeDetails();
         } else if (itemId == R.id.action_sync_file) {
             containerActivity.getFileOperationsHelper().syncFile(getFile());
+        } else if (itemId == R.id.action_cancel_sync) {
+            containerActivity.getFileOperationsHelper().cancelTransference(getFile());
         } else if (itemId == R.id.action_stream_media) {
             containerActivity.getFileOperationsHelper().streamMediaFile(getFile());
         } else if (itemId == R.id.action_export_file) {

+ 2 - 0
app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java

@@ -330,6 +330,8 @@ public class PreviewTextFileFragment extends PreviewTextFragment {
             seeDetails();
         } else if (itemId == R.id.action_sync_file) {
             containerActivity.getFileOperationsHelper().syncFile(getFile());
+        } else if(itemId == R.id.action_cancel_sync){
+            containerActivity.getFileOperationsHelper().cancelTransference(getFile());
         } else if (itemId == R.id.action_edit) {
             containerActivity.getFileOperationsHelper().openFileWithTextEditor(getFile(), getContext());
         }

+ 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())) {

+ 2 - 1
app/src/main/res/layout/receive_external_files.xml

@@ -34,10 +34,11 @@
         android:layout_height="0dp"
         android:layout_weight="1">
 
-        <ListView
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@android:id/list"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:orientation="vertical"
             android:background="@color/bg_default"
             android:divider="@color/transparent"
             android:dividerHeight="0dip"

+ 6 - 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>
@@ -886,6 +891,7 @@
     <string name="upload_lock_failed">فشل قفّل المجلد</string>
     <string name="upload_old_android">التشفير ممكن فقط مع &gt;= Android 5.0</string>
     <string name="upload_query_move_foreign_files">يمنع وجود مساحة غير كافية نسخ الملفات المحددة إلى المجلد %1$s. هل ترغب في نقلهم إلى هناك بدلاً من ذلك؟</string>
+    <string name="upload_quota_exceeded">الحصة التخزينية تمّ استنفاذها</string>
     <string name="upload_scan_doc_upload">مسح مستند عبر الكاميرا</string>
     <string name="upload_sync_conflict">تعارض المزامنة ، يرجى حلها يدويًا</string>
     <string name="upload_unknown_error">خطأ غير معروف</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>

+ 6 - 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>
@@ -879,6 +884,7 @@
     <string name="upload_lock_failed">Uzamčení složky se nezdařilo</string>
     <string name="upload_old_android">Šifrování je možné pouze na systému Android verze 5.0 a novějším</string>
     <string name="upload_query_move_foreign_files">Pro zkopírování vybraných souborů do složky %1$s není dostatek volného místa. Chcete je tam namísto toho přesunout?</string>
+    <string name="upload_quota_exceeded">Kvóta úložiště překročena</string>
     <string name="upload_scan_doc_upload">Naskenovat dokument kamerou</string>
     <string name="upload_sync_conflict">Konflikt synchronizace – vyřešte ručně</string>
     <string name="upload_unknown_error">Neznámá chyba</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>

+ 6 - 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>
@@ -880,6 +885,7 @@
     <string name="upload_lock_failed">Fehler beim Sperren des Ordners</string>
     <string name="upload_old_android">Verschlüsselung ist nur möglich mit &gt;= Android 5.0</string>
     <string name="upload_query_move_foreign_files">Es steht nicht genügend Speicherplatz zur Verfügung, um die ausgewählten Dateien in das Verzeichnis %1$s zu kopieren. Sollen diese stattdessen verschoben werden?</string>
+    <string name="upload_quota_exceeded">Speicherkontingent überschritten</string>
     <string name="upload_scan_doc_upload">Dokument von der Kamera scannen</string>
     <string name="upload_sync_conflict">Synchronisierungskonflikt, bitte manuell beheben</string>
     <string name="upload_unknown_error">Unbekannter Fehler</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>

+ 6 - 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>
@@ -880,6 +885,7 @@
     <string name="upload_lock_failed">Produciuse un fallo ao bloquear o cartafol</string>
     <string name="upload_old_android">O cifrado só é posíbel con &gt;= Android 5.0</string>
     <string name="upload_query_move_foreign_files">Non hai espazo abondo para copiar os ficheiros seleccionados no cartafol %1$s. No canto diso, gustaríalle movelos?</string>
+    <string name="upload_quota_exceeded">Superouse a cota de almacenamento</string>
     <string name="upload_scan_doc_upload">Escanear o documento dende a cámara</string>
     <string name="upload_sync_conflict">Conflito ao sincronizar, resólvao manualmente</string>
     <string name="upload_unknown_error">Produciuse un erro descoñecido</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>

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

@@ -18,6 +18,7 @@
     <string name="actionbar_copy">Kopírovať</string>
     <string name="actionbar_mkdir">Nový priečinok</string>
     <string name="actionbar_move">Presunúť</string>
+    <string name="actionbar_move_or_copy">Presunúť alebo Kopírovať</string>
     <string name="actionbar_open_with">Otvoriť pomocou</string>
     <string name="actionbar_search">Hľadať</string>
     <string name="actionbar_see_details">Podrobnosti</string>
@@ -32,6 +33,7 @@
     <string name="activity_icon">Aktivita</string>
     <string name="add_another_public_share_link">Pridať ďalší odkaz</string>
     <string name="add_new_public_share">Pridajte nový odkaz na verejné zdieľanie</string>
+    <string name="add_new_secure_file_drop">Pridať nový zabezpečený file drop</string>
     <string name="add_to_cloud">Pridaj do %1$s</string>
     <string name="advanced_settings">Rozšírené nastavenia</string>
     <string name="allow_resharing">Povoliť sprístupňovanie ďalej</string>
@@ -157,6 +159,8 @@
     <string name="conflict_local_file">Lokálny súbor</string>
     <string name="conflict_message_description">Ak vyberiete obe verzie, miestny súbor bude mať k svojmu názvu pridané číslo.</string>
     <string name="conflict_server_file">Súbor na serveri</string>
+    <string name="contact_backup_title">Záloha kontaktov</string>
+    <string name="contact_no_permission">Vyžaduje sa prístup ku kontaktom</string>
     <string name="contactlist_item_icon">Používateľská ikona pre zoznam kontaktov</string>
     <string name="contactlist_no_permission">Žiadne povolenia neboli udelené, nič nebolo importnuté.</string>
     <string name="contacts">Kontakty</string>
@@ -202,14 +206,21 @@
     <string name="did_not_check_for_dupes">Nekontrolovať duplicity</string>
     <string name="digest_algorithm_not_available">Algoritmus digest nie je na vašom telefóne dostupný.</string>
     <string name="direct_login_failed">Prihlásenie pomocou priameho odkazu zlyhalo!</string>
+    <string name="direct_login_text">Prihlásiť sa s %1$s do %2$s</string>
     <string name="disable_new_media_folder_detection_notifications">Zakázať</string>
     <string name="dismiss">Odmietnuť</string>
     <string name="dismiss_notification_description">Odmietnuť upozornenie</string>
     <string name="displays_mnemonic">Zobrazí vaše prístupové heslo o 12-tich slovách</string>
     <string name="dnd">Nerušiť</string>
+    <string name="document_scan_export_dialog_images">Viacero obrázkov</string>
+    <string name="document_scan_export_dialog_pdf">PDF súbor</string>
+    <string name="document_scan_export_dialog_title">Vyberte typ exportu</string>
+    <string name="document_scan_pdf_generation_failed">Generovanie PDF zlyhalo</string>
+    <string name="document_scan_pdf_generation_in_progress">Generuje sa PDF...</string>
     <string name="done">Hotové</string>
     <string name="dontClear">Nemazať</string>
     <string name="download_cannot_create_file">Nieje možné vytvoriť lokálny súbor</string>
+    <string name="download_download_invalid_local_file_name">Nesprávny názov pre lokálny súbor</string>
     <string name="download_latest_dev_version">Stiahnuť poslednú vývojársku verziu</string>
     <string name="downloader_download_failed_content">Nepodarilo sa stiahnuť %1$s</string>
     <string name="downloader_download_failed_credentials_error">Sťahovanie neúspešné, je potrebné sa znovu prihlásiť</string>
@@ -227,6 +238,7 @@
     <string name="drawer_item_all_files">Všetky súbory</string>
     <string name="drawer_item_favorites">Obľúbené</string>
     <string name="drawer_item_gallery">Média</string>
+    <string name="drawer_item_groupfolders">Skupinové priečinky</string>
     <string name="drawer_item_home">Domov</string>
     <string name="drawer_item_notifications">Upozornenia</string>
     <string name="drawer_item_on_device">V zariadení</string>
@@ -240,10 +252,14 @@
     <string name="drawer_quota_unlimited">%1$s použitých</string>
     <string name="drawer_synced_folders">Automatické nahratie</string>
     <string name="e2e_not_yet_setup">E2E zatiaľ nie je nastavené</string>
+    <string name="e2e_offline">Nie je možné bez internetového pripojenia</string>
     <string name="ecosystem_apps_display_more">Viac</string>
     <string name="ecosystem_apps_display_notes">Poznámky</string>
     <string name="ecosystem_apps_display_talk">Rozhovor</string>
+    <string name="ecosystem_apps_more">Ďalšie Nextcloud aplikácie</string>
     <string name="ecosystem_apps_notes">Nextcloud poznámky</string>
+    <string name="ecosystem_apps_talk">Nextcloud Talk</string>
+    <string name="email_pick_failed">Nepodarilo sa vybrať e-mailovú adresu.</string>
     <string name="encrypted">Nastaviť ako zašifrované</string>
     <string name="end_to_end_encryption_confirm_button">Nastavenie šifrovania</string>
     <string name="end_to_end_encryption_decrypting">Dešifrujem…</string>
@@ -274,7 +290,9 @@
     <string name="error_report_issue_text">Nahlásiť chybu? (vyžaduje účet na Githube)</string>
     <string name="error_retrieving_file">Chyba pri získavaní súboru</string>
     <string name="error_retrieving_templates">Chyba pri načítaní šablón</string>
+    <string name="error_showing_encryption_dialog">Chyba pri zobrazovaní dialogu nastavenia šifrovania!</string>
     <string name="error_starting_direct_camera_upload">Chyba pri otváraní fotoaparátu</string>
+    <string name="error_starting_doc_scan">Chyba pri spustení skenovania dokumentu</string>
     <string name="etm_accounts">Účty</string>
     <string name="etm_background_job_name">Názov úlohy</string>
     <string name="etm_background_job_progress">Pokrok</string>
@@ -371,12 +389,14 @@
     <string name="filename_hint">Názov súboru</string>
     <string name="first_run_1_text">Majte svoje dáta pod vlastnou kontrolou a zabezpečené</string>
     <string name="first_run_2_text">Bezpečná kolaborácia&amp;výmena súborov</string>
+    <string name="first_run_3_text">Jednoduchý webmail, kalendár &amp; kontakty</string>
     <string name="first_run_4_text">Zdieľanie obrazovky, online meetingy &amp; web konferencie</string>
     <string name="folder_already_exists">Priečinok už existuje</string>
     <string name="folder_confirm_create">Vytvoriť</string>
     <string name="folder_list_empty_headline">Nie sú tu žiadne priečinky</string>
     <string name="folder_picker_choose_button_text">Vybrať</string>
     <string name="folder_picker_choose_caption_text">Vyberte cieľový priečinok</string>
+    <string name="folder_picker_copy_button_text">Kopírovať</string>
     <string name="folder_picker_move_button_text">Presunúť</string>
     <string name="forbidden_permissions">Nemáte oprávnenie %s</string>
     <string name="forbidden_permissions_copy">kopírovať súbor</string>
@@ -402,6 +422,19 @@
     <string name="icon_for_empty_list">Ikona pre prázdny zoznam</string>
     <string name="icon_of_dashboard_widget">Ikona widgetu na hlavnom paneli</string>
     <string name="icon_of_widget_entry">Ikona položky ovládacieho panelu</string>
+    <string name="image_editor_file_edited_suffix">upravené</string>
+    <string name="image_editor_flip_horizontal">Preklopiť horizontálne</string>
+    <string name="image_editor_flip_vertical">Preklopiť vertikálne</string>
+    <string name="image_editor_rotate_ccw">Otočť v protismere hodinových ručičiek</string>
+    <string name="image_editor_rotate_cw">Otočiť v smere hodinových ručičiek</string>
+    <string name="image_editor_unable_to_edit_image">Nie je možné upraviť obrázok</string>
+    <string name="image_preview_filedetails">Detaily súboru</string>
+    <string name="image_preview_image_taking_conditions">Podmienky snímania</string>
+    <string name="image_preview_unit_fnumber">ƒ/%s</string>
+    <string name="image_preview_unit_iso">ISO %s</string>
+    <string name="image_preview_unit_megapixel">%s MP</string>
+    <string name="image_preview_unit_millimetres">%s mm</string>
+    <string name="image_preview_unit_seconds">%s s</string>
     <string name="in_folder">v priečinku %1$s</string>
     <string name="instant_upload_existing">Nahrať aj existujúce súbory</string>
     <string name="instant_upload_on_charging">Nahrať iba počas nabíjania</string>
@@ -429,6 +462,7 @@
     <string name="locked_by_app">Zamknuté od aplikácie %1$s</string>
     <string name="log_send_mail_subject">%1$s Android app logs</string>
     <string name="log_send_no_mail_app">Aplikácia na odoslanie logov nenájdená. Prosím nainštalujte emailovú aplikáciu.</string>
+    <string name="logged_in_as">Prihlásený ako %1$s</string>
     <string name="login">Prihlásiť sa</string>
     <string name="login_url_helper_text">Odkaz k vašemu %1$s webovému rozhraniu keď ho otvoríte v prehliadači. </string>
     <string name="logs_menu_delete">Zmazať záznamy</string>
@@ -481,6 +515,7 @@
     <string name="no_calendar_exists">Neexistuje žiadny kalendár</string>
     <string name="no_email_app_available">Aplikácia pre prácu s e-mailovými adresami nenájdená</string>
     <string name="no_items">Žiadne položky</string>
+    <string name="no_map_app_availble">Nie je dostupná žiadna aplikácia na spracovanie máp.</string>
     <string name="no_mutliple_accounts_allowed">Povolený je iba jeden účet</string>
     <string name="no_pdf_app_available">Aplikácia na prácu s PDF nenájdená</string>
     <string name="no_send_app">Nie je k dispozícii žiadna aplikácia na odosielanie vybratých súborov</string>
@@ -519,6 +554,7 @@
     <string name="pass_code_removed">Bezpečnostný kód odstránený</string>
     <string name="pass_code_stored">Bezpečnostný kód uložený</string>
     <string name="pass_code_wrong">Nesprávne heslo</string>
+    <string name="pdf_password_protected">Nepodarilo sa otvoriť heslom chránený PDF súbor. Prosím, použite externý PDF prehliadač.</string>
     <string name="pdf_zoom_tip">Klepnutím na stránku priblížite</string>
     <string name="permission_allow">Povoliť</string>
     <string name="permission_deny">Odmietnuť</string>
@@ -555,6 +591,7 @@
     <string name="prefs_category_more">Viac</string>
     <string name="prefs_daily_backup_summary">Denná záloha vašeho kalendára &amp; kontaktov</string>
     <string name="prefs_daily_contact_backup_summary">Denná záloha vašich kontaktov</string>
+    <string name="prefs_davx5_setup_error">Neočakávaná chyba pri nastavovaní DAVx5 (predtým známy ako DAVdroid)</string>
     <string name="prefs_e2e_active">End-to-end šifrovanie je nastavené!</string>
     <string name="prefs_e2e_mnemonic">E2E mnemotechnické</string>
     <string name="prefs_e2e_no_device_credentials">Pre zobrazenie mnemotechnické, zapnite prihlasovacie údaje zariadenia.</string>
@@ -565,7 +602,9 @@
     <string name="prefs_imprint">Podmienky používania</string>
     <string name="prefs_instant_behaviour_dialogTitle">Pôvodný súbor bude…</string>
     <string name="prefs_instant_behaviour_title">Pôvodný súbor bude…</string>
+    <string name="prefs_instant_upload_path_use_date_subfolders_summary">Uložiť v podpriečinkoch podľa dátumu</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Použi podpriečinky</string>
+    <string name="prefs_instant_upload_subfolder_rule_title">Možnosti podpriečinka</string>
     <string name="prefs_keys_exist">Pridať end-to-end šifrovanie do tohto klienta</string>
     <string name="prefs_license">Licencia</string>
     <string name="prefs_lock">Prístupový kód apky</string>
@@ -579,6 +618,8 @@
     <string name="prefs_recommend">Odporučiť priateľom</string>
     <string name="prefs_remove_e2e">Lokálne odstrániť šifrovanie</string>
     <string name="prefs_setup_e2e">Nastaviť šifrovanie end-to-end</string>
+    <string name="prefs_show_ecosystem_apps">Zobraziť prepínač aplikácií</string>
+    <string name="prefs_show_ecosystem_apps_summary">Návrhy Nextcloud aplikácií v navigačnom záhlaví</string>
     <string name="prefs_show_hidden_files">Zobraziť skryté súbory</string>
     <string name="prefs_sourcecode">Získajte zdrojový kód</string>
     <string name="prefs_storage_path">Priečinok dátového úložiska</string>
@@ -606,13 +647,20 @@
     <string name="reload">Znovu načítať</string>
     <string name="remote">(vzdialené)</string>
     <string name="remote_file_fetch_failed">Nepodarilo sa nájsť súbor</string>
+    <string name="remove_e2e">Môžete odstrániť end-to-end šifrovanie lokálne na tomto klientovi.</string>
+    <string name="remove_e2e_message">Môžete odstrániť end-to-end šifrovanie lokálne na tomto klientovi. Šifrované súbory zostanú na serveri, ale už nebudú synchronizované s týmto počítačom.</string>
     <string name="remove_fail_msg">Zmazanie zlyhalo</string>
+    <string name="remove_local_account">Odstrániť lokálny účet</string>
+    <string name="remove_local_account_details">Odstrániť účet zo zariadenia a odstrániť všetky lokálne súbory</string>
     <string name="remove_notification_failed">Odstránenie upozornenia zlyhalo.</string>
     <string name="remove_push_notification">Odstrániť</string>
     <string name="remove_success_msg">Zmazané</string>
     <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="request_account_deletion_button">Žiadosť o zmazanie</string>
+    <string name="request_account_deletion_details">Žiadosť o trvalé odstránenie účtu poskytovateľom</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>
@@ -623,6 +671,7 @@
     <string name="retrieving_file">Získava sa súbor…</string>
     <string name="richdocuments_failed_to_load_document">Nepodarilo sa načítať dokument!</string>
     <string name="scanQR_description">Prihlásiť sa pomocou QR kódu</string>
+    <string name="scan_page">Skenovať stránku</string>
     <string name="screenshot_01_gridView_heading">Chránte vaše údaje</string>
     <string name="screenshot_01_gridView_subline">Nástroje pre produktivitu s vlastným hosťovaním</string>
     <string name="screenshot_02_listView_heading">Prehliadajte a zdieľajte</string>
@@ -649,6 +698,7 @@
     <string name="set_picture_as">Použiť obrázok ako</string>
     <string name="set_status">Nastaviť stav</string>
     <string name="set_status_message">Nastaviť správu o stave</string>
+    <string name="setup_e2e">Počas nastavovania end-to-end šifrovania dostanete náhodný 12-slovový mnemotechnický záznam, ktorý budete potrebovať na otvorenie svojich súborov na iných zariadeniach. Tento záznam bude uložený iba na tomto zariadení a môže byť znovu zobrazený na tejto obrazovke. Prosím, zapíšte si ho na bezpečné miesto!</string>
     <string name="share">Zdieľať</string>
     <string name="share_copy_link">Zdieľať &amp; Kopírovať Odkaz</string>
     <string name="share_dialog_title">Sprístupnenie</string>
@@ -673,6 +723,7 @@
     <string name="share_password_title">Chránené heslom</string>
     <string name="share_permission_can_edit">Môže upravovať</string>
     <string name="share_permission_file_drop">Vložiť súbor</string>
+    <string name="share_permission_secure_file_drop">Zabezpečený file drop</string>
     <string name="share_permission_view_only">Iba pre čítanie</string>
     <string name="share_permissions">Zdieľať oprávnenia</string>
     <string name="share_remote_clarification">%1$s (vzdialený)</string>
@@ -748,6 +799,8 @@
     <string name="stream_not_possible_headline">Vnútorné streamovanie nie je možné</string>
     <string name="stream_not_possible_message">Médium si namiesto toho stiahnite alebo použite externú aplikáciu.</string>
     <string name="strict_mode">Striktný mód: HTTP spojenie nie je povolené!</string>
+    <string name="sub_folder_rule_day">Rok/Mesiac/Deň</string>
+    <string name="sub_folder_rule_month">Rok/Mesiac</string>
     <string name="sub_folder_rule_year">Rok</string>
     <string name="subject_shared_with_you">\"%1$s\" vám bol sprístupnený</string>
     <string name="subject_user_shared_with_you">%1$s vám sprístupnil \"%2$s\"</string>
@@ -787,6 +840,7 @@
     <string name="thumbnail">Náhľad</string>
     <string name="thumbnail_for_existing_file_description">Náhľad existujúceho súboru</string>
     <string name="thumbnail_for_new_file_desc">Náhľad nového súboru</string>
+    <string name="timeout_richDocuments">Načítavanie trvá dlhšie ako sa očakávalo</string>
     <string name="today">Dnes</string>
     <string name="trashbin_activity_title">Zmazané súbory</string>
     <string name="trashbin_empty_headline">Žiadne zmazané súbory</string>
@@ -830,6 +884,7 @@
     <string name="upload_lock_failed">Uzamknutie priečinka zlyhalo</string>
     <string name="upload_old_android">Šifrovanie je možné iba pri &gt;= Android 5.0</string>
     <string name="upload_query_move_foreign_files">Pre skopírovanie vybratých súborov do adresára %1$s nie je dostatok voľného miesta. Chcete ich namiesto toho presunúť?</string>
+    <string name="upload_quota_exceeded">Kapacita úložiska prekročená</string>
     <string name="upload_scan_doc_upload">Naskenujte dokument z fotoaparátu</string>
     <string name="upload_sync_conflict">Chyba synchronizácie, prosím vyriešte manuálne</string>
     <string name="upload_unknown_error">Neznáma chyba</string>
@@ -898,7 +953,9 @@
     <string name="wait_a_moment">Počkajte chvíľu…</string>
     <string name="wait_checking_credentials">Overujem uložené prihlasovacie údaje</string>
     <string name="wait_for_tmp_copy_from_private_storage">Kopírovanie súboru z privátneho úložiska</string>
+    <string name="webview_version_check_alert_dialog_message">Pre prihlásenie prosím aktualizujte aplikáciu Android System WebView</string>
     <string name="webview_version_check_alert_dialog_positive_button_title">Aktualizovať</string>
+    <string name="webview_version_check_alert_dialog_title">Aktualizujte Android System WebView</string>
     <string name="what_s_new_image">Obrázok čo je nové</string>
     <string name="whats_new_skip">Preskočiť</string>
     <string name="whats_new_title">Nové v %1$s</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>

+ 6 - 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>
@@ -879,6 +884,7 @@
     <string name="upload_lock_failed">Неуспело закључавање фасцикле</string>
     <string name="upload_old_android">Шифровање је могуће само са &gt;= Андроидом 5.0</string>
     <string name="upload_query_move_foreign_files">Недостатак простора  спречава копирање фајлова у фасциклу %1$s. Желите ли да их преместите тамо?</string>
+    <string name="upload_quota_exceeded">Прекорачена је квота за складиште</string>
     <string name="upload_scan_doc_upload">Скенирање документа камером</string>
     <string name="upload_sync_conflict">Сукоб синхронизације. Разрешите га ручно</string>
     <string name="upload_unknown_error">Непозната грешка</string>

+ 6 - 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>
@@ -880,6 +885,7 @@
     <string name="upload_lock_failed">Misslyckades låsa mappen</string>
     <string name="upload_old_android">Kryptering är endast möjlig med &gt;= Android 5.0</string>
     <string name="upload_query_move_foreign_files">Otillräckligt utrymme förhindrar kopiering av de valda filerna till %1$s -mappen. Vill du flytta dem dit i stället?</string>
+    <string name="upload_quota_exceeded">Lagringskvot överskriden</string>
     <string name="upload_scan_doc_upload">Slams dokument från kamera</string>
     <string name="upload_sync_conflict">Synkroniseringskonflikt, lös problemet manuellt</string>
     <string name="upload_unknown_error">Okänt fel</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>

+ 7 - 2
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>
@@ -726,7 +731,7 @@
     <string name="shared_icon_share">поділитися</string>
     <string name="shared_icon_shared">надано доступ</string>
     <string name="shared_icon_shared_via_link">доступ надано за посиланням</string>
-    <string name="shared_with_you_by">%1$s поділився з вами</string>
+    <string name="shared_with_you_by">%1$s поділив(-ла-)ся з вами</string>
     <string name="sharee_add_failed">Помилка додавання користувача, з яким ви хочете поділитися</string>
     <string name="show_images">Показувати зображення</string>
     <string name="show_video">Показувати відео</string>
@@ -788,7 +793,7 @@
     <string name="sub_folder_rule_month">Рік/місяць</string>
     <string name="sub_folder_rule_year">Рік</string>
     <string name="subject_shared_with_you">Вам було надано доступ до \"%1$s\"</string>
-    <string name="subject_user_shared_with_you">%1$s поділився %2$s з вами</string>
+    <string name="subject_user_shared_with_you">%1$s поділив(-ла-)ся %2$s з вами</string>
     <string name="subtitle_photos_only">Лише зображення</string>
     <string name="subtitle_photos_videos">Зображення та відео</string>
     <string name="subtitle_videos_only">Лише відео</string>

+ 6 - 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>
@@ -882,6 +887,7 @@
     <string name="upload_lock_failed">锁定文件夹失败</string>
     <string name="upload_old_android">加密功能仅适用于安卓 5.0 及以上版本</string>
     <string name="upload_query_move_foreign_files">空间不足将阻止将所选文件复制到%1$s文件夹中。 你想把它们移到那里吗?</string>
+    <string name="upload_quota_exceeded">超出存储限额</string>
     <string name="upload_scan_doc_upload">使用相机扫描文档</string>
     <string name="upload_sync_conflict">同步时发生异常,请手动同步</string>
     <string name="upload_unknown_error">未知错误</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>

+ 6 - 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>
@@ -879,6 +884,7 @@
     <string name="upload_lock_failed">鎖定資料夾失敗</string>
     <string name="upload_old_android">加密功能僅適用於 Android 5.0 及更新版本</string>
     <string name="upload_query_move_foreign_files">空間不足以將選擇的檔案複製到 %1$s 資料夾,是否要改成移動它們?</string>
+    <string name="upload_quota_exceeded">超過儲存空間配額</string>
     <string name="upload_scan_doc_upload">使用相機掃描文件</string>
     <string name="upload_sync_conflict">同步發生衝突,請手動處理</string>
     <string name="upload_unknown_error">未知的錯誤</string>

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

@@ -831,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>

+ 0 - 7
app/src/main/res/values/styles.xml

@@ -233,13 +233,6 @@
         <item name="android:textStyle">bold</item>
     </style>
 
-    <style name="Button.Borderless.Destructive" parent="Widget.Material3.Button.TextButton">
-        <item name="android:textColor">@color/highlight_textColor_Warning</item>
-        <item name="android:textAllCaps">false</item>
-        <item name="android:typeface">sans</item>
-        <item name="android:textStyle">bold</item>
-    </style>
-
     <style name="Button.Borderless.Login" parent="Widget.Material3.Button.TextButton">
         <item name="android:textColor">@color/fg_inverse</item>
         <item name="android:textAllCaps">false</item>

+ 1 - 1
build.gradle

@@ -1,6 +1,6 @@
 buildscript {
     ext {
-        androidPluginVersion = '8.1.4'
+        androidPluginVersion = '8.2.0'
         appCompatVersion = '1.6.1'
         jacoco_version = '0.8.10'
         kotlin_version = '1.8.22'

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