来自树URI的Android 5.0 DocumentFile

Joe*_*ton 11 storage android android-sdcard documentfile

好吧,我进行了搜索和搜索,没有人得到我的确切答案,或者我错过了.我让我的用户通过以下方式选择目录:

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, READ_REQUEST_CODE);
Run Code Online (Sandbox Code Playgroud)

在我的活动中,我想捕捉实际路径,这似乎是不可能的.

protected void onActivityResult(int requestCode, int resultCode, Intent intent){
    super.onActivityResult(requestCode, resultCode, intent);
    if (resultCode == RESULT_OK) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
            //Marshmallow 

        } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
            //Set directory as default in preferences
            Uri treeUri = intent.getData();
            //grant write permissions
            getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            //File myFile = new File(uri.getPath()); 
            DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
Run Code Online (Sandbox Code Playgroud)

我选择的文件夹位于:

Device storage/test/
Run Code Online (Sandbox Code Playgroud)

我已经尝试了以下所有方法来获得确切的路径名称,但无济于事.

File myFile = new File (uri.getPath());
//returns: /tree/1AF6-3708:test

treeUri.getPath();
//returns: /tree/1AF6-3708:test/

pickedDir.getName()
//returns: test

pickedDir.getParentFile()
//returns: null
Run Code Online (Sandbox Code Playgroud)

基本上,我需要把/tree/1AF6-3708:/storage/emulated/0/或任何每个设备调用它的存储位置.所有其他可用选项/tree/1AF6-37u08:也返回.

我想这样做有两个原因.

1)在我的应用程序中,我将文件位置存储为共享首选项,因为它是特定于用户的.我有相当多的数据将被下载和存储,我希望用户能够将它放在他们想要的地方,特别是如果他们有一个额外的存储位置.我设置了默认值,但我想要多功能性,而不是专用位置:

Device storage/Android/data/com.app.name/
Run Code Online (Sandbox Code Playgroud)

2)在5.0中我想让用户获得该文件夹的读/写权限,这似乎是唯一的方法.如果我可以从一个可以解决此问题的字符串获得读/写权限.

我能找到的所有解决方案都与Mediastore有关,这对我没有帮助.我必须在某处丢失某些东西,否则我必须把它弄透.任何帮助,将不胜感激.谢谢.

Ano*_*ous 22

这将为您提供所选文件夹的实际路径(假设所选文件夹位于外部存储或外部SD卡中)

Uri treeUri = data.getData();
String path = FileUtil.getFullPathFromTreeUri(treeUri,this); 
Run Code Online (Sandbox Code Playgroud)

其中FileUtil是以下类

public final class FileUtil {
    static String TAG="TAG";
    private static final String PRIMARY_VOLUME_NAME = "primary";

    @Nullable
    public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
        if (treeUri == null) return null;
        String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con);
        if (volumePath == null) return File.separator;
        if (volumePath.endsWith(File.separator))
            volumePath = volumePath.substring(0, volumePath.length() - 1);

        String documentPath = getDocumentPathFromTreeUri(treeUri);
        if (documentPath.endsWith(File.separator))
            documentPath = documentPath.substring(0, documentPath.length() - 1);

        if (documentPath.length() > 0) {
            if (documentPath.startsWith(File.separator))
                return volumePath + documentPath;
            else
                return volumePath + File.separator + documentPath;
        }
        else return volumePath;
    }


    @SuppressLint("ObsoleteSdkInt")
    private static String getVolumePath(final String volumeId, Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;
        try {
            StorageManager mStorageManager =
                    (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
            Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
            Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
            Method getUuid = storageVolumeClazz.getMethod("getUuid");
            Method getPath = storageVolumeClazz.getMethod("getPath");
            Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
            Object result = getVolumeList.invoke(mStorageManager);

            final int length = Array.getLength(result);
            for (int i = 0; i < length; i++) {
                Object storageVolumeElement = Array.get(result, i);
                String uuid = (String) getUuid.invoke(storageVolumeElement);
                Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);

                // primary volume?
                if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
                    return (String) getPath.invoke(storageVolumeElement);

                // other volumes?
                if (uuid != null && uuid.equals(volumeId))
                    return (String) getPath.invoke(storageVolumeElement);
            }
            // not found.
            return null;
        } catch (Exception ex) {
            return null;
        }
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static String getVolumeIdFromTreeUri(final Uri treeUri) {
        final String docId = DocumentsContract.getTreeDocumentId(treeUri);
        final String[] split = docId.split(":");
        if (split.length > 0) return split[0];
        else return null;
    }


    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static String getDocumentPathFromTreeUri(final Uri treeUri) {
        final String docId = DocumentsContract.getTreeDocumentId(treeUri);
        final String[] split = docId.split(":");
        if ((split.length >= 2) && (split[1] != null)) return split[1];
        else return File.separator;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是有效的答案.Google工程师过度设计了File类,因此我们必须使用这样的hacks.注意,API24添加了StorageVolume类,这消除了使用反射代码的需要.需要注意的是,上面的代码假设从DocumentsContract.getTreeDocumentId返回的一些特定格式的ID没有记录,它的工作原理与API 25相同,但不保证将来也一样. (6认同)

Com*_*are 3

在我的活动中,我想捕获实际的路径,这似乎是不可能的。

这是因为可能没有一条实际的路径,更不用说您可以访问的路径了。有许多可能的文档提供程序,其中很少有将所有文档保存在设备本地的情况,也很少有将文件保存在外部存储上的情况,您可以在其中使用它们。

我有相当多的数据需要下载和存储,我希望用户能够将其放置在他们想要的地方

然后使用存储访问框架 API,而不是认为从存储访问框架获取的文档/树始终是本地的。或者,不要使用ACTION_OPEN_DOCUMENT_TREE.

在 5.0 中,我想让用户获得该文件夹的读/写权限

这是由存储提供商处理的,作为用户与该存储提供商交互的一部分。你没有参与其中。