0

该应用程序使用ACTION_OPEN_DOCUMENT_TREE将用户发送到 SAF 选择器:

void openStoragePicker() {
    String messageTitle = "Choose directory app to use";
    Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(Intent.createChooser(intent, messageTitle), Dry.REQUEST_CHOOSE_APP_DIR);
}

onActivityResult中,我们获取持久权限并存储 Uri 的字符串:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    switch (resultCode) {
        case Activity.RESULT_OK:
            if (requestCode == Dry.REQUEST_CHOOSE_APP_DIR) {
                if (resultData == null) {
                    Log.d(Dry.TAG, "result data null");
                } else {
                    if (resultData.getData() != null) {
                        Uri uri = resultData.getData();
                        Storage.releasePersistedPermissions(this);
                        getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                        Storage.setSharedPrefString(uri.toString(), Storage.SHARED_PREF_APP_DIR_URI, this);
                        dbw.clearAlreadyPlayed();
                    }
                }
            }
            break;
        case Activity.RESULT_CANCELED:
            //
            break;
    }
}

我们在需要时重新创建树 Uri:

static Uri getTheDir(Context context) {
    String result = Storage.getSharedPrefString(SHARED_PREF_APP_DIR_URI, context);
    if (result == DEFAULT_SHARED_PREF_STRING) {
        return null;
    }
    Uri dirUriParsed = Uri.parse(Uri.decode(result));
    Log.d(Dry.TAG, "the dir uri parsed: " + dirUriParsed.toPath());
    return dirUriParsed;
}

我们想要列出文件,我们可以使用此处显示的模式。

static ArrayList<String> getFiles(Context context) {
    ArrayList<String> fileStrings = new ArrayList<>();
    Uri rootUri = getTheDir(context);
    if (rootUri == null) {
        return fileStrings;
    }

    long startTime = System.currentTimeMillis();

    ContentResolver contentResolver = context.getContentResolver();
    String theDocToReturnChildrenFor = DocumentsContract.getTreeDocumentId(rootUri);
    Log.d(Dry.TAG, "theDocToReturnChildrenFor: " + theDocToReturnChildrenFor);
    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, theDocToReturnChildrenFor);
    List<Uri> dirNodes = new LinkedList<>();
    dirNodes.add(childrenUri);
    while(!dirNodes.isEmpty()) {
        childrenUri = dirNodes.remove(0);
        Cursor c = contentResolver.query(childrenUri, new String[]{DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_MIME_TYPE}, null, null, null);
        try {
            while (c.moveToNext()) {
                final String docId = c.getString(0);
                final String name = c.getString(1);
                final String mime = c.getString(2);
                if (isDirectory(mime)) {
                    if (Arrays.asList(SUBDIRECTORIES_TO_OMIT).contains(name)) {
                        continue;
                    }
                    final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                    dirNodes.add(newNode);
                } else {
                    for (String ext: SUPPORTED_FILE_EXTENSIONS) {
                        if (name.endsWith(ext)) {
                            fileStrings.add(docId);
                            break;
                        }
                    }
                }
            }
        } finally {
            closeQuietly(c);
        }
    }

    Log.d(Dry.TAG, "fileStrings length: " + fileStrings.size() + "time spent building song list: " + ((System.currentTimeMillis() - startTime) / 1000.0) + "s");
    return fileStrings;
}

但是,只有当目录恰好是存储卷中的顶级目录时,这才会按预期工作。如果用户选择的目录不是卷根的直接子目录,那么当我们尝试时DocumentsContract.getTreeDocumentId(rootUri),它返回的不是该 URI 的文档 ID,而是卷根之前其最高父级的文档 ID!

打印重建的 Uri 的日志调用给出以下输出:

解析的 dir uri:/tree/primary:a test dir/a child test dir/3rd level dir

但是另一个打印文档 ID 的日志调用会打印:

theDocToReturnChildrenFor:主要:测试目录

我在做吗?这是Android错误吗?我注意到这个问题描述了这种方法的完全相同的行为。通过遵循既定的递归列表模式可以解决该问题,但是,该用户说:

几乎就像 getTreeDocumentId(rootUri) 正在返回 getRootId(rootUri) 应该返回的内容。

这种方法的文档没有帮助,它们很简短并且有错字,意思不清楚。DocumentsContract.getTreeDocumentId 文档

该应用的目标SDK为30。设备Android版本也是api 30(Android 11)。

如果有人可以帮助我为用户选择的目录获取正确的文档 ID,我将不胜感激。

4

1 回答 1

0

Uri dirUriParsed = Uri.parse(Uri.decode(result))

尝试:

  Uri dirUriParsed = Uri.parse(result)
于 2021-11-16T07:11:50.847 回答