Search code examples
androidandroid-permissionsandroid-sdcardstorage-access-framework

Can't access Sd Card after providing permission with Storage Access Framework


I'm trying to provide sd card access inside my app with Storage Access Framework.

This is how I call an intent to let the user choose the sd card directory.

private void openDocumentTree() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(
            Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
    startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);
}

And this is how I manage the intent result to set permissions:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case REQUEST_CODE_OPEN_DOCUMENT_TREE:
                if (resultCode == Activity.RESULT_OK) {
                    Uri treeUri = data.getData();
                    int takeFlags = data.getFlags();
                    takeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION |
                                  Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        this.getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
                    }
               }
        }

But still can't save any file on sd card.

Value of treeUri on my device is:

content://com.android.externalstorage.documents/tree/6921-B9FD%3A

What I missed here that system still couldn't let the user have access to sd card(saving a simple file on sd card)?


Solution

  • Ok. This is how I copy image on sd card with provided permissions which invokes file picker to choose sd card directory by the user (which may user choose a wrong directory so prepare your code for it).

    private void newcopyFile(File fileInput, String outputParentPath,
                            String mimeType, String newFileName) {
    
        DocumentFile documentFileGoal = DocumentFile.fromTreeUri(this, treeUri);
    
        String[] parts = outputParentPath.split("\\/");
        for (int i = 3; i < parts.length; i++) { //ex: parts:{"", "storage", "extSdCard", "MyFolder", "MyFolder", "MyFolder"}
            if (documentFileGoal != null) {
                documentFileGoal = documentFileGoal.findFile(parts[i]);
            }
        }
        if (documentFileGoal == null) {
            Toast.makeText(MainActivity.this, "Directory not found", Toast.LENGTH_SHORT).show();
            return;
        }
    
        DocumentFile documentFileNewFile = documentFileGoal.createFile(mimeType, newFileName);
    
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            outputStream = getContentResolver().openOutputStream(documentFileNewFile.getUri());
            inputStream = new FileInputStream(fileInput);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            if (outputStream != null) {
                byte[] buffer = new byte[1024];
                int read;
                if (inputStream != null) {
                    while ((read = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, read);
                    }
                }
                if (inputStream != null) {
                    inputStream.close();
                }
                inputStream = null;
                outputStream.flush();
                outputStream.close();
                outputStream = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    NOTE: Read all the comments under the question.

    And be aware of setting a ContentObserver to your ContentResolver If you want to get notified when your newly created image is registered inside ContentResolver and hence it's time to query it(if you wish to).