Add parent restoration
Add a failsafe to the persistence system that allows the parent of a playback state to be restored from the queue.
This commit is contained in:
parent
1af17a6df1
commit
7e0ee3d04f
8 changed files with 90 additions and 16 deletions
|
@ -26,6 +26,7 @@ import org.oxycblt.auxio.ui.getTransparentAccent
|
|||
import org.oxycblt.auxio.ui.toColor
|
||||
import kotlin.IllegalArgumentException
|
||||
|
||||
// TODO: Dedicated Search Tab?
|
||||
class MainFragment : Fragment() {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
|
@ -98,7 +99,7 @@ class MainFragment : Fragment() {
|
|||
|
||||
playbackModel.navToItem.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
// If the current destination isnt even LibraryFragment, then navigate there first
|
||||
// If the current destination isn't even LibraryFragment, then navigate there first
|
||||
if (binding.navBar.selectedItemId != R.id.library_fragment) {
|
||||
binding.navBar.selectedItemId = R.id.library_fragment
|
||||
} else {
|
||||
|
|
|
@ -235,17 +235,25 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
// --- QUEUE FUNCTIONS ---
|
||||
|
||||
/**
|
||||
* Go to the next song, along with doing all the checks that entails.
|
||||
*/
|
||||
fun next() {
|
||||
resetLoopMode()
|
||||
|
||||
// If there's anything in the user queue, go to the first song in there instead
|
||||
// of incrementing the index.
|
||||
if (mUserQueue.isNotEmpty()) {
|
||||
updatePlayback(mUserQueue[0])
|
||||
mUserQueue.removeAt(0)
|
||||
|
||||
// Mark that the playback state is currently in the user queue, for later.
|
||||
mIsInUserQueue = true
|
||||
|
||||
forceUserQueueUpdate()
|
||||
} else {
|
||||
// If not in the user queue, then increment the current index
|
||||
// If it cant be incremented anymore, end playback or loop depending on the setting.
|
||||
if (mIndex < mQueue.lastIndex) {
|
||||
mIndex = mIndex.inc()
|
||||
} else {
|
||||
|
@ -261,6 +269,9 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the previous song, doing any checks that are needed.
|
||||
*/
|
||||
fun prev() {
|
||||
if (mIndex > 0 && !mIsInUserQueue) {
|
||||
mIndex = mIndex.dec()
|
||||
|
@ -457,6 +468,8 @@ class PlaybackStateManager private constructor() {
|
|||
database.writeQueue(queueItems)
|
||||
}
|
||||
|
||||
getCommonGenre()
|
||||
|
||||
val time = System.currentTimeMillis() - start
|
||||
|
||||
Log.d(this::class.simpleName, "Save finished in ${time}ms")
|
||||
|
@ -486,6 +499,7 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
unpackFromPlaybackState(it)
|
||||
unpackQueue(queueItems)
|
||||
doParentSanityCheck()
|
||||
}
|
||||
|
||||
val time = System.currentTimeMillis() - start
|
||||
|
@ -568,7 +582,8 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
}
|
||||
|
||||
// Get a more accurate index [At least if were not in the user queue]
|
||||
// When done, get a more accurate index to prevent issues with queue songs that were saved
|
||||
// to the db but are now deleted when the restore occurred.
|
||||
if (!mIsInUserQueue) {
|
||||
mSong?.let {
|
||||
val index = mQueue.indexOf(it)
|
||||
|
@ -580,6 +595,62 @@ class PlaybackStateManager private constructor() {
|
|||
forceUserQueueUpdate()
|
||||
}
|
||||
|
||||
private fun doParentSanityCheck() {
|
||||
// Check if the parent was lost while in the DB.
|
||||
if (mSong != null && mParent == null && mMode != PlaybackMode.ALL_SONGS) {
|
||||
Log.d(this::class.simpleName, "Parent lost, attempting restore.")
|
||||
|
||||
mParent = when (mMode) {
|
||||
PlaybackMode.IN_ALBUM -> mQueue.firstOrNull()?.album
|
||||
PlaybackMode.IN_ARTIST -> mQueue.firstOrNull()?.album?.artist
|
||||
PlaybackMode.IN_GENRE -> getCommonGenre()
|
||||
PlaybackMode.ALL_SONGS -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the common genre out of a queue of songs that **should have a common genre**.
|
||||
* @return The **single** common genre, null if there isn't any or if there's multiple.
|
||||
*/
|
||||
private fun getCommonGenre(): Genre? {
|
||||
// Pool of "Possible" genres, these get narrowed down until the list is only
|
||||
// the actual genre(s) that all songs in the queue have in common.
|
||||
var genres = mutableListOf<Genre>()
|
||||
var otherGenres: MutableList<Genre>
|
||||
|
||||
for (queueSong in mQueue) {
|
||||
// If there's still songs to check despite the pool of genres being empty, re-add them.
|
||||
if (genres.size == 0) {
|
||||
genres.addAll(queueSong.album.artist.genres)
|
||||
continue
|
||||
}
|
||||
|
||||
otherGenres = genres.toMutableList()
|
||||
|
||||
// Iterate through the current genres and remove the ones that don't exist in this song,
|
||||
// narrowing down the pool of possible genres.
|
||||
for (genre in genres) {
|
||||
if (queueSong.album.artist.genres.find { it.id == genre.id } == null) {
|
||||
otherGenres.remove(genre)
|
||||
}
|
||||
}
|
||||
|
||||
genres = otherGenres.toMutableList()
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, "Found genre $genres")
|
||||
|
||||
// There should not be more than one common genre, so return null if that's the case
|
||||
if (genres.size > 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Sometimes the narrowing process will lead to a zero-size list, so return null if that
|
||||
// is the case.
|
||||
return genres.firstOrNull()
|
||||
}
|
||||
|
||||
// --- ORDERING FUNCTIONS ---
|
||||
|
||||
private fun setupOrderedQueue() {
|
||||
|
|
|
@ -82,19 +82,20 @@ class SongsFragment : Fragment() {
|
|||
val item = musicStore.songs[pos]
|
||||
iters++
|
||||
|
||||
var char = item.name[0].toUpperCase()
|
||||
|
||||
// If the item starts with "the"/"a", then actually use the character after that
|
||||
// as its initial. Yes, this is stupidly anglo-centric but the code [hopefully]
|
||||
// as its initial. Yes, this is stupidly western-centric but the code [hopefully]
|
||||
// shouldn't run with other languages.
|
||||
if (item.name.length > 5 &&
|
||||
val char: Char = if (item.name.length > 5 &&
|
||||
item.name.startsWith("the ", ignoreCase = true)
|
||||
) {
|
||||
char = item.name[4].toUpperCase()
|
||||
item.name[4].toUpperCase()
|
||||
} else if (item.name.length > 3 &&
|
||||
item.name.startsWith("a ", ignoreCase = true)
|
||||
) {
|
||||
char = item.name[2].toUpperCase()
|
||||
item.name[2].toUpperCase()
|
||||
} else {
|
||||
// If it doesn't begin with that word, then just use the first character.
|
||||
item.name[0].toUpperCase()
|
||||
}
|
||||
|
||||
// Check if this song starts with a number, if so, then concat it with a single
|
||||
|
|
|
@ -129,8 +129,8 @@
|
|||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:paddingBottom="@dimen/padding_small"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/genre_artist_header"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/genre_artist_header"
|
||||
tools:layout_editor_absoluteX="355dp"
|
||||
tools:src="@drawable/ic_sort_alpha_down" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
android:id="@+id/action_shuffle"
|
||||
android:icon="@drawable/ic_shuffle"
|
||||
android:title="@string/label_shuffle"
|
||||
app:showAsAction="ifRoom" />
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:icon="@drawable/ic_queue_add"
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_play"
|
||||
android:title="@string/label_play"
|
||||
android:icon="@drawable/ic_play"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/action_shuffle"
|
||||
android:icon="@drawable/ic_shuffle"
|
||||
android:title="@string/label_shuffle"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/action_play"
|
||||
android:title="@string/label_play"
|
||||
android:icon="@drawable/ic_play"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -42,7 +42,7 @@
|
|||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@animator/nav_default_exit_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim"
|
||||
app:destination="@id/album_detail_fragment"
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
<style name="TextAppearance.FastScroll" parent="TextAppearance.AppCompat.Body2">
|
||||
<item name="android:fontFamily">@font/inter_semibold</item>
|
||||
<item name="android:verticalSpacing">1dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.ThumbIndicator" parent="TextAppearance.FastScroll">
|
||||
|
|
Loading…
Reference in a new issue