7

In my app I gain sd card write permission using the following intent. If the user selects the sd card folder from the system file explorer then I have sd card write access.

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, 42);

After that I am able to modify files in th sd card using the DocumentFile class. But I'm having problem getting a DocumentFile for a random file path.

Document.fromFile(new File(path));
Document.fromSingleUri(Uri.fromFile(new File(path)));

both return a DocumentFile object that returns false on .canWrite(). Even though I allready have sd card permission.

So I wrote the method posted in the end of my question to get a DocumentFile that returns true on .canWrite(). But this is slow...And also feels very wrong! There has to be a better way to do this. I also wrote a method that returns the same string as

String docFileUriString = docFile.getUri().toString(); 

for any file, where docFile is the DocumentFile that is returned by the method below. But

DocumentFile.fromTreeUri(Uri.parse(docFileUriString ));

returns a DocumentFile that points to the root of the sd card instead of the DocumentFile path. Which is just weird. Can someone suggest a more elegant solution?

public static DocumentFile getDocumentFileIfAllowedToWrite(File file, Context con){ 

    List<UriPermission> permissionUris = con.getContentResolver().getPersistedUriPermissions();

    for(UriPermission permissionUri:permissionUris){

        Uri treeUri = permissionUri.getUri();
        DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, treeUri);
        String rootDocFilePath = FileUtil.getFullPathFromTreeUri(treeUri, con);

        if(file.getAbsolutePath().startsWith(rootDocFilePath)){

            ArrayList<String> pathInRootDocParts = new ArrayList<String>();
            while(!rootDocFilePath.equals(file.getAbsolutePath())){
                pathInRootDocParts.add(file.getName());
                file = file.getParentFile();
            } 

            DocumentFile docFile = null;  

            if(pathInRootDocParts.size()==0){ 
                docFile = DocumentFile.fromTreeUri(con, rootDocFile.getUri()); 
            }
            else{
                for(int i=pathInRootDocParts.size()-1;i>=0;i--){
                    if(docFile==null){docFile = rootDocFile.findFile(pathInRootDocParts.get(i));}
                    else{docFile = docFile.findFile(pathInRootDocParts.get(i)); }  
                }
            }
            if(docFile!=null && docFile.canWrite()){ 
                return docFile; 
            }else{
                return null;
            }

        }
    }
    return null; 
}
4

2 回答 2

2

Document.fromFile(File)在最近的设备上似乎无法为此目的正常工作

这是答案的链接,我获得了有关该问题的最多信息 https://stackoverflow.com/a/26765884/971355

关键步骤是:

  1. 获得对 SD 卡的写入权限,并使其在手机重启时保持不变

    公共无效onActivityResult(int requestCode,int resultCode,Intent resultData){

    ...

    getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

    }

  2. 之后,无论文件和文件夹嵌套多深,您都可以访问它们,但您必须注意必须处理的复杂 URL。

如果你是像我一样的 linux 人,并且曾经认为一切都是文件,一切都很简单。这次不是。SD卡上的文件,说明您的SD卡根文件夹/storage/13E5-2604/testDir/test.txt 在哪里:13E5-2604

content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt

新的现实中。为什么他们做的如此复杂 - 是另一个问题......

我不知道一种 API 方法可以透明且轻松地将普通的 linux 路径转换为这个新的现实路径。但是在你完成之后(%3Ais : and %2Fis / See https://www.w3schools.com/tags/ref_urlencode.ASP供参考)你可以很容易地创建一个实例DocumentFile

DocumentFile txtFile = DocumentFile.fromSingleUri(context,
Uri.parse("content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt"));

并写信给它。

于 2021-01-25T20:39:45.500 回答
1

为了回复@AndiMar(3 月 21 日),我只是检查它是否存在于内部存储中,如果它不需要担心。

       try {
        if (sourcefile.toString().contains(Environment.getExternalStorageDirectory().toString())) {
            if (sourcefile.exists()) {
                sourcefile.delete();
            }
        } else {
            FileUtilities fio = new FileUtilities();
            DocumentFile filetodelete = fio.getDocumentFileIfAllowedToWrite(sourcefile, context);
            if (filetodelete.exists()) {
                filetodelete.delete();
            }
        }

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

    }
于 2018-03-25T11:05:35.740 回答