Browse Source

Merge pull request #9228 from nextcloud/manage-all-files

Ditch scoped storage in favor of MANAGE_EXTERNAL_STORAGE
Álvaro Brey 3 years ago
parent
commit
38e78b3f37
22 changed files with 123 additions and 98 deletions
  1. 8 8
      src/androidTest/java/com/nextcloud/client/EndToEndRandomIT.java
  2. 23 1
      src/androidTest/java/com/nextcloud/client/GrantStoragePermissionRule.kt
  3. 2 2
      src/androidTest/java/com/owncloud/android/AbstractIT.java
  4. 2 2
      src/androidTest/java/com/owncloud/android/DownloadIT.java
  5. 17 17
      src/androidTest/java/com/owncloud/android/UploadIT.java
  6. 2 3
      src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt
  7. 3 6
      src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt
  8. 0 8
      src/debug/AndroidManifest.xml
  9. 4 1
      src/main/AndroidManifest.xml
  10. 0 2
      src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt
  11. 3 17
      src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt
  12. 3 5
      src/main/java/com/owncloud/android/operations/DownloadFileOperation.java
  13. 2 5
      src/main/java/com/owncloud/android/operations/RenameFileOperation.java
  14. 2 3
      src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java
  15. 1 1
      src/main/java/com/owncloud/android/services/OperationsService.java
  16. 5 3
      src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  17. 1 1
      src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  18. 1 1
      src/main/java/com/owncloud/android/ui/activity/RichDocumentsEditorWebView.java
  19. 1 1
      src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java
  20. 1 2
      src/main/java/com/owncloud/android/utils/FileStorageUtils.java
  21. 40 9
      src/main/java/com/owncloud/android/utils/PermissionUtil.kt
  22. 2 0
      src/main/res/values/strings.xml

+ 8 - 8
src/androidTest/java/com/nextcloud/client/EndToEndRandomIT.java

@@ -378,13 +378,13 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
     public void testUploadWithCopy() throws Exception {
     public void testUploadWithCopy() throws Exception {
         init();
         init();
 
 
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_COPY);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_COPY);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
                                                                                       "nonEmpty.txt");
                                                                                       "nonEmpty.txt");
 
 
@@ -396,13 +396,13 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
     public void testUploadWithMove() throws Exception {
     public void testUploadWithMove() throws Exception {
         init();
         init();
 
 
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_MOVE);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_MOVE);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
                                                                                       "nonEmpty.txt");
                                                                                       "nonEmpty.txt");
 
 
@@ -414,13 +414,13 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
     public void testUploadWithForget() throws Exception {
     public void testUploadWithForget() throws Exception {
         init();
         init();
 
 
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_FORGET);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_FORGET);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
                                                                                       "nonEmpty.txt");
                                                                                       "nonEmpty.txt");
 
 
@@ -432,13 +432,13 @@ public class EndToEndRandomIT extends AbstractOnServerIT {
     public void testUploadWithDelete() throws Exception {
     public void testUploadWithDelete() throws Exception {
         init();
         init();
 
 
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          currentFolder.getRemotePath() + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_DELETE);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_DELETE);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(currentFolder.getRemotePath() +
                                                                                       "nonEmpty.txt");
                                                                                       "nonEmpty.txt");
 
 

+ 23 - 1
src/androidTest/java/com/nextcloud/client/GrantStoragePermissionRule.kt

@@ -21,14 +21,36 @@
 
 
 package com.nextcloud.client
 package com.nextcloud.client
 
 
+import android.os.Build
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.rule.GrantPermissionRule
 import androidx.test.rule.GrantPermissionRule
 import com.owncloud.android.utils.PermissionUtil
 import com.owncloud.android.utils.PermissionUtil
 import org.junit.rules.TestRule
 import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
 
 
 class GrantStoragePermissionRule private constructor() {
 class GrantStoragePermissionRule private constructor() {
 
 
     companion object {
     companion object {
         @JvmStatic
         @JvmStatic
-        fun grant(): TestRule = GrantPermissionRule.grant(PermissionUtil.getExternalStoragePermission())
+        fun grant(): TestRule = when {
+            Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> GrantPermissionRule.grant(
+                PermissionUtil
+                    .getExternalStoragePermission()
+            )
+            else -> GrantManageExternalStoragePermissionRule()
+        }
+    }
+
+    private class GrantManageExternalStoragePermissionRule : TestRule {
+        override fun apply(base: Statement, description: Description): Statement = object : Statement() {
+            override fun evaluate() {
+                InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand(
+                    "appops set --uid ${InstrumentationRegistry.getInstrumentation().targetContext.packageName} " +
+                        "MANAGE_EXTERNAL_STORAGE allow"
+                )
+                base.evaluate()
+            }
+        }
     }
     }
 }
 }

