问题描述
我的目标是能够使用Intent.ACTION_OPEN_DOCUMENT_TREE
获取用户选择的目录,然后从该目录获取所有音频文件及其元数据。我已经可以使用Intent.ACTION_OPEN_DOCUMENT_TREE
获取用户选择的目录,但是我无法提取音频文件元数据。我得到的是null而不是元数据。我认为问题在于返回到onActivityResult
的{{1}}的Uri与MediaStore Uri不兼容。
Intent
我在这里查询 void getAudioFiles(){
String[] projection = new String[]{
MediaStore.Audio.Media._ID,MediaStore.Audio.Media.disPLAY_NAME,MediaStore.Audio.Media.IS_MUSIC,MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.TITLE,MediaStore.Images.Media.DATA
};
String selection = MediaStore.Audio.Media.IS_MUSIC + " != ?";
String[] selectionArgs = new String[]{"0"};
String sortOrder = MediaStore.Video.Media.TITLE + " ASC";
try (Cursor cursor = getApplicationContext().getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,projection,selection,selectionArgs,sortOrder)) {
if (cursor != null) {
List<Uri> newURIs = getURIs(cursor);
}
}
}
private List<Uri> getURIs(Cursor cursor) {
ArrayList<Uri> newURIs = new ArrayList<>();
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.disPLAY_NAME);
int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
int artistCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);
int dataCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
while (cursor.movetoNext()) {
long id = cursor.getLong(idCol);
String displayName = cursor.getString(nameCol);
String title = cursor.getString(titleCol);
String artist = cursor.getString(artistCol);
String data = cursor.getString(dataCol);
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,id);
if (!uriMap.containsKey(uri)) {
AudioURI audioURI = new AudioURI(uri,data,displayName,artist,title,id);
uriMap.put(uri,audioURI);
}
newURIs.add(uri);
}
return newURIs;
}
。这使我相信,我需要一种基于MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
的{{1}}
首先,我设置一个onClickListener来要求用户选择目录:
onActivityResult
然后我得到结果并尝试解析数据:
Intent
这对于查找文件而不是获取元数据非常有用。每个 @Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String title = getResources().getString(R.string.pick_folder);
Intent chooser = Intent.createChooser(intent,title);
if (intent.resolveActivity(activityMain.getPackageManager()) != null) {
startActivityForResult(chooser,REQUEST_CODE_OPEN_FOLDER);
}
}
列都有一个空值。
对于我的第二次尝试,我尝试使用 @Override
public void onActivityResult(int requestCode,int resultCode,Intent resultData) {
if (requestCode == REQUEST_CODE_OPEN_FOLDER && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
final int takeFlags = resultData.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION);
ContentResolver contentResolver = activityMain.getContentResolver();
contentResolver.takePersistableuriPermission(uri,takeFlags);
traverseDirectoryEntries(uri);
}
}
}
void traverseDirectoryEntries(Uri rootUri) {
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
rootUri,DocumentsContract.getTreeDocumentId(rootUri));
getFiles(childrenUri,rootUri);
}
private void getFiles(Uri childrenUri,Uri rootUri) {
ContentResolver contentResolver = activityMain.getContentResolver();
List<TreeViewNode> treeViewNodes = new LinkedList<>();
String selection = MediaStore.Audio.Media.IS_MUSIC + " != ? OR" +
DocumentsContract.Document.COLUMN_MIME_TYPE + " == ?";
String[] selectionArgs = new String[]{"0"},DocumentsContract.Document.MIME_TYPE_DIR};
try (Cursor cursor = contentResolver.query(childrenUri,new String[]{
DocumentsContract.Document.COLUMN_DOCUMENT_ID,DocumentsContract.Document.COLUMN_disPLAY_NAME,DocumentsContract.Document.COLUMN_MIME_TYPE,MediaStore.Audio.Media._ID,MediaStore.Audio.Media.DATA},null)) {
if (cursor != null) {
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.disPLAY_NAME);
int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
int artistCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);
int dataCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
while (cursor.movetoNext()) {
String docId = cursor.getString(0);
String name = cursor.getString(1);
String mime = cursor.getString(2);
long id = cursor.getLong(idCol);
this.uriID = id;
String displayName = cursor.getString(nameCol);
String title = cursor.getString(titleCol);
String artist = cursor.getString(artistCol);
String data = cursor.getString(dataCol);
Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,id);
if (isDirectory(mime)) {
Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(
rootUri,docId);
getFiles(newNode,rootUri);
} else {
audioFiles.add(childrenUri);
}
}
}
}
}
private static boolean isDirectory(String mimeType) {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
,但收到“ java.lang.RuntimeException:setDataSource失败:status = 0x80000000”:
MediaStore.Audio.Media
我认为这可能是一个安全问题,因为MediaMetadataRetriever没有具有读取权限的根uri。这是因为从MediaStore Uri检索的相同文件与MediaMetadataRetriever兼容。
对于我的第三次尝试,我尝试根据MediaMetadataRetriever
的{{1}}返回的文档ID从MediaStore中进行选择,但是我得到了“ android.database.sqlite.sqliteException:没有这样的列:document_id(代码1 sqlITE_ERROR)”:
MediaMetadataRetriever mediaMetadataRetriever;
mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(activityMain.getApplicationContext(),childrenUri);
解决方法
Intent.ACTION_OPEN_DOCUMENT_TREE为您提供了不同于媒体存储库的其他提供商的SAF uri。
用saf uri查询媒体存储毫无意义。
在最新的Android上,您可以将一些安全的uri转换为mediastore的uri。反之亦然。
然后使用媒体存储库urie查询媒体存储库。
,我找到了解决此问题的一种方法:
private void getAudioUri(String displayName) {
ContentResolver contentResolver = activityMain.getContentResolver();
String selection = MediaStore.Audio.Media.DISPLAY_NAME + " == ?";
String[] selectionArgs = new String[]{displayName};
try (Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,new String[]{
MediaStore.Audio.Media._ID,MediaStore.Audio.Media.DISPLAY_NAME,MediaStore.Audio.Media.TITLE,MediaStore.Audio.Media.ARTIST,MediaStore.Audio.Media.DATA
},selection,selectionArgs,null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
int artistCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);
int dataCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
long id = cursor.getLong(idCol);
this.mediaStoreUriID = id;
String displayName2 = cursor.getString(nameCol);
String title = cursor.getString(titleCol);
String artist = cursor.getString(artistCol);
String data = cursor.getString(dataCol);
Uri uri1 = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,id);
}
}
}
}
在这种情况下,传入的displayName来自文档的MediaStore.Audio.Media.DISPLAY_NAME
列。出于某种原因,可以从Intent中提取的Uri中获得元数据。
我不确定文件是否始终都具有“ MediaStore.Audio.Media.DISPLAY_NAME”。不过,这适用于Android Pie和Marshmellow。