music: split display name fix across versions

When having to fall back to an alternative displayName field, use
the version-specific RELATIVE_PATH and DATA fields when necessary.
This commit is contained in:
OxygenCobalt 2022-06-05 19:11:58 -06:00
parent dd00c70488
commit fb3c32b14c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 55 additions and 27 deletions

View file

@ -2,8 +2,11 @@
## dev [v2.3.2, v2.4.0, or v3.0.0]
#### What's New
- Folders on external drives can now be excluded on Android Q+ [#134]
#### What's Improved
- Genre parsing now handles multiple integer values and cover/remix indicators
- Genre parsing now handles multiple integer values and cover/remix indicators (May wipe playback state)
#### Dev/Meta
- New translations [Fjuro -> Czech]

View file

@ -21,7 +21,10 @@ import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/** A ViewModel representing the current music indexing state. */
/**
* A ViewModel representing the current music indexing state.
* @author OxygenCobalt
*/
class IndexerViewModel : ViewModel(), Indexer.Callback {
private val indexer = Indexer.getInstance()

View file

@ -25,6 +25,7 @@ import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.core.database.getIntOrNull
import androidx.core.database.getStringOrNull
import java.io.File
import org.oxycblt.auxio.music.Indexer
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.albumCoverUri
@ -104,14 +105,13 @@ import org.oxycblt.auxio.util.logW
abstract class MediaStoreBackend : Indexer.Backend {
private var idIndex = -1
private var titleIndex = -1
private var fileIndex = -1
private var displayNameIndex = -1
private var durationIndex = -1
private var yearIndex = -1
private var albumIndex = -1
private var albumIdIndex = -1
private var artistIndex = -1
private var albumArtistIndex = -1
private var dataIndex = -1
override fun query(context: Context): Cursor {
val settingsManager = SettingsManager.getInstance()
@ -193,14 +193,14 @@ abstract class MediaStoreBackend : Indexer.Backend {
// We need to initialize the cursor indices.
idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns._ID)
titleIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TITLE)
fileIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME)
displayNameIndex =
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DISPLAY_NAME)
durationIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DURATION)
yearIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.YEAR)
albumIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM)
albumIdIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ALBUM_ID)
artistIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.ARTIST)
albumArtistIndex = cursor.getColumnIndexOrThrow(AUDIO_COLUMN_ALBUM_ARTIST)
dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATA)
}
val audio = Audio()
@ -212,12 +212,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
// from the android system. Once again though, OEM issues get in our way and
// this field isn't available on some platforms. In that case, see if we can
// grok a file name from the DATA field.
audio.displayName =
cursor.getStringOrNull(fileIndex)
?: cursor
.getStringOrNull(dataIndex)
?.substringAfterLast('/', MediaStore.UNKNOWN_STRING)
?: MediaStore.UNKNOWN_STRING
audio.displayName = cursor.getStringOrNull(displayNameIndex)
audio.duration = cursor.getLong(durationIndex)
audio.year = cursor.getIntOrNull(yearIndex)
@ -317,8 +312,7 @@ abstract class MediaStoreBackend : Indexer.Backend {
MediaStore.Audio.AudioColumns.ALBUM,
MediaStore.Audio.AudioColumns.ALBUM_ID,
MediaStore.Audio.AudioColumns.ARTIST,
AUDIO_COLUMN_ALBUM_ARTIST,
MediaStore.Audio.AudioColumns.DATA)
AUDIO_COLUMN_ALBUM_ARTIST)
/**
* The base selector that works across all versions of android. Does not exclude
@ -334,9 +328,12 @@ abstract class MediaStoreBackend : Indexer.Backend {
*/
class Api21MediaStoreBackend : MediaStoreBackend() {
private var trackIndex = -1
private var dataIndex = -1
override val projection: Array<String>
get() = super.projection + arrayOf(MediaStore.Audio.AudioColumns.TRACK)
get() =
super.projection +
arrayOf(MediaStore.Audio.AudioColumns.TRACK, MediaStore.Audio.AudioColumns.DATA)
override fun buildExcludedSelector(dirs: List<ExcludedDirectory>): Selector {
val base = Environment.getExternalStorageDirectory().absolutePath
@ -363,6 +360,7 @@ class Api21MediaStoreBackend : MediaStoreBackend() {
// Initialize the TRACK index if we have not already.
if (trackIndex == -1) {
trackIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.TRACK)
dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.DATA)
}
// TRACK is formatted as DTTT where D is the disc number and T is the track number.
@ -379,6 +377,14 @@ class Api21MediaStoreBackend : MediaStoreBackend() {
}
}
if (audio.displayName == null) {
audio.displayName =
cursor
.getStringOrNull(dataIndex)
?.substringAfterLast(File.separatorChar, MediaStore.UNKNOWN_STRING)
?: MediaStore.UNKNOWN_STRING
}
return audio
}
}
@ -390,12 +396,10 @@ class Api21MediaStoreBackend : MediaStoreBackend() {
*/
@RequiresApi(Build.VERSION_CODES.Q)
open class Api29MediaStoreBackend : MediaStoreBackend() {
private var relativePathIndex = -1
override val projection: Array<String>
get() =
super.projection +
arrayOf(
MediaStore.Audio.AudioColumns.VOLUME_NAME,
MediaStore.Audio.AudioColumns.RELATIVE_PATH)
get() = super.projection + arrayOf(MediaStore.Audio.AudioColumns.RELATIVE_PATH)
override fun buildExcludedSelector(dirs: List<ExcludedDirectory>): Selector {
var selector = BASE_SELECTOR
@ -422,6 +426,25 @@ open class Api29MediaStoreBackend : MediaStoreBackend() {
return Selector(selector, args)
}
override fun buildAudio(context: Context, cursor: Cursor): Audio {
val audio = super.buildAudio(context, cursor)
if (relativePathIndex != -1) {
relativePathIndex =
cursor.getColumnIndexOrThrow(MediaStore.Audio.AudioColumns.RELATIVE_PATH)
}
if (audio.displayName == null) {
audio.displayName =
cursor
.getStringOrNull(relativePathIndex)
?.substringAfterLast(File.separatorChar, MediaStore.UNKNOWN_STRING)
?: MediaStore.UNKNOWN_STRING
}
return audio
}
}
/**

View file

@ -18,6 +18,7 @@
package org.oxycblt.auxio.music.excluded
import android.os.Build
import java.io.File
import org.oxycblt.auxio.util.logW
data class ExcludedDirectory(val volume: Volume, val relativePath: String) {
@ -44,10 +45,9 @@ data class ExcludedDirectory(val volume: Volume, val relativePath: String) {
}
companion object {
private const val VOLUME_SEPARATOR = ':'
fun fromString(dir: String): ExcludedDirectory? {
val split = dir.split(VOLUME_SEPARATOR, limit = 2)
val split = dir.split(File.pathSeparator, limit = 2)
val volume = Volume.fromString(split.getOrNull(0) ?: return null)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && volume is Volume.Secondary) {
logW("Cannot use secondary volumes below API 29")
@ -55,6 +55,7 @@ data class ExcludedDirectory(val volume: Volume, val relativePath: String) {
}
val relativePath = split.getOrNull(1) ?: return null
return ExcludedDirectory(volume, relativePath)
}
}

View file

@ -139,9 +139,6 @@ class PlaybackService :
// --- PLAYBACKSTATEMANAGER SETUP ---
playbackManager.addCallback(this)
// --- SETTINGSMANAGER SETUP ---
settingsManager.addCallback(this)
logD("Service created")

View file

@ -24,6 +24,7 @@ import android.database.sqlite.SQLiteOpenHelper
import android.os.Build
import android.os.Environment
import androidx.core.content.edit
import java.io.File
import org.oxycblt.auxio.music.excluded.ExcludedDirectory
import org.oxycblt.auxio.ui.accent.Accent
import org.oxycblt.auxio.util.logD
@ -90,7 +91,7 @@ fun handleAccentCompat(prefs: SharedPreferences): Accent {
*/
fun handleExcludedCompat(context: Context): List<ExcludedDirectory> {
val db = LegacyExcludedDatabase(context)
val primaryPrefix = Environment.getExternalStorageDirectory().absolutePath + '/'
val primaryPrefix = Environment.getExternalStorageDirectory().absolutePath + File.separatorChar
return db.readPaths().map { path ->
val relativePath = path.removePrefix(primaryPrefix)
ExcludedDirectory(ExcludedDirectory.Volume.Primary, relativePath)