From 574e129f34a226d370e4163c7df4057d346c7716 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 1 Jan 2024 14:08:47 -0700 Subject: [PATCH] music: cleanup and document --- .../auxio/music/fs/MediaStoreExtractor.kt | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStoreExtractor.kt b/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStoreExtractor.kt index 5dbb968cf..618ad9b2a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStoreExtractor.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/fs/MediaStoreExtractor.kt @@ -101,7 +101,7 @@ interface MediaStoreExtractor { // instead. Build.MANUFACTURER.equals("huawei", ignoreCase = true) || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q -> - Api24PathInterpreter.Factory(volumeManager) + DataPathInterpreter.Factory(volumeManager) else -> VolumePathInterpreter.Factory(volumeManager) } @@ -345,24 +345,60 @@ private class MediaStoreExtractorImpl( } } +/** + * Wrapper around a [Cursor] that interprets path information on a per-API/manufacturer basis. + * + * @author Alexander Capehart (OxygenCobalt) + */ private sealed interface PathInterpreter { + /** + * Populate the [RawSong] with version-specific path information. + * + * @param rawSong The [RawSong] to populate. + * @return True if the path was successfully populated, false otherwise. + */ fun populate(rawSong: RawSong): Boolean interface Factory { + /** The columns that must be added to a query to support this interpreter. */ val projection: Array + /** + * Wrap a [Cursor] with this interpreter. This cursor should be the result of a query + * containing the columns specified by [projection]. + * + * @param cursor The [Cursor] to wrap. + * @return A new [PathInterpreter] that will work best on the device's API level. + */ fun wrap(cursor: Cursor): PathInterpreter + /** + * Create a selector that will filter the given paths. By default this will filter *to* the + * given paths, to exclude them, use a NOT. + * + * @param paths The paths to filter for. + * @return A selector that will filter to the given paths, or null if a selector could not + * be created from the paths. + */ fun createSelector(paths: List): Selector? + /** + * A selector that will filter to the given paths. + * + * @param template The template to use for the selector. + * @param args The arguments to use for the selector. + * @see Factory.createSelector + */ data class Selector(val template: String, val args: List) } } -private open class Api24PathInterpreter( - private val cursor: Cursor, - private val volumeManager: VolumeManager -) : PathInterpreter { +/** + * Wrapper around a [Cursor] that interprets the DATA column as a path. Create an instance with + * [Factory]. + */ +private class DataPathInterpreter +private constructor(private val cursor: Cursor, volumeManager: VolumeManager) : PathInterpreter { private val dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATA) private val volumes = volumeManager.getVolumes() @@ -382,6 +418,11 @@ private open class Api24PathInterpreter( return false } + /** + * Factory for [DataPathInterpreter]. + * + * @param volumeManager The [VolumeManager] to use for volume information. + */ class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory { override val projection: Array get() = @@ -411,12 +452,16 @@ private open class Api24PathInterpreter( } override fun wrap(cursor: Cursor): PathInterpreter = - Api24PathInterpreter(cursor, volumeManager) + DataPathInterpreter(cursor, volumeManager) } } -private class VolumePathInterpreter(private val cursor: Cursor, volumeManager: VolumeManager) : - PathInterpreter { +/** + * Wrapper around a [Cursor] that interprets the VOLUME_NAME, RELATIVE_PATH, and DISPLAY_NAME + * columns as a path. Create an instance with [Factory]. + */ +private class VolumePathInterpreter +private constructor(private val cursor: Cursor, volumeManager: VolumeManager) : PathInterpreter { private val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME) private val volumeIndex = @@ -438,6 +483,11 @@ private class VolumePathInterpreter(private val cursor: Cursor, volumeManager: V return true } + /** + * Factory for [VolumePathInterpreter]. + * + * @param volumeManager The [VolumeManager] to use for volume information. + */ class Factory(private val volumeManager: VolumeManager) : PathInterpreter.Factory { override val projection: Array get() = @@ -486,12 +536,30 @@ private class VolumePathInterpreter(private val cursor: Cursor, volumeManager: V } } +/** + * Wrapper around a [Cursor] that interprets certain tags on a per-API basis. + * + * @author Alexander Capehart (OxygenCobalt) + */ private sealed interface TagInterpreter { + /** + * Populate the [RawSong] with version-specific tags. + * + * @param rawSong The [RawSong] to populate. + */ fun populate(rawSong: RawSong) interface Factory { + /** The columns that must be added to a query to support this interpreter. */ val projection: Array + /** + * Wrap a [Cursor] with this interpreter. This cursor should be the result of a query + * containing the columns specified by [projection]. + * + * @param cursor The [Cursor] to wrap. + * @return A new [TagInterpreter] that will work best on the device's API level. + */ fun wrap(cursor: Cursor): TagInterpreter } }