playback: fix queue moves

Fix moving items with the new queue system.

This took a bit of thinking, but I think this is the correct way to
implement this in a future-proof manner.
This commit is contained in:
Alexander Capehart 2023-01-07 12:00:53 -07:00
parent 5adc87550e
commit bef4dca0ce
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 96 additions and 47 deletions

View file

@ -23,10 +23,10 @@ import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper import android.database.sqlite.SQLiteOpenHelper
import androidx.core.database.getIntOrNull import androidx.core.database.getIntOrNull
import androidx.core.database.getStringOrNull import androidx.core.database.getStringOrNull
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.parsing.correctWhitespace import org.oxycblt.auxio.music.parsing.correctWhitespace
import org.oxycblt.auxio.music.parsing.splitEscaped import org.oxycblt.auxio.music.parsing.splitEscaped
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.util.* import org.oxycblt.auxio.util.*
/** /**

View file

@ -27,7 +27,6 @@ import androidx.annotation.RequiresApi
import androidx.core.database.getIntOrNull import androidx.core.database.getIntOrNull
import androidx.core.database.getStringOrNull import androidx.core.database.getStringOrNull
import java.io.File import java.io.File
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.music.MusicSettings import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.parsing.parseId3v2Position import org.oxycblt.auxio.music.parsing.parseId3v2Position
@ -38,6 +37,7 @@ import org.oxycblt.auxio.music.storage.mediaStoreVolumeNameCompat
import org.oxycblt.auxio.music.storage.safeQuery import org.oxycblt.auxio.music.storage.safeQuery
import org.oxycblt.auxio.music.storage.storageVolumesCompat import org.oxycblt.auxio.music.storage.storageVolumesCompat
import org.oxycblt.auxio.music.storage.useQuery import org.oxycblt.auxio.music.storage.useQuery
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.util.getSystemServiceCompat import org.oxycblt.auxio.util.getSystemServiceCompat
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.nonZeroOrNull import org.oxycblt.auxio.util.nonZeroOrNull

View file

@ -22,10 +22,10 @@ import androidx.core.text.isDigitsOnly
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.MetadataRetriever import com.google.android.exoplayer2.MetadataRetriever
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.parsing.parseId3v2Position import org.oxycblt.auxio.music.parsing.parseId3v2Position
import org.oxycblt.auxio.music.storage.toAudioUri import org.oxycblt.auxio.music.storage.toAudioUri
import org.oxycblt.auxio.music.tags.Date
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logW import org.oxycblt.auxio.util.logW

View file

@ -235,7 +235,8 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
val tokens = val tokens =
// Match the input with the timestamp regex. If there is no match, see if we can // Match the input with the timestamp regex. If there is no match, see if we can
// fall back to some kind of year value. // fall back to some kind of year value.
(ISO8601_REGEX.matchEntire(timestamp) ?: return timestamp.toIntOrNull()?.let(Companion::from)) (ISO8601_REGEX.matchEntire(timestamp)
?: return timestamp.toIntOrNull()?.let(Companion::from))
.groupValues .groupValues
// Filter to the specific tokens we want and convert them to integer tokens. // Filter to the specific tokens we want and convert them to integer tokens.
.mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null } .mapIndexedNotNull { index, s -> if (index % 2 != 0) s.toIntOrNull() else null }

View file

@ -1,3 +1,20 @@
/*
* Copyright (c) 2023 Auxio Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.music.tags package org.oxycblt.auxio.music.tags
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
@ -5,8 +22,8 @@ import org.oxycblt.auxio.R
/** /**
* The type of release an [Album] is considered. This includes EPs, Singles, Compilations, etc. * The type of release an [Album] is considered. This includes EPs, Singles, Compilations, etc.
* *
* This class is derived from the MusicBrainz Release Group Type specification. It can be found * This class is derived from the MusicBrainz Release Group Type specification. It can be found at:
* at: https://musicbrainz.org/doc/Release_Group/Type * https://musicbrainz.org/doc/Release_Group/Type
* @author Alexander Capehart (OxygenCobalt) * @author Alexander Capehart (OxygenCobalt)
*/ */
sealed class ReleaseType { sealed class ReleaseType {
@ -21,8 +38,8 @@ sealed class ReleaseType {
/** /**
* A plain album. * A plain album.
* @param refinement A specification of what kind of performance this release is. If null, * @param refinement A specification of what kind of performance this release is. If null, the
* the release is considered "Plain". * release is considered "Plain".
*/ */
data class Album(override val refinement: Refinement?) : ReleaseType() { data class Album(override val refinement: Refinement?) : ReleaseType() {
override val stringRes: Int override val stringRes: Int
@ -37,8 +54,8 @@ sealed class ReleaseType {
/** /**
* A "Extended Play", or EP. Usually a smaller release consisting of 4-5 songs. * A "Extended Play", or EP. Usually a smaller release consisting of 4-5 songs.
* @param refinement A specification of what kind of performance this release is. If null, * @param refinement A specification of what kind of performance this release is. If null, the
* the release is considered "Plain". * release is considered "Plain".
*/ */
data class EP(override val refinement: Refinement?) : ReleaseType() { data class EP(override val refinement: Refinement?) : ReleaseType() {
override val stringRes: Int override val stringRes: Int
@ -53,8 +70,8 @@ sealed class ReleaseType {
/** /**
* A single. Usually a release consisting of 1-2 songs. * A single. Usually a release consisting of 1-2 songs.
* @param refinement A specification of what kind of performance this release is. If null, * @param refinement A specification of what kind of performance this release is. If null, the
* the release is considered "Plain". * release is considered "Plain".
*/ */
data class Single(override val refinement: Refinement?) : ReleaseType() { data class Single(override val refinement: Refinement?) : ReleaseType() {
override val stringRes: Int override val stringRes: Int
@ -69,8 +86,8 @@ sealed class ReleaseType {
/** /**
* A compilation. Usually consists of many songs from a variety of artists. * A compilation. Usually consists of many songs from a variety of artists.
* @param refinement A specification of what kind of performance this release is. If null, * @param refinement A specification of what kind of performance this release is. If null, the
* the release is considered "Plain". * release is considered "Plain".
*/ */
data class Compilation(override val refinement: Refinement?) : ReleaseType() { data class Compilation(override val refinement: Refinement?) : ReleaseType() {
override val stringRes: Int override val stringRes: Int
@ -108,8 +125,8 @@ sealed class ReleaseType {
} }
/** /**
* A Mix-tape. These are usually [EP]-sized releases of music made to promote an [Artist] or * A Mix-tape. These are usually [EP]-sized releases of music made to promote an [Artist] or a
* a future release. * future release.
*/ */
object Mixtape : ReleaseType() { object Mixtape : ReleaseType() {
override val refinement: Refinement? override val refinement: Refinement?
@ -133,7 +150,8 @@ sealed class ReleaseType {
* Parse a [ReleaseType] from a string formatted with the MusicBrainz Release Group Type * Parse a [ReleaseType] from a string formatted with the MusicBrainz Release Group Type
* specification. * specification.
* @param types A list of values consisting of valid release type values. * @param types A list of values consisting of valid release type values.
* @return A [ReleaseType] consisting of the given types, or null if the types were not valid. * @return A [ReleaseType] consisting of the given types, or null if the types were not
* valid.
*/ */
fun parse(types: List<String>): ReleaseType? { fun parse(types: List<String>): ReleaseType? {
val primary = types.getOrNull(0) ?: return null val primary = types.getOrNull(0) ?: return null
@ -150,12 +168,12 @@ sealed class ReleaseType {
} }
/** /**
* Parse "secondary" types (i.e not [Album], [EP], or [Single]) from a string formatted * Parse "secondary" types (i.e not [Album], [EP], or [Single]) from a string formatted with
* with the MusicBrainz Release Group Type specification. * the MusicBrainz Release Group Type specification.
* @param index The index of the release type to parse. * @param index The index of the release type to parse.
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType] corresponding * @param convertRefinement Code to convert a [Refinement] into a [ReleaseType]
* to the callee's context. This is used in order to handle secondary times that are * corresponding to the callee's context. This is used in order to handle secondary times
* actually [Refinement]s. * that are actually [Refinement]s.
* @return A [ReleaseType] corresponding to the secondary type found at that index. * @return A [ReleaseType] corresponding to the secondary type found at that index.
*/ */
private inline fun List<String>.parseSecondaryTypes( private inline fun List<String>.parseSecondaryTypes(
@ -174,12 +192,12 @@ sealed class ReleaseType {
} }
/** /**
* Parse "secondary" types (i.e not [Album], [EP], [Single]) that do not correspond to * Parse "secondary" types (i.e not [Album], [EP], [Single]) that do not correspond to any
* any child values. * child values.
* @param type The release type value to parse. * @param type The release type value to parse.
* @param convertRefinement Code to convert a [Refinement] into a [ReleaseType] corresponding * @param convertRefinement Code to convert a [Refinement] into a [ReleaseType]
* to the callee's context. This is used in order to handle secondary times that are * corresponding to the callee's context. This is used in order to handle secondary times
* actually [Refinement]s. * that are actually [Refinement]s.
*/ */
private inline fun parseSecondaryTypeImpl( private inline fun parseSecondaryTypeImpl(
type: String?, type: String?,

View file

@ -112,6 +112,21 @@ class Queue {
} }
} }
/**
* Reformat the queue's internal representation to align with the given values. This is not
* useful in most circumstances.
* @param
*/
fun rework(heap: List<Song?>, orderedMapping: IntArray, shuffledMapping: IntArray) {
// val instructions = mutableListOf<Int?>()
// val currentBackshift = 0
// for (song in heap) {
// if (song == null) {
// instructions.add(0, )
// }
// }
}
/** /**
* Add [Song]s to the top of the queue. Will start playback if nothing is playing. * Add [Song]s to the top of the queue. Will start playback if nothing is playing.
* @param songs The [Song]s to add. * @param songs The [Song]s to add.
@ -179,18 +194,17 @@ class Queue {
orderedMapping.add(dst, orderedMapping.removeAt(src)) orderedMapping.add(dst, orderedMapping.removeAt(src))
} }
// TODO: I really need to figure out how to get non-swap moves working. when (index) {
return when (index) { // We are moving the currently playing song, correct the index to it's new position.
src -> { src -> index = dst
index = dst // We have moved an song from behind the playing song to in front, shift back.
ChangeResult.INDEX in (src + 1)..dst -> index -= 1
} // We have moved an song from in front of the playing song to behind, shift forward.
dst -> { in dst until src -> index += 1
index = src
ChangeResult.INDEX
}
else -> ChangeResult.MAPPING else -> ChangeResult.MAPPING
} }
return ChangeResult.INDEX
} }
/** /**

View file

@ -1,3 +1,20 @@
/*
* Copyright (c) 2023 Auxio Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.oxycblt.auxio.music.library package org.oxycblt.auxio.music.library
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
@ -5,10 +22,6 @@ import org.oxycblt.auxio.music.Song
class LibraryTest { class LibraryTest {
companion object { companion object {
val LIBRARY = listOf( val LIBRARY = listOf(Song.Raw())
Song.Raw(
)
)
} }
} }

View file

@ -30,7 +30,8 @@ class ReleaseTypeTest {
@Test @Test
fun releaseType_parse_secondary() { fun releaseType_parse_secondary() {
assertEquals(ReleaseType.Compilation(null), ReleaseType.parse(listOf("album", "compilation"))) assertEquals(
ReleaseType.Compilation(null), ReleaseType.parse(listOf("album", "compilation")))
assertEquals(ReleaseType.Soundtrack, ReleaseType.parse(listOf("album", "soundtrack"))) assertEquals(ReleaseType.Soundtrack, ReleaseType.parse(listOf("album", "soundtrack")))
assertEquals(ReleaseType.Mix, ReleaseType.parse(listOf("album", "dj-mix"))) assertEquals(ReleaseType.Mix, ReleaseType.parse(listOf("album", "dj-mix")))
assertEquals(ReleaseType.Mixtape, ReleaseType.parse(listOf("album", "mixtape/street"))) assertEquals(ReleaseType.Mixtape, ReleaseType.parse(listOf("album", "mixtape/street")))
@ -39,7 +40,8 @@ class ReleaseTypeTest {
@Test @Test
fun releaseType_parse_modifiers() { fun releaseType_parse_modifiers() {
assertEquals( assertEquals(
ReleaseType.Album(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("album", "live"))) ReleaseType.Album(ReleaseType.Refinement.LIVE),
ReleaseType.parse(listOf("album", "live")))
assertEquals( assertEquals(
ReleaseType.Album(ReleaseType.Refinement.REMIX), ReleaseType.Album(ReleaseType.Refinement.REMIX),
ReleaseType.parse(listOf("album", "remix"))) ReleaseType.parse(listOf("album", "remix")))
@ -75,7 +77,8 @@ class ReleaseTypeTest {
@Test @Test
fun releaseType_parse_orphanedModifier() { fun releaseType_parse_orphanedModifier() {
assertEquals(ReleaseType.Album(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("live"))) assertEquals(
ReleaseType.Album(ReleaseType.Refinement.LIVE), ReleaseType.parse(listOf("live")))
assertEquals( assertEquals(
ReleaseType.Album(ReleaseType.Refinement.REMIX), ReleaseType.parse(listOf("remix"))) ReleaseType.Album(ReleaseType.Refinement.REMIX), ReleaseType.parse(listOf("remix")))
} }