我试图按正确的顺序对 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 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
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.
所以这个权限实际上并不符合,我还没有进一步尝试。