2016-01-21 158 views
4

好吧,我搜索过,没有人有我的确切答案,或者我错过了。我有我的用户通过选择一个目录:Android 5.0 DocumentFile树URI

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 
startActivityForResult(intent, READ_REQUEST_CODE); 

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

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/ 

我已经尝试了所有的以下方式来得到一个确切的路径名,但无济于事。

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:

有2个理由我想这样做。

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

Device storage/Android/data/com.app.name/ 

2)在5.0我想使用户能够获得读/写权限到该文件夹​​,这似乎是唯一的出路要做到这一点。如果我可以从字符串获得读/写权限来解决这个问题。

我一直能找到的所有解决方案都与Mediastore有关,这对我无能为力。我必须在某处丢失某些东西,否则我必须瞥见它。任何帮助,将不胜感激。谢谢。

+0

您是否得到了它的工作? –

回答

3

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

那是因为可能没有实际路径,更不用说可以访问的路径了。有许多可能的文档提供者,其中很少会在设备上本地存放所有文档,并且很少有这些文档将在外部存储上有文件,您可以在这些文档中使用它们。

我有相当多的将被下载并存储数据的,我希望用户能够将其放置在他们希望

然后使用Storage Access Framework APIs,而不是思考的文档/从存储访问框架获得的树永远是本地的。或者,请勿使用ACTION_OPEN_DOCUMENT_TREE

在5.0我想使用户能够获得读/写权限到该文件夹​​

这是由存储提供商处理,因为用户是如何与存储提供商交互的一部分。你没有参与。

12

这会给你所选择的文件夹的实际路径(假定所选择的文件夹位于外部存储器或外部SD卡)

Uri treeUri = data.getData(); 
String path = FileUtil.getFullPathFromTreeUri(treeUri,this); 

其中FileUtil是下面的类

import java.io.File; 
import java.io.IOException; 
import java.lang.reflect.Array; 
import java.lang.reflect.Method; 
import java.util.ArrayList; 

import android.annotation.SuppressLint; 
import android.annotation.TargetApi; 
import android.content.Context; 
import android.net.Uri; 
import android.os.Build; 
import android.os.Environment; 
import android.os.storage.StorageManager; 
import android.provider.DocumentsContract; 
import android.support.annotation.NonNull; 
import android.support.annotation.Nullable; 
import android.support.v4.content.ContextCompat; 
import android.util.Log; 

@SuppressLint("NewApi") 
public final class FileUtil { 

    static String TAG="TAG"; 
    private static final String PRIMARY_VOLUME_NAME = "primary"; 



    public static boolean isKitkat() { 
     return Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT; 
    } 
    public static boolean isAndroid5() { 
     return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 
    } 


    @NonNull 
    public static String getSdCardPath() { 
     String sdCardDirectory = Environment.getExternalStorageDirectory().getAbsolutePath(); 

     try { 
      sdCardDirectory = new File(sdCardDirectory).getCanonicalPath(); 
     } 
     catch (IOException ioe) { 
      Log.e(TAG, "Could not get SD directory", ioe); 
     } 
     return sdCardDirectory; 
    } 


    public static ArrayList<String> getExtSdCardPaths(Context con) { 
     ArrayList<String> paths = new ArrayList<String>(); 
     File[] files = ContextCompat.getExternalFilesDirs(con, "external"); 
     File firstFile = files[0]; 
     for (File file : files) { 
      if (file != null && !file.equals(firstFile)) { 
       int index = file.getAbsolutePath().lastIndexOf("/Android/data"); 
       if (index < 0) { 
        Log.w("", "Unexpected external file dir: " + file.getAbsolutePath()); 
       } 
       else { 
        String path = file.getAbsolutePath().substring(0, index); 
        try { 
         path = new File(path).getCanonicalPath(); 
        } 
        catch (IOException e) { 
         // Keep non-canonical path. 
        } 
        paths.add(path); 
       } 
      } 
     } 
     return paths; 
    } 

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

     String documentPath = FileUtil.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; 
     } 
    } 


    private static String getVolumePath(final String volumeId, Context con) { 
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 
      return null; 
     } 

     try { 
      StorageManager mStorageManager = 
        (StorageManager) con.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) { 
        if (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; 
     } 
    } 


} 
+0

如果他们没有解释,downvotes不是很有帮助。这种方法适用于我。 downvoter看到什么问题? – Anonymous

+0

您的解决方案确实有效。谢谢 –

+0

为什么有人会为此投票?这实际上适用于我的情况。非常感谢 – Dante

0

我试图添加一个默认的保存目录,或者如果用户没有在我的应用程序的首选项屏幕中使用SAF UI选择一个自定义目录。用户可能会错过选择一个文件夹和应用程序可能会崩溃。在设备内存中添加默认的文件夹,你应该

DocumentFile saveDir = null; 
    saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory()); 
    String uriString = saveDir.getUri().toString(); 

    List<UriPermission> perms = getContentResolver().getPersistedUriPermissions(); 
    for (UriPermission p : perms) { 
     if (p.getUri().toString().equals(uriString) && p.isWritePermission()) { 
      canWrite = true; 
      break; 
     } 
    } 
    // Permitted to create a direct child of parent directory 
    DocumentFile newDir = null; 
    if (canWrite) { 
     newDir = saveDir.createDirectory("MyFolder"); 
    } 

    if (newDir != null && newDir.exists()) { 
     return newDir; 
    } 

这个片段将创建内部设备的主存储器中的目录,并授予该文件夹和子文件夹读/写权限。您不能直接创建MyFolder/MySubFolder层次结构,您应该再次创建另一个目录。

您可以检查该目录是否具有写入权限,就目前为止我在3台设备上看到的,如果使用DocumentFile而不是File类创建它,则返回true。这是一种创建和授予Android> = 5.0的写入权限的简单方法,无需使用ACTION_OPEN_DOCUMENT_TREE

+0

只要存在,您也可以将主目录设置为'Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)'或任何公用目录。 – Thracian