Моя цель - получить выбранный пользователем каталог с помощью Intent.ACTION_OPEN_DOCUMENT_TREE
, а затем получить все аудиофайлы и их метаданные из этого каталога. Я уже могу получить выбранный пользователем каталог с помощью Intent.ACTION_OPEN_DOCUMENT_TREE
, но не могу извлечь метаданные аудиофайла. Я получаю null вместо метаданных. Я считаю, что проблема в том, что Uri, возвращенный onActivityResult
Intent
, несовместим с Uri MediaStore.
В настоящее время я могу получить метаданные из MediaStore, используя следующий код:
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
. Это наводит меня на мысль, что мне нужен способ выбора из MediaStore на основе Uri, возвращаемого onActivityResult
Intent
.
Вот моя первая попытка получить метаданные, используя выбранный пользователем каталог.
Во-первых, я установил onClickListener, чтобы попросить пользователя выбрать каталог:
@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.DISPLAY_NAME,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DATA},
selection, selectionArgs, 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);
}
Это отлично работает для поиска файлов, но не для получения метаданных. Каждый столбец MediaStore.Audio.Media
имеет нулевое значение.
Во второй попытке я попытался использовать MediaMetadataRetriever
, но получаю java.lang.RuntimeException: setDataSource failed: status = 0x80000000:
MediaMetadataRetriever mediaMetadataRetriever;
mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(activityMain.getApplicationContext(), childrenUri);
Я думаю, что это может быть проблемой безопасности, потому что MediaMetadataRetriever не имеет корневого uri с разрешением на чтение. Это связано с тем, что те же файлы, полученные из Uri MediaStore, совместимы с MediaMetadataRetriever.
Для моей третьей попытки я пытаюсь выбрать из MediaStore на основе идентификатора документа, возвращенного onActivityResult
Intent
, но я получаю android.database.sqlite.SQLiteException: нет такого столбца: document_id (код 1 SQLITE_ERROR):
ContentResolver contentResolver = activityMain.getContentResolver();
String selection = DocumentsContract.Document.COLUMN_DOCUMENT_ID + " == ?";
String[] selectionArgs = new String[]{docID};
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.uriID = id;
String displayName = 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);
}
}
}
Как можно получить метаданные аудиофайла из выбранного пользователем каталога?
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Здесь ничего нельзя дарить. Удалите это заявление. Это ничего не делает. Разве я не говорил об этом раньше? - person blackapps   schedule 21.10.2020getMediaUri()
. - person CommonsWare   schedule 21.10.2020