来自树URI的Android 5.0 DocumentFile [英] Android 5.0 DocumentFile from tree URI
问题描述
好的,我已经搜索了很多东西,没有人知道我的确切答案,或者我错过了.我让我的用户通过以下方式选择目录:
Alright, I've searched and searched and no one has my exact answer, or I missed it. I'm having my users select a directory by:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, READ_REQUEST_CODE);
在我的活动中,我想捕获实际路径,这似乎是不可能的.
In my activity I want to capture the actual path, which seems to be impossible.
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);
我选择的文件夹位于:
Device storage/test/
我尝试了以下所有方法来获取确切的路径名,但无济于事.
I've tried all of the following ways to get an exact path name, but to no avail.
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
基本上,我需要将/tree/1AF6-3708:
转换为/storage/emulated/0/
或每个设备称为其存储位置的位置.所有其他可用选项也会返回/tree/1AF6-37u08:
.
Basically I need to turn /tree/1AF6-3708:
into /storage/emulated/0/
or whatever each device calls it's storage location. All other available options return /tree/1AF6-37u08:
also.
我要这样做的原因有两个.
There are 2 reasons I want to do it this way.
1)在我的应用程序中,我将文件位置存储为共享首选项,因为它是用户特定的.我有很多数据将被下载和存储,我希望用户能够将其放置在所需的位置,尤其是在他们有其他存储位置的情况下.我确实设置了默认值,但我想使用多功能性,而不是专用于以下位置:
1) In my app I store the file location as a shared preference because it is user specific. I have quite a bit of data that will be downloaded and stored and I want the user to be able to place it where they want, especially if they have an additional storage location. I do set a default, but I want versatility, rather than the dedicated location of:
Device storage/Android/data/com.app.name/
2)在5.0中,我想使用户能够获得对该文件夹的读/写权限,这似乎是唯一的方法.如果我可以从解决此问题的字符串中获得读/写权限.
2) In 5.0 I want to enable the user to get read/write permissions to that folder and this seems the only way to do that. If I can get read/write permissions from a string that would fix this issue.
我能够找到的所有解决方案都与Mediastore有关,但这并不能完全帮助我.我必须在某处缺少某些东西,或者一定要给它上釉.任何帮助,将不胜感激.谢谢.
All solutions I've been able to find relate to Mediastore, which doesn't help me exactly. I have to be missing something somewhere or I must have glazed over it. Any help would be appreciated. Thanks.
推荐答案
这将为您提供所选文件夹的实际路径.它仅对属于本地存储的文件/文件夹有效./p>
This will give you the actual path of the selected folder It will work ONLY for files/folders that belong in local storage.
Uri treeUri = data.getData();
String path = FileUtil.getFullPathFromTreeUri(treeUri,this);
其中FileUtil是以下类
where FileUtil is the following class
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;
}
}
更新:
要解决注释中提到的下载"问题:如果从默认Android文件选择器的左侧抽屉中选择Downloads
,则实际上不是在选择目录.下载是提供者.普通的文件夹树uri看起来像这样:
To address the Downloads issue mentioned in the comments: If you select Downloads
from the left drawer in the default Android file picker you are not actually selecting a directory. Downloads is a provider. A normal folder tree uri looks something like this:
content://com.android.externalstorage.documents/tree/primary%3ADCIM
下载的树uri是
content://com.android.providers.downloads.documents/tree/downloads
您可以看到一个显示externalstorage
,另一个显示providers
.这就是为什么它不能与文件系统中的目录匹配的原因.因为它不是目录.
You can see that the one says externalstorage
while the other one says providers
. That is why it cannot be matched to a directory in the file system. Because it is not a directory.
解决方案:您可以添加一个相等性检查,如果树uri与该相等,则返回默认的下载文件夹路径,可以像这样检索该路径:
SOLUTION: You can add an equality check and if the tree uri is equal to that then return the default download folder path which can be retrieved like this:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
如果愿意,可以对所有提供者执行类似的操作.我认为它可以正常工作.但是我认为在某些情况下是不会的.
And do something similar for all the providers if you wish to. And it would work correctly most of the time
I assume. But I imagine that there are edge cases where it wouldn't.
这篇关于来自树URI的Android 5.0 DocumentFile的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!