Android ContentResolver 如何指定排序顺序?

问题描述 投票:0回答:1

我试图按正确的顺序对 mp3 文件按字母顺序排序,但没有成功。 我尝试使用不同的排序顺序,但它不起作用。

List<Uri> listUriSub = new ArrayList<>();
ContentResolver contentResolver = mContext.getContentResolver();
Timber.tag("SUB_FOLDER_NODE").e(" %s", rootUri.getPath());
String sortOrder = DocumentsContract.Document.COLUMN_DISPLAY_NAME + " DESC";
String sortOrder2 = DocumentsContract.Document.COLUMN_DISPLAY_NAME + " COLLATE NOCASE ASC";
Cursor c = contentResolver.query(rootUri, new String[]{
                DocumentsContract.Document.COLUMN_DOCUMENT_ID,
                DocumentsContract.Document.COLUMN_DISPLAY_NAME,
                DocumentsContract.Document.COLUMN_MIME_TYPE},
        null, null, sortOrder2);
try {
    while (c.moveToNext()) {
        final String docId = c.getString(0);
        final String name = c.getString(1);
        final String mime = c.getString(2);
        // Timber.tag("SCAN_FILE_ID").e( "docId: " + docId + ", name: " + name + ", mime: " + mime);
        Timber.tag("FILE_ORDER_NAME").e(name);
        //IS DIRECTORY
        if (isDirectory(mime)) {
            final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
            // Timber.tag("FOLDER: ").e(name);
        }
        //IS FILE
        else {
            Timber.tag("FILE: ").e(name);
            if (name.contains(".mp3") || name.contains(".MP3")) {
                final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId);
                listUriSub.add(newNode);

            }
        }
    }
}

示例 OTPUT 1

02a_WP_Druzyna_Pierscienia_Prolog_O_fajkowym_zielu.mp3
01a_WP_Druzyna_Pierscienia_Prolog_W_sprawie_hobbitow.mp3
01b_WP_Druzyna_Pierscienia_Prolog_W_sprawie_hobbitow.mp3

应该是

01a_WP_Druzyna_Pierscienia_Prolog_W_sprawie_hobbitow.mp3
01b_WP_Druzyna_Pierscienia_Prolog_W_sprawie_hobbitow.mp3
02a_WP_Druzyna_Pierscienia_Prolog_O_fajkowym_zielu.mp3

输出示例

02_metro_2035.mp3
05_metro_2035.mp3
03_metro_2035.mp3
04_metro_2035.mp3
01_metro_2035.mp3

应该是

01_metro_2035.mp3
02_metro_2035.mp3
03_metro_2035.mp3
04_metro_2035.mp3
05_metro_2035.mp3

问题可能是标题或数字中的“_”?

android sqlite android-studio android-sqlite android-contentresolver
1个回答
0
投票

您的问题标签不太准确;这应该主要是与 Android SAF 相关的问题。

我目前面临着同样的问题,经过研究,我发现了无法做到的原因。

背景

我通过

Intent.ACTION_OPEN_DOCUMENT_TREE
获得了 PERSISTABLE_URI_PERMISSION。

然后我尝试查询文件列表,通过

DocumentsContract.buildChildDocumentsUriUsingTree
构造的 URI 是(如你所做的):

content://com.android.externalstorage.documents/tree/primary%3ADocuments/document/primary%3ADocuments%2FXXXX/children

而且,首先要了解一个继承关系,才能理解我下面跟踪的路径:有权限的内容提供者

com.android.externalstorage.documents
就是ExternalStorageProvider类,ExternalStorageProvider继承自FileSystemProvider。

DocumentsProvider -> FileSystemProvider -> ExternalStorageProvider
  • PS:这个
    ExternalStorageProvider
    内容提供程序位于com.android.externalstorage包中,它是一个系统应用程序。

分析

根据android-34 android\provider\DocumentsProvider.java

public final Cursor query(
        Uri uri, String[] projection, Bundle queryArgs, CancellationSignal cancellationSignal) {
    try {
        switch (mMatcher.match(uri)) {
            case MATCH_ROOTS:
                return queryRoots(projection);
            case MATCH_RECENT:
                return queryRecentDocuments(
                        getRootId(uri), projection, queryArgs, cancellationSignal);
            case MATCH_SEARCH:
                return querySearchDocuments(getRootId(uri), projection, queryArgs);
            case MATCH_DOCUMENT:
            case MATCH_DOCUMENT_TREE:
                enforceTree(uri);
                return queryDocument(getDocumentId(uri), projection);
            case MATCH_CHILDREN:
            case MATCH_CHILDREN_TREE:
                enforceTree(uri);
                if (DocumentsContract.isManageMode(uri)) {
                    // TODO: Update "ForManage" variant to support query args.
                    return queryChildDocumentsForManage(
                            getDocumentId(uri),
                            projection,
                            getSortClause(queryArgs));
                } else {
                    return queryChildDocuments(getDocumentId(uri), projection, queryArgs);
                }
            default:
                throw new UnsupportedOperationException("Unsupported Uri " + uri);
        }
    } catch (FileNotFoundException e) {
        Log.w(TAG, "Failed during query", e);
        return null;
    }
}

并且:

mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE);
mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE);

所以,我们构造的URI对应于

MATCH_CHILDREN_TREE
,它调用:
return queryChildDocuments(getDocumentId(uri), projection, queryArgs);

在DocumentsProvider中,追踪到底,它调用了抽象方法:

public abstract Cursor queryChildDocuments(
    String parentDocumentId, String[] projection, String sortOrder)
    throws FileNotFoundException;
  • 原始
    queryArgs
    通过
    sortOrder
    存储在
    getSortClause(queryArgs)
    中。

我们来到 FileSystemProvider 类,跳过一些重载方法:

private Cursor queryChildDocuments(
        String parentDocumentId, String[] projection, String sortOrder,
        @NonNull Predicate<File> filter) throws FileNotFoundException {
    final File parent = getFileForDocId(parentDocumentId);
    final MatrixCursor result = new DirectoryCursor(
            resolveProjection(projection), parentDocumentId, parent);

    if (!filter.test(parent)) {
        Log.w(TAG, "No permission to access parentDocumentId: " + parentDocumentId);
        return result;
    }

    if (parent.isDirectory()) {
        for (File file : FileUtils.listFilesOrEmpty(parent)) {
            if (filter.test(file)) {
                includeFile(result, null, file);
            }
        }
    } else {
        Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
    }
    return result;
}

我们可以看到FileSystemProvider实际上不使用sortOrder。并且ExternalStorageProvider 也不会覆盖这些方法。

其他

所以我们知道使用

DocumentsContract.buildChildDocumentsUriUsingTree
查询本地存储文件是不适用查询条件和排序的。

但是,我看到

querySearchDocuments
实际上正在使用,对应于:
DocumentsContract.buildSearchDocumentsUri(String authority, String rootId, String query)

测试时遇到权限问题:

requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
。因为,正如背景中提到的,我使用了
Intent.ACTION_OPEN_DOCUMENT_TREE

根据文档

However, instead of directly accessing the target directory, the returned URI will leverage access granted through a subtree URI, typically returned by Intent#ACTION_OPEN_DOCUMENT_TREE. The target directory must be a descendant (child, grandchild, etc) of the subtree.

所以这个权限实际上并不符合,我还没有进一步尝试。

© www.soinside.com 2019 - 2024. All rights reserved.