0

In devices below android 10 we can access any file like this:: File f = new File("storage/emulated/0/filename.txt");

But I want to do to same on android 10+ devices which are using something like scoped storage or mediastore class that I didn't understood in android studio I don't know exactly how to do it I want to access files any directories not only public directories like "Pictures" please help me

File f = new File("storage/emulated/0/file.jpg");

Vinay K
  • 23
  • 7

3 Answers3

2

A modification of Yatik's solution separates out the specification of the required folder

private fun requestDocumentPermission() {
    val externalStorageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
    val folder = File(externalStorageDir, "RaceManagement/Entrants")
    val storageManager = application.getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val intent =  storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()
    val targetDirectory = "Android%2F$folder" // user will be prompted for permission to access this directory
    val uri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
        @Suppress("DEPRECATION") intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") as Uri
    else
        intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI", Uri::class.java) as Uri
    val uriString = uri.toString().replace("/root/", "/document/") + "%3A$targetDirectory"
    intent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(uriString))
    val activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.data?.let { directoryUri ->
                // make the permission persistent using takePersistableUriPermission()
                val modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                contentResolver.takePersistableUriPermission(directoryUri, modeFlags)
                // save the persistent permission
                val editor = getSharedPreferences(PREFERENCES_FILENAME, MODE_PRIVATE).edit()
                editor.putString(KEY_NAME, directoryUri.toString())
                editor.apply()
                listFiles(directoryUri)
            }
        }
    }
    activityResultLauncher.launch(intent)
}

Note: No additional permissions are required in the manifest. There is also no requirement for provider or queries sections

While not the solution I eventually used some findings on my experience with MANAGE_EXTERNAL_STORAGE

The permission was always denied even after requesting the required permission using requestPermissions(). Strangely the option to grant the permission was never presented (?)

To fix the issue involved manually granting the permission.

Navigate to the specific App permissions -> Files and Media Access -> three options, with "Allow access to media only" set.

Change this to "Allow management of all files" and app can list all files in the folder (even though checkSelfPermission continued to report access as denied?).

As noted Google restrictions mean the use of MANAGE_EXTERNAL_STORAGE is probably unsuitable for Apps intended to be listed on the Play store.

RatherBeSailing
  • 261
  • 1
  • 11
  • Although the project is not well managed but I hope [this](https://github.com/yatiksihag01/Status-Saver/blob/master/app/src/main/java/com/yatik/statussaver/ui/SplashActivity.java) will help you. Please tell me if this don't work for you. – Yatik Jul 16 '23 at 03:21
  • @Yatik thank you for the prompt response. I suspected my issue was an incorrect uriMain. I eventually settled on expanded code to specify the folder I required. – RatherBeSailing Jul 17 '23 at 06:48
1

To get all the files of folder xyz (storage/emulated/0/xyz) below android 10:

File xyzFolder = new File(Environment.getExternalStorageDirectory() + File.separator + "xyz");

File[] allFiles = xyzFolder.listFiles();

But for android 10 and above either use declare MANAGE_EXTERNAL_STORAGE (not recommended if it's not some kind of file manager app) or you can use below method:

1). First take folder permission

uriMain = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia/document/primary%3Axyz");

private final int REQUEST_CODE = 100;

List<Object> filesList = new ArrayList<>();

private void aboveQFolderPermission() {

    try {
        Intent createOpenDocumentTreeIntent = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            createOpenDocumentTreeIntent = ((StorageManager) getSystemService(STORAGE_SERVICE)).getPrimaryStorageVolume().createOpenDocumentTreeIntent();
        }

        assert createOpenDocumentTreeIntent != null;
        String replace = createOpenDocumentTreeIntent.getParcelableExtra("android.provider.extra.INITIAL_URI").toString().replace("/root/", "/document/");
        createOpenDocumentTreeIntent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(replace + "%3A" + "xyz"));
        startActivityForResult(createOpenDocumentTreeIntent, REQUEST_CODE);
    } catch (Exception e) {
        e.printStackTrace()
    }

}

Now in onActivityResult(),

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE) {
        if (data != null) {
            getContentResolver().takePersistableUriPermission(data.getData(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

            //save shared preference to check whether app specific folder permission granted or not
            sharedPreferences = getSharedPreferences("tree", MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();

            editor.putString("treeUriString", String.valueOf(uriMain));
            editor.apply();

            new Thread(() -> {
                getListAboveQ(uriMain);
                handler. Post(() -> /*Some code here as per your need*/);
            }).start();

        }
    }
}

2). Now get the List of files:

private void getListAboveQ(uriMain) {


    ContentResolver contentResolver = getContext().getContentResolver();
    Uri buildChildDocumentsUriUsingTree = DocumentsContract.buildChildDocumentsUriUsingTree(uriMain, DocumentsContract.getDocumentId(uriMain));

    try (Cursor cursor = contentResolver.query(buildChildDocumentsUriUsingTree, new String[]{"document_id"}, null, null, null)) {
        while (cursor.moveToNext()) {
               filesList.add(DocumentsContract.buildDocumentUriUsingTree(uriMain, cursor.getString(0)));
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

}

Edit:- You can use saved preferences like:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

        sharedPreferences = getSharedPreferences("tree", Context.MODE_PRIVATE);
        String uriString = sharedPreferences.getString("treeUriString", "");

        if (uriString.matches("")) {
            //ask for folder permission
            aboveQFolderPermission()
        } else {
            //Permission is already granted
            //TODO: Whatever you want
        }
}
Yatik
  • 66
  • 6
  • Do we need to take that specific folder permission everytime we open it or just for the first time – Vinay K Jan 05 '23 at 05:13
  • Look at in `onActivityResult(...)` method we're using `.takePersistableUriPermission(..)`. This means that the app will have long-term access to the data at the URI, even after the app is no longer running. – Yatik Jan 07 '23 at 04:28
0

You'll need to request MANAGE_EXTERNAL_STORAGE permission. With that permission, you can access shared storage files with the File API.

Reference:

https://developer.android.com/training/data-storage/manage-all-files#all-files-access

Chunhui Deng
  • 111
  • 1
  • 4
  • Bear in mind that some app distribution channels, such as the Play Store, restrict what apps can request this permission. – CommonsWare Nov 06 '22 at 12:03