Skip to content Skip to sidebar Skip to footer

Android 11 Action_open_document_tree: Set Initial Uri To The Documents Folder

using the Scoped Storage model in Android 11 I want to give the user the ability to choose a folder, starting in the documents folder: val intent = Intent(Intent.ACTION_O

Solution 1:

We will manupilate INITIAL_URI obtained from StorageManager..getPrimaryStorageVolume().createOpenDocumentTreeIntent().

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q)
        {
            StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);

            Intent intent = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
            //String startDir = "Android";
            //String startDir = "Download"; // Not choosable on an Android 11 device
            //String startDir = "DCIM";
            //String startDir = "DCIM/Camera";  // replace "/", "%2F"
            //String startDir = "DCIM%2FCamera";
            String startDir = "Documents";

            Uri uri = intent.getParcelableExtra("android.provider.extra.INITIAL_URI");

            String scheme = uri.toString();

            Log.d(TAG, "INITIAL_URI scheme: " + scheme);

            scheme = scheme.replace("/root/", "/document/");

            scheme += "%3A" + startDir;

            uri = Uri.parse(scheme);

            intent.putExtra("android.provider.extra.INITIAL_URI", uri);

            Log.d(TAG, "uri: " + uri.toString());

            ((Activity) context).startActivityForResult(intent, REQUEST_ACTION_OPEN_DOCUMENT_TREE);

            return;
        }

Solution 2:

how can I generate a proper URI of the phone's documents folder?

Tested on :

  1. Xiaomi M2102J20SI
  2. Emulator Pixel 4 XL API 30

Function askPermission() opens the target directory.

@RequiresApi(Build.VERSION_CODES.Q)privatefunaskPermission() {
    val storageManager = application.getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val intent =  storageManager.primaryStorageVolume.createOpenDocumentTreeIntent()

    val targetDirectory = "WhatsApp%2FMedia%2F.Statuses"// add your directory to be selected by the uservar uri = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI") as Uri
    var scheme = uri.toString()
    scheme = scheme.replace("/root/", "/document/")
    scheme += "%3A$targetDirectory"
    uri = Uri.parse(scheme)
    intent.putExtra("android.provider.extra.INITIAL_URI", uri)
    startActivityForResult(intent, REQUEST_CODE)
}

Uri of the file will be returned in onActivityResult()

overridefunonActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
            if (data != null) {
                data.data?.let { treeUri ->

                    // treeUri is the Uri of the file// if life long access is required the takePersistableUriPermission() is used

                    contentResolver.takePersistableUriPermission(
                            treeUri,
                            Intent.FLAG_GRANT_READ_URI_PERMISSION or
                                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    )

                  readSDK30(treeUri)
                }
            }
        }
    }

Function readSDK30() is used to read files & folders from Uri

privatefunreadSDK30(treeUri: Uri) {
        val tree = DocumentFile.fromTreeUri(this, treeUri)!!

        thread {
            val uriList  = arrayListOf<Uri>()
            listFiles(tree).forEach { uri ->
                 
                // Collect all the Uri from here
            }
            
        }
    }

Function listFiles() returns all the files & folders in the given Uri

funlistFiles(folder: DocumentFile): List<Uri> {
            returnif (folder.isDirectory) {
                folder.listFiles().mapNotNull { file ->
                    if (file.name != null) file.uri elsenull
                }
            } else {
                emptyList()
            }
        }

Solution 3:

All the credit goes to blackapps answer! https://stackoverflow.com/a/67554693/2036264

Here is the same code in Kotlin language:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            val sm =  getSystemService(Context.STORAGE_SERVICE) as StorageManager
            intent = sm.primaryStorageVolume.createOpenDocumentTreeIntent()
            //String startDir = "Android";//String startDir = "Download"; // Not choosable on an Android 11 device//String startDir = "DCIM";//String startDir = "DCIM/Camera";  // replace "/", "%2F"//String startDir = "DCIM%2FCamera";val startDir = "Documents"var uriroot = intent.getParcelableExtra<Uri>("android.provider.extra.INITIAL_URI")    // get system root urivar scheme = uriroot.toString()
            Log.d("Debug", "INITIAL_URI scheme: $scheme")
            scheme = scheme.replace("/root/", "/document/")
            scheme += "%3A$startDir"//change uri to Documents folder
            uriroot = Uri.parse(scheme)
            intent.putExtra("android.provider.extra.INITIAL_URI", uriroot)                        // give changed uri to Intent
            Log.d("Debug", "uri: $uriroot")
          
            startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
          }

As some commentors mentioned, this code might break and not work in the future, which is true. However, considering Android's past, they will change the storage API anyways every other year.

Post a Comment for "Android 11 Action_open_document_tree: Set Initial Uri To The Documents Folder"