+ 2 - 2
src/androidTest/java/com/owncloud/android/AbstractIT.java

@@ -205,7 +205,7 @@ public abstract class AbstractIT {
     }
     }
 
 
     protected static void createDummyFiles() throws IOException {
     protected static void createDummyFiles() throws IOException {
-        File tempPath = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext));
+        File tempPath = new File(FileStorageUtils.getTemporalPath(account.name));
         if (!tempPath.exists()) {
         if (!tempPath.exists()) {
             assertTrue(tempPath.mkdirs());
             assertTrue(tempPath.mkdirs());
         }
         }
@@ -243,7 +243,7 @@ public abstract class AbstractIT {
     }
     }
 
 
     public static File createFile(String name, int iteration) throws IOException {
     public static File createFile(String name, int iteration) throws IOException {
-        File file = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + File.separator + name);
+        File file = new File(FileStorageUtils.getTemporalPath(account.name) + File.separator + name);
         if (!file.getParentFile().exists()) {
         if (!file.getParentFile().exists()) {
             assertTrue(file.getParentFile().mkdirs());
             assertTrue(file.getParentFile().mkdirs());
         }
         }

+ 2 - 2
src/androidTest/java/com/owncloud/android/DownloadIT.java

@@ -73,13 +73,13 @@ public class DownloadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void verifyDownload() {
     public void verifyDownload() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload);
         uploadOCUpload(ocUpload);
 
 
-        OCUpload ocUpload2 = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload2 = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                           FOLDER + "nonEmpty2.txt",
                                           FOLDER + "nonEmpty2.txt",
                                           account.name);
                                           account.name);
 
 

+ 17 - 17
src/androidTest/java/com/owncloud/android/UploadIT.java

@@ -122,7 +122,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testEmptyUpload() {
     public void testEmptyUpload() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "empty.txt",
                                          FOLDER + "empty.txt",
                                          account.name);
                                          account.name);
 
 
@@ -131,7 +131,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testNonEmptyUpload() {
     public void testNonEmptyUpload() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
@@ -140,13 +140,13 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadWithCopy() {
     public void testUploadWithCopy() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_COPY);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_COPY);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
 
 
         assertTrue(originalFile.exists());
         assertTrue(originalFile.exists());
@@ -156,13 +156,13 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadWithMove() {
     public void testUploadWithMove() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_MOVE);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_MOVE);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
 
 
         assertFalse(originalFile.exists());
         assertFalse(originalFile.exists());
@@ -172,13 +172,13 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadWithForget() {
     public void testUploadWithForget() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_FORGET);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_FORGET);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
 
 
         assertTrue(originalFile.exists());
         assertTrue(originalFile.exists());
@@ -188,13 +188,13 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadWithDelete() {
     public void testUploadWithDelete() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          FOLDER + "nonEmpty.txt",
                                          account.name);
                                          account.name);
 
 
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_DELETE);
         uploadOCUpload(ocUpload, FileUploader.LOCAL_BEHAVIOUR_DELETE);
 
 
-        File originalFile = new File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/nonEmpty.txt");
+        File originalFile = new File(FileStorageUtils.getTemporalPath(account.name) + "/nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
         OCFile uploadedFile = fileDataStorageManager.getFileByDecryptedRemotePath(FOLDER + "nonEmpty.txt");
 
 
         assertFalse(originalFile.exists());
         assertFalse(originalFile.exists());
@@ -204,7 +204,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testChunkedUpload() {
     public void testChunkedUpload() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/chunkedFile.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/chunkedFile.txt",
                                          FOLDER + "chunkedFile.txt", account.name);
                                          FOLDER + "chunkedFile.txt", account.name);
 
 
         uploadOCUpload(ocUpload);
         uploadOCUpload(ocUpload);
@@ -212,7 +212,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadInNonExistingFolder() {
     public void testUploadInNonExistingFolder() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "2/3/4/1.txt", account.name);
                                          FOLDER + "2/3/4/1.txt", account.name);
 
 
         uploadOCUpload(ocUpload);
         uploadOCUpload(ocUpload);
@@ -220,7 +220,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadOnChargingOnlyButNotCharging() {
     public void testUploadOnChargingOnlyButNotCharging() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "notCharging.txt", account.name);
                                          FOLDER + "notCharging.txt", account.name);
         ocUpload.setWhileChargingOnly(true);
         ocUpload.setWhileChargingOnly(true);
 
 
@@ -268,7 +268,7 @@ public class UploadIT extends AbstractOnServerIT {
             }
             }
         };
         };
 
 
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "charging.txt", account.name);
                                          FOLDER + "charging.txt", account.name);
         ocUpload.setWhileChargingOnly(true);
         ocUpload.setWhileChargingOnly(true);
 
 
@@ -308,7 +308,7 @@ public class UploadIT extends AbstractOnServerIT {
                 return new Connectivity(true, false, false, true);
                 return new Connectivity(true, false, false, true);
             }
             }
         };
         };
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "noWifi.txt", account.name);
                                          FOLDER + "noWifi.txt", account.name);
         ocUpload.setUseWifiOnly(true);
         ocUpload.setUseWifiOnly(true);
 
 
@@ -338,7 +338,7 @@ public class UploadIT extends AbstractOnServerIT {
 
 
     @Test
     @Test
     public void testUploadOnWifiOnlyAndWifi() {
     public void testUploadOnWifiOnlyAndWifi() {
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "wifi.txt", account.name);
                                          FOLDER + "wifi.txt", account.name);
         ocUpload.setWhileChargingOnly(true);
         ocUpload.setWhileChargingOnly(true);
 
 
@@ -387,7 +387,7 @@ public class UploadIT extends AbstractOnServerIT {
                 return new Connectivity(true, true, true, true);
                 return new Connectivity(true, true, true, true);
             }
             }
         };
         };
-        OCUpload ocUpload = new OCUpload(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + "/empty.txt",
+        OCUpload ocUpload = new OCUpload(FileStorageUtils.getTemporalPath(account.name) + "/empty.txt",
                                          FOLDER + "noWifi.txt",
                                          FOLDER + "noWifi.txt",
                                          account.name);
                                          account.name);
         ocUpload.setUseWifiOnly(true);
         ocUpload.setUseWifiOnly(true);

+ 2 - 3
src/androidTest/java/com/owncloud/android/utils/FileUtilTest.kt

@@ -43,8 +43,7 @@ class FileUtilTest : AbstractIT() {
 
 
     @Test
     @Test
     fun assertSlashInput() {
     fun assertSlashInput() {
-        val tempPath =
-            File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + File.pathSeparator + "folder")
+        val tempPath = File(FileStorageUtils.getTemporalPath(account.name) + File.pathSeparator + "folder")
         if (!tempPath.exists()) {
         if (!tempPath.exists()) {
             Assert.assertTrue(tempPath.mkdirs())
             Assert.assertTrue(tempPath.mkdirs())
         }
         }
@@ -59,7 +58,7 @@ class FileUtilTest : AbstractIT() {
 
 
     @Test
     @Test
     fun assertFolderInput() {
     fun assertFolderInput() {
-        val tempPath = File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext))
+        val tempPath = File(FileStorageUtils.getTemporalPath(account.name))
         if (!tempPath.exists()) {
         if (!tempPath.exists()) {
             Assert.assertTrue(tempPath.mkdirs())
             Assert.assertTrue(tempPath.mkdirs())
         }
         }

+ 3 - 6
src/androidTest/java/com/owncloud/android/utils/SyncedFolderUtilsTest.kt

@@ -195,10 +195,7 @@ class SyncedFolderUtilsTest : AbstractIT() {
         getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_JPEG)
         getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_JPEG)
         getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_BITMAP)
         getDummyFile(THUMBNAILS_FOLDER + File.separatorChar + IMAGE_BITMAP)
         val folder = SyncedFolder(
         val folder = SyncedFolder(
-            FileStorageUtils.getInternalTemporalPath(
-                account.name,
-                targetContext
-            ) + File.separatorChar + THUMBNAILS_FOLDER,
+            FileStorageUtils.getTemporalPath(account.name) + File.separatorChar + THUMBNAILS_FOLDER,
             "",
             "",
             true,
             true,
             false,
             false,
@@ -235,7 +232,7 @@ class SyncedFolderUtilsTest : AbstractIT() {
         fun setUp() {
         fun setUp() {
             val tempPath =
             val tempPath =
                 File(
                 File(
-                    FileStorageUtils.getInternalTemporalPath(account.name, targetContext) + File.separatorChar +
+                    FileStorageUtils.getTemporalPath(account.name) + File.separatorChar +
                         THUMBNAILS_FOLDER
                         THUMBNAILS_FOLDER
                 )
                 )
             if (!tempPath.exists()) {
             if (!tempPath.exists()) {
@@ -260,7 +257,7 @@ class SyncedFolderUtilsTest : AbstractIT() {
         @AfterClass
         @AfterClass
         @JvmStatic
         @JvmStatic
         fun tearDown() {
         fun tearDown() {
-            FileUtils.deleteDirectory(File(FileStorageUtils.getInternalTemporalPath(account.name, targetContext)))
+            FileUtils.deleteDirectory(File(FileStorageUtils.getTemporalPath(account.name)))
         }
         }
     }
     }
 }
 }

+ 0 - 8
src/debug/AndroidManifest.xml

@@ -4,14 +4,6 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
 
 
-    <!-- Allows for storing and retrieving screenshots -->
-    <uses-permission
-        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
-        android:maxSdkVersion="29"
-        tools:ignore="ScopedStorage" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-
     <!-- Allows changing locales -->
     <!-- Allows changing locales -->
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"
         tools:ignore="ProtectedPermissions" />
         tools:ignore="ProtectedPermissions" />

+ 4 - 1
src/main/AndroidManifest.xml

@@ -32,7 +32,10 @@
         android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:maxSdkVersion="29"
         android:maxSdkVersion="29"
         tools:ignore="ScopedStorage" />
         tools:ignore="ScopedStorage" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission
+        android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" />
+
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.VIBRATE" />
 
 

+ 0 - 2
src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt

@@ -195,10 +195,8 @@ class AccountRemovalWork(
 
 
     private fun removeFiles(user: User, storageManager: FileDataStorageManager) {
     private fun removeFiles(user: User, storageManager: FileDataStorageManager) {
         val tempDir = File(FileStorageUtils.getTemporalPath(user.accountName))
         val tempDir = File(FileStorageUtils.getTemporalPath(user.accountName))
-        val internalTempDir = File(FileStorageUtils.getInternalTemporalPath(user.accountName, context))
         val saveDir = File(FileStorageUtils.getSavePath(user.accountName))
         val saveDir = File(FileStorageUtils.getSavePath(user.accountName))
         FileStorageUtils.deleteRecursively(tempDir, storageManager)
         FileStorageUtils.deleteRecursively(tempDir, storageManager)
-        FileStorageUtils.deleteRecursively(internalTempDir, storageManager)
         FileStorageUtils.deleteRecursively(saveDir, storageManager)
         FileStorageUtils.deleteRecursively(saveDir, storageManager)
     }
     }
 
 

+ 3 - 17
src/main/java/com/nextcloud/client/onboarding/OnboardingServiceImpl.kt

@@ -22,7 +22,6 @@ import android.app.Activity
 import android.content.Context
 import android.content.Context
 import android.content.Intent
 import android.content.Intent
 import android.content.res.Resources
 import android.content.res.Resources
-import android.os.Build
 import com.nextcloud.client.account.CurrentAccountProvider
 import com.nextcloud.client.account.CurrentAccountProvider
 import com.nextcloud.client.preferences.AppPreferences
 import com.nextcloud.client.preferences.AppPreferences
 import com.owncloud.android.BuildConfig
 import com.owncloud.android.BuildConfig
@@ -38,7 +37,7 @@ internal class OnboardingServiceImpl constructor(
 ) : OnboardingService {
 ) : OnboardingService {
 
 
     private companion object {
     private companion object {
-        const val ITEM_VERSION_CODE = 30185300
+        const val ITEM_VERSION_CODE = 99999999
     }
     }
 
 
     private val notSeenYet: Boolean
     private val notSeenYet: Boolean
@@ -47,21 +46,8 @@ internal class OnboardingServiceImpl constructor(
         }
         }
 
 
     override val whatsNew: Array<FeatureItem>
     override val whatsNew: Array<FeatureItem>
-        get() = if (!isFirstRun && notSeenYet && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-            arrayOf(
-                FeatureItem(
-                    R.drawable.folder_alert_outline, R.string.whats_new_storage_sdk30_title,
-                    R.string
-                        .whats_new_storage_sdk30_content,
-                    true, false
-                ),
-                FeatureItem(
-                    R.drawable.folder_alert_outline, R.string.whats_new_storage_sdk30_title,
-                    R.string
-                        .whats_new_storage_sdk30_content_page2,
-                    true, false
-                )
-            )
+        get() = if (!isFirstRun && notSeenYet) {
+            emptyArray()
         } else {
         } else {
             emptyArray()
             emptyArray()
         }
         }

+ 3 - 5
src/main/java/com/owncloud/android/operations/DownloadFileOperation.java

@@ -23,7 +23,6 @@ package com.owncloud.android.operations;
 
 
 import android.accounts.Account;
 import android.accounts.Account;
 import android.content.Context;
 import android.content.Context;
-import android.os.FileUtils;
 import android.text.TextUtils;
 import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 import android.webkit.MimeTypeMap;
 
 
@@ -105,11 +104,11 @@ public class DownloadFileOperation extends RemoteOperation {
     }
     }
 
 
     public String getTmpPath() {
     public String getTmpPath() {
-        return FileStorageUtils.getInternalTemporalPath(account.name, context) + file.getRemotePath();
+        return FileStorageUtils.getTemporalPath(account.name) + file.getRemotePath();
     }
     }
 
 
     public String getTmpFolder() {
     public String getTmpFolder() {
-        return FileStorageUtils.getInternalTemporalPath(account.name, context);
+        return FileStorageUtils.getTemporalPath(account.name);
     }
     }
 
 
     public String getRemotePath() {
     public String getRemotePath() {
@@ -204,8 +203,7 @@ public class DownloadFileOperation extends RemoteOperation {
                     return new RemoteOperationResult(e);
                     return new RemoteOperationResult(e);
                 }
                 }
             }
             }
-
-            moved = FileStorageUtils.moveFile(tmpFile, newFile);
+            moved = tmpFile.renameTo(newFile);
             newFile.setLastModified(file.getModificationTimestamp());
             newFile.setLastModified(file.getModificationTimestamp());
             if (!moved) {
             if (!moved) {
                 result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
                 result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);

+ 2 - 5
src/main/java/com/owncloud/android/operations/RenameFileOperation.java

@@ -21,7 +21,6 @@
 
 
 package com.owncloud.android.operations;
 package com.owncloud.android.operations;
 
 
-import android.content.Context;
 import android.text.TextUtils;
 import android.text.TextUtils;
 
 
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.FileDataStorageManager;
@@ -49,7 +48,6 @@ public class RenameFileOperation extends SyncOperation {
     private OCFile file;
     private OCFile file;
     private String remotePath;
     private String remotePath;
     private String newName;
     private String newName;
-    private Context context;
 
 
     /**
     /**
      * Constructor
      * Constructor
@@ -57,12 +55,11 @@ public class RenameFileOperation extends SyncOperation {
      * @param remotePath RemotePath of the OCFile instance describing the remote file or folder to rename
      * @param remotePath RemotePath of the OCFile instance describing the remote file or folder to rename
      * @param newName    New name to set as the name of file.
      * @param newName    New name to set as the name of file.
      */
      */
-    public RenameFileOperation(String remotePath, String newName, FileDataStorageManager storageManager, Context context) {
+    public RenameFileOperation(String remotePath, String newName, FileDataStorageManager storageManager) {
         super(storageManager);
         super(storageManager);
 
 
         this.remotePath = remotePath;
         this.remotePath = remotePath;
         this.newName = newName;
         this.newName = newName;
-        this.context = context;
     }
     }
 
 
     /**
     /**
@@ -171,7 +168,7 @@ public class RenameFileOperation extends SyncOperation {
             return false;
             return false;
         }
         }
         // create a test file
         // create a test file
-        String tmpFolderName = FileStorageUtils.getInternalTemporalPath("", context);
+        String tmpFolderName = FileStorageUtils.getTemporalPath("");
         File testFile = new File(tmpFolderName + newName);
         File testFile = new File(tmpFolderName + newName);
         File tmpFolder = testFile.getParentFile();
         File tmpFolder = testFile.getParentFile();
         if (! tmpFolder.mkdirs()) {
         if (! tmpFolder.mkdirs()) {

+ 2 - 3
src/main/java/com/owncloud/android/providers/DocumentsStorageProvider.java

@@ -343,8 +343,7 @@ public class DocumentsStorageProvider extends DocumentsProvider {
 
 
         RemoteOperationResult result = new RenameFileOperation(document.getRemotePath(),
         RemoteOperationResult result = new RenameFileOperation(document.getRemotePath(),
                                                                displayName,
                                                                displayName,
-                                                               document.getStorageManager(),
-                                                               getNonNullContext())
+                                                               document.getStorageManager())
             .execute(document.getClient());
             .execute(document.getClient());
 
 
         if (!result.isSuccess()) {
         if (!result.isSuccess()) {
@@ -509,7 +508,7 @@ public class DocumentsStorageProvider extends DocumentsProvider {
         User user = targetFolder.getUser();
         User user = targetFolder.getUser();
 
 
         // create dummy file
         // create dummy file
-        File tempDir = new File(FileStorageUtils.getInternalTemporalPath(user.getAccountName(), getNonNullContext()));
+        File tempDir = new File(FileStorageUtils.getTemporalPath(user.getAccountName()));
 
 
         if (!tempDir.exists() && !tempDir.mkdirs()) {
         if (!tempDir.exists() && !tempDir.mkdirs()) {
             throw new FileNotFoundException("Temp folder could not be created: " + tempDir.getAbsolutePath());
             throw new FileNotFoundException("Temp folder could not be created: " + tempDir.getAbsolutePath());

+ 1 - 1
src/main/java/com/owncloud/android/services/OperationsService.java

@@ -658,7 +658,7 @@ public class OperationsService extends Service {
                     case ACTION_RENAME:
                     case ACTION_RENAME:
                         remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                         remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                         String newName = operationIntent.getStringExtra(EXTRA_NEWNAME);
                         String newName = operationIntent.getStringExtra(EXTRA_NEWNAME);
-                        operation = new RenameFileOperation(remotePath, newName, fileDataStorageManager, getApplicationContext());
+                        operation = new RenameFileOperation(remotePath, newName, fileDataStorageManager);
                         break;
                         break;
 
 
                     case ACTION_REMOVE:
                     case ACTION_REMOVE:

+ 5 - 3
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -39,6 +39,7 @@ import android.os.Bundle;
 import android.os.Handler;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.text.TextUtils;
+import android.widget.Toast;
 
 
 import com.google.android.material.snackbar.Snackbar;
 import com.google.android.material.snackbar.Snackbar;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.account.UserAccountManager;
@@ -91,6 +92,7 @@ import com.owncloud.android.utils.ClipboardUtil;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ErrorMessageAdapter;
 import com.owncloud.android.utils.ErrorMessageAdapter;
 import com.owncloud.android.utils.FilesSyncHelper;
 import com.owncloud.android.utils.FilesSyncHelper;
+import com.owncloud.android.utils.PermissionUtil;
 import com.owncloud.android.utils.theme.ThemeSnackbarUtils;
 import com.owncloud.android.utils.theme.ThemeSnackbarUtils;
 import com.owncloud.android.utils.theme.ThemeToolbarUtils;
 import com.owncloud.android.utils.theme.ThemeToolbarUtils;
 
 
@@ -919,9 +921,9 @@ public abstract class FileActivity extends DrawerActivity
      */
      */
     @Override
     @Override
     public void onShareProcessClosed() {
     public void onShareProcessClosed() {
-        Fragment fragment  = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
-        if (fragment!=null){
-            ((FileDetailFragment)fragment).showHideFragmentView(false);
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+        if (fragment != null) {
+            ((FileDetailFragment) fragment).showHideFragmentView(false);
         }
         }
     }
     }
 }
 }

+ 1 - 1
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -48,9 +48,9 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManager;
+import android.widget.Toast;
 
 
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.AppBarLayout;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import com.google.android.material.snackbar.Snackbar;
 import com.google.android.material.snackbar.Snackbar;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.appinfo.AppInfo;
 import com.nextcloud.client.appinfo.AppInfo;

+ 1 - 1
src/main/java/com/owncloud/android/ui/activity/RichDocumentsEditorWebView.java

@@ -225,7 +225,7 @@ public class RichDocumentsEditorWebView extends EditorWebView {
             return;
             return;
         }
         }
 
 
-        File targetFile = new File(FileStorageUtils.getInternalTemporalPath(account.getName(), getBaseContext()) + "/print.pdf");
+        File targetFile = new File(FileStorageUtils.getTemporalPath(account.getName()) + "/print.pdf");
 
 
         new PrintAsyncTask(targetFile, url.toString(), new WeakReference<>(this)).execute();
         new PrintAsyncTask(targetFile, url.toString(), new WeakReference<>(this)).execute();
     }
     }

+ 1 - 1
src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java

@@ -162,7 +162,7 @@ public class CopyAndUploadContentUrisTask extends AsyncTask<Object, Void, Result
                     }
                     }
                 }
                 }
 
 
-                fullTempPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mAppContext) + currentRemotePath;
+                fullTempPath = FileStorageUtils.getTemporalPath(user.getAccountName()) + currentRemotePath;
                 inputStream = leakedContentResolver.openInputStream(currentUri);
                 inputStream = leakedContentResolver.openInputStream(currentUri);
                 File cacheFile = new File(fullTempPath);
                 File cacheFile = new File(fullTempPath);
                 File tempDir = cacheFile.getParentFile();
                 File tempDir = cacheFile.getParentFile();

+ 1 - 2
src/main/java/com/owncloud/android/utils/FileStorageUtils.java

@@ -96,10 +96,9 @@ public final class FileStorageUtils {
 
 
     /**
     /**
      * Get absolute path to tmp folder inside datafolder in sd-card for given accountName.
      * Get absolute path to tmp folder inside datafolder in sd-card for given accountName.
-     * @deprecated use {@link #getInternalTemporalPath(String, Context)}, as this one is broken on sdk >= 30
      */
      */
-    @Deprecated
     public static String getTemporalPath(String accountName) {
     public static String getTemporalPath(String accountName) {
+        // FIXME broken in SDK 30
         return MainApp.getStoragePath()
         return MainApp.getStoragePath()
                 + File.separator
                 + File.separator
                 + MainApp.getDataFolder()
                 + MainApp.getDataFolder()

+ 40 - 9
src/main/java/com/owncloud/android/utils/PermissionUtil.kt

@@ -24,10 +24,17 @@ package com.owncloud.android.utils
 import android.Manifest
 import android.Manifest
 import android.app.Activity
 import android.app.Activity
 import android.content.Context
 import android.content.Context
+import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager
+import android.net.Uri
 import android.os.Build
 import android.os.Build
+import android.os.Environment
+import android.provider.Settings
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AlertDialog
 import androidx.core.app.ActivityCompat
 import androidx.core.app.ActivityCompat
 import androidx.core.content.ContextCompat
 import androidx.core.content.ContextCompat
+import com.owncloud.android.R
 
 
 /**
 /**
  * Created by scherzia on 29.12.2015.
  * Created by scherzia on 29.12.2015.
@@ -40,6 +47,8 @@ object PermissionUtil {
     const val PERMISSIONS_READ_CALENDAR_AUTOMATIC = 6
     const val PERMISSIONS_READ_CALENDAR_AUTOMATIC = 6
     const val PERMISSIONS_WRITE_CALENDAR = 7
     const val PERMISSIONS_WRITE_CALENDAR = 7
 
 
+    const val REQUEST_CODE_MANAGE_ALL_FILES = 19203
+
     /**
     /**
      * Wrapper method for ContextCompat.checkSelfPermission().
      * Wrapper method for ContextCompat.checkSelfPermission().
      * Determine whether *the app* has been granted a particular permission.
      * Determine whether *the app* has been granted a particular permission.
@@ -75,8 +84,8 @@ object PermissionUtil {
      */
      */
     @JvmStatic
     @JvmStatic
     fun getExternalStoragePermission(): String = when {
     fun getExternalStoragePermission(): String = when {
-        Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> Manifest.permission.WRITE_EXTERNAL_STORAGE
-        else -> Manifest.permission.READ_EXTERNAL_STORAGE
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Manifest.permission.MANAGE_EXTERNAL_STORAGE
+        else -> Manifest.permission.WRITE_EXTERNAL_STORAGE
     }
     }
 
 
     /**
     /**
@@ -85,8 +94,10 @@ object PermissionUtil {
      * @return `true` if app has the permission, or `false` if not.
      * @return `true` if app has the permission, or `false` if not.
      */
      */
     @JvmStatic
     @JvmStatic
-    fun checkExternalStoragePermission(context: Context): Boolean =
-        ContextCompat.checkSelfPermission(context, getExternalStoragePermission()) == PackageManager.PERMISSION_GRANTED
+    fun checkExternalStoragePermission(context: Context): Boolean = when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> Environment.isExternalStorageManager()
+        else -> checkSelfPermission(context, getExternalStoragePermission())
+    }
 
 
     /**
     /**
      * Request relevant external storage permission depending on SDK.
      * Request relevant external storage permission depending on SDK.
@@ -94,11 +105,31 @@ object PermissionUtil {
      * @param activity The target activity.
      * @param activity The target activity.
      */
      */
     @JvmStatic
     @JvmStatic
-    fun requestExternalStoragePermission(activity: Activity) {
-        ActivityCompat.requestPermissions(
-            activity, arrayOf(getExternalStoragePermission()),
-            PERMISSIONS_EXTERNAL_STORAGE
-        )
+    fun requestExternalStoragePermission(activity: Activity) = when {
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> requestManageFilesPermission(activity)
+        else -> {
+            ActivityCompat.requestPermissions(
+                activity, arrayOf(getExternalStoragePermission()),
+                PERMISSIONS_EXTERNAL_STORAGE
+            )
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.R)
+    private fun requestManageFilesPermission(activity: Activity) {
+        AlertDialog.Builder(activity, R.style.Theme_ownCloud_Dialog)
+            .setTitle(R.string.file_management_permission)
+            .setMessage(R.string.file_management_permission_text)
+            .setCancelable(false)
+            .setPositiveButton(R.string.common_ok) { dialog, _ ->
+                val intent = Intent().apply {
+                    action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
+                    data = Uri.parse("package:${activity.applicationContext.packageName}")
+                }
+                activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_ALL_FILES)
+                dialog.dismiss()
+            }
+            .show()
     }
     }
 
 
     /**
     /**

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

@@ -1005,4 +1005,6 @@
     <string name="whats_new_storage_sdk30_title">Changes in storage access from Android 11</string>
     <string name="whats_new_storage_sdk30_title">Changes in storage access from Android 11</string>
     <string name="whats_new_storage_sdk30_content">From Android 11 onwards, strict storage access control is enforced.\n\nAs a consequence, Nextcloud will no longer be able to write or delete files on external storage, or read files in private folders owned by other apps.</string>
     <string name="whats_new_storage_sdk30_content">From Android 11 onwards, strict storage access control is enforced.\n\nAs a consequence, Nextcloud will no longer be able to write or delete files on external storage, or read files in private folders owned by other apps.</string>
     <string name="whats_new_storage_sdk30_content_page2">Please check your auto upload items and verify that Nextcloud still has read access to the local folder.\n\nAdditionally, other applications will not be able to read Nextcloud\'s downloaded files directly from the external storage.</string>
     <string name="whats_new_storage_sdk30_content_page2">Please check your auto upload items and verify that Nextcloud still has read access to the local folder.\n\nAdditionally, other applications will not be able to read Nextcloud\'s downloaded files directly from the external storage.</string>
+    <string name="file_management_permission">Permissions needed</string>
+    <string name="file_management_permission_text">Nextcloud needs file management permissions to work properly. Please enable it in the following screen to continue.</string>
 </resources>
 </resources>