diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 96dbdbf71..1f05107d5 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -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 { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 2cfc83697..e2085e922 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -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() + var otherGenres: MutableList + + 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() { diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 1820d3a87..560dbbf26 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -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 diff --git a/app/src/main/res/layout/fragment_genre_detail.xml b/app/src/main/res/layout/fragment_genre_detail.xml index e7e2c338e..4e04f313c 100644 --- a/app/src/main/res/layout/fragment_genre_detail.xml +++ b/app/src/main/res/layout/fragment_genre_detail.xml @@ -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" /> + app:showAsAction="never" /> - + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_explore.xml b/app/src/main/res/navigation/nav_explore.xml index 5cf0b55c6..63c0c6a4c 100644 --- a/app/src/main/res/navigation/nav_explore.xml +++ b/app/src/main/res/navigation/nav_explore.xml @@ -42,7 +42,7 @@ @font/inter_semibold + 1dp