Моя цель - прочитать файл плейлиста (M3U) и запрашивать/прочитать метаданные для каждого файла. Однако я как бы застрял в разрешении. Остальное должно быть «автоматическим» < /p>
Во-первых, я прошу разрешение на чтение для файла плейлиста, выполнив: < /p>
val filePickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
onResult = { uri: Uri? ->
context.contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
context.contentResolver.openInputStream(it)?.use { inputStream -> // readfile}
// ADD OPTION 1 OR 2 HERE
}
< /code>
У меня есть два варианта; fun scanMediaStore(): MutableList {
val mediaFiles: MutableList = mutableListOf()
val projection = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM_ID,
)
val selectionArgs = arrayOf("%.mp3", "%.m4a")
val selection = selectionArgs.joinToString(" OR ") {
"${MediaStore.Audio.Media.DATA} LIKE ?"
}
val sortOrder = null
fun queryMediaStore(uri: Uri) {
Log.d("Scanning", "queryMediaStore: $uri")
app.contentResolver.query(
uri,
projection,
selection,
selectionArgs,
sortOrder
)?.use { cursor ->
while (cursor.moveToNext()) {
val getOrThrow: (String) -> Int = { columnName ->
cursor.getColumnIndexOrThrow(columnName)
}
val mediaID: Int = cursor.getInt(getOrThrow(MediaStore.Audio.Media._ID))
val name: String = cursor.getString(getOrThrow(MediaStore.Audio.Media.TITLE))
val path: String = cursor.getString(getOrThrow(MediaStore.Audio.Media.DATA))
val artist: String = cursor.getString(getOrThrow(MediaStore.Audio.Media.ARTIST))
val duration: Int = cursor.getInt(getOrThrow(MediaStore.Audio.Media.DURATION))
Log.d("MediaFiles", "Found: $path")
mediaFiles.add(...)
}
}
}
queryMediaStore(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)
queryMediaStore(MediaStore.Audio.Media.INTERNAL_CONTENT_URI)
return mediaFiles
}
for path in playlist:
MatchAgainst(path, scanMediaStore()) // pseudo code; matches paths/names against already scanned files
< /code>
или лучший вариант (который борется за работу)fun getMediaIDFor(path: String) : int {
???
}
fun getMetaDataAndAdd(path: String) : int {
???
}
// pseudo code
for path in playlist:
itemID = getMediaIDFor(path)
if (itemID not in scanMediaStore()) {
getMetaDataAndAdd(itemID/path)
}
< /code>
Для тех, кто заинтересован в том, что я специально говорит о M3U, сгенерированном Samsungmusic, и файлы обычно содержат относительные пути к файлу. (Итак, "../../..../sdcardid/foo/bar/song.mp3")
Что я пробовал:
непосредственно чтение FilePaths (ошибка разрешения)
Query uri (ursired yrrish-k prety-ktride" lang-kt-ktide "// Uri is built with
val playlistDocId = DocumentsContract.getDocumentId(it)
val basePath = playlistDocId.substringBeforeLast("/")
val baseUri = DocumentsContract.buildDocumentUri("com.android.externalstorage.documents", basePath)
var resolvedUri = resolveRelativeUri(baseUri, path) // where path is the relative path from the playlist file
fun resolveRelativeUri(baseUri: Uri, relativePath: String): Uri {
val baseDocId = DocumentsContract.getDocumentId(baseUri)
val baseParts = baseDocId.split(":", limit = 2)
val storageRoot = baseParts[0]
val baseDir = baseParts.getOrNull(1) ?: ""
val normalizedPath = File("/$baseDir", relativePath).normalize().path.removePrefix("/")
val resolvedDocId = "$storageRoot:$normalizedPath"
return DocumentsContract.buildDocumentUri(
"com.android.externalstorage.documents",
resolvedDocId
)
}
fun resolveRelativeUri(baseUri: Uri, relativePath: String): Uri {
val baseDocId = DocumentsContract.getDocumentId(baseUri)
val baseParts = baseDocId.split(":", limit = 2)
val storageRoot = baseParts[0]
val baseDir = baseParts.getOrNull(1) ?: ""
val normalizedPath = File("/$baseDir", relativePath).normalize().path.removePrefix("/")
val resolvedDocId = "$storageRoot:$normalizedPath"
return DocumentsContract.buildDocumentUri(
"com.android.externalstorage.documents",
resolvedDocId
)
}
suspend fun getSongFromUri(uri: Uri): ...? = withContext(Dispatchers.IO) {
Log.d("ViewModel", "Trying to read $uri")
val projection = arrayOf(
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DURATION
)
app.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
if (cursor.moveToFirst()) {
val id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID))
val title =
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE))
val path =
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA))
val artist =
cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST))
val duration =
cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION))
return@withContext ...
}
}
return@withContext null
}
< /code>
его JetPack Compose
Target API - Android 10+; Тем не менее, обратная совместимость приветствуется.
Любая помощь ценится.
Подробнее здесь: https://stackoverflow.com/questions/796 ... filepicker