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:
parent
dd00c70488
commit
fb3c32b14c
6 changed files with 55 additions and 27 deletions
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,9 +139,6 @@ class PlaybackService :
|
|||
// --- PLAYBACKSTATEMANAGER SETUP ---
|
||||
|
||||
playbackManager.addCallback(this)
|
||||
|
||||
// --- SETTINGSMANAGER SETUP ---
|
||||
|
||||
settingsManager.addCallback(this)
|
||||
|
||||
logD("Service created")
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue