diff --git a/CHANGELOG.md b/CHANGELOG.md index 186558c62..74f7b60b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 3.4.2 + +#### What's Fixed +- Fixed "Add to queue" incorrectly changing the queue and crashing the app +- Fixed 1x4 and 1x3 widgets having square edges +- Fixed crash when music library updates in such a way to change music information +- Fixed crash when music library updates while scrolled in a list +- Fixed inconsistent corner radius in wafer widgets + ## 3.4.1 #### What's Fixed diff --git a/README.md b/README.md index 85006d016..03010bdbe 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@

Auxio

A simple, rational music player for android.

- - Latest Version + + Latest Version Releases diff --git a/app/build.gradle b/app/build.gradle index d17211886..868e7d8f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,8 +21,8 @@ android { defaultConfig { applicationId namespace - versionName "3.4.1" - versionCode 42 + versionName "3.4.2" + versionCode 43 minSdk 24 targetSdk 34 diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index 74c942dae..a3ad98835 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -95,7 +95,7 @@ class AlbumListFragment : } override fun getPopup(pos: Int): String? { - val album = homeModel.albumList.value[pos] + val album = homeModel.albumList.value.getOrNull(pos) ?: return null // Change how we display the popup depending on the current sort mode. return when (homeModel.albumSort.mode) { // By Name -> Use Name diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt index 7dc885308..0f99cdb48 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/ArtistListFragment.kt @@ -90,7 +90,7 @@ class ArtistListFragment : } override fun getPopup(pos: Int): String? { - val artist = homeModel.artistList.value[pos] + val artist = homeModel.artistList.value.getOrNull(pos) ?: return null // Change how we display the popup depending on the current sort mode. return when (homeModel.artistSort.mode) { // By Name -> Use Name diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt index 3307fa721..ef001d36e 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/GenreListFragment.kt @@ -89,7 +89,7 @@ class GenreListFragment : } override fun getPopup(pos: Int): String? { - val genre = homeModel.genreList.value[pos] + val genre = homeModel.genreList.value.getOrNull(pos) ?: return null // Change how we display the popup depending on the current sort mode. return when (homeModel.genreSort.mode) { // By Name -> Use Name diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt index 4228c872a..e0fe15bec 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/PlaylistListFragment.kt @@ -87,7 +87,7 @@ class PlaylistListFragment : } override fun getPopup(pos: Int): String? { - val playlist = homeModel.playlistList.value[pos] + val playlist = homeModel.playlistList.value.getOrNull(pos) ?: return null // Change how we display the popup depending on the current sort mode. return when (homeModel.playlistSort.mode) { // By Name -> Use Name diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index 04f9847f1..6a5d91bcf 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -92,7 +92,7 @@ class SongListFragment : } override fun getPopup(pos: Int): String? { - val song = homeModel.songList.value[pos] + val song = homeModel.songList.value.getOrNull(pos) ?: return null // Change how we display the popup depending on the current sort mode. // Note: We don't use the more correct individual artist name here, as sorts are largely // based off the names of the parent objects and not the child objects. diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt index 459c8fcbb..6ec2c5c09 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicRepository.kt @@ -305,35 +305,35 @@ constructor( val userLibrary = synchronized(this) { userLibrary ?: return } logD("Creating playlist $name with ${songs.size} songs") userLibrary.createPlaylist(name, songs) - dispatchLibraryChange(device = false, user = true) + withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } } override suspend fun renamePlaylist(playlist: Playlist, name: String) { val userLibrary = synchronized(this) { userLibrary ?: return } logD("Renaming $playlist to $name") userLibrary.renamePlaylist(playlist, name) - dispatchLibraryChange(device = false, user = true) + withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } } override suspend fun deletePlaylist(playlist: Playlist) { val userLibrary = synchronized(this) { userLibrary ?: return } logD("Deleting $playlist") userLibrary.deletePlaylist(playlist) - dispatchLibraryChange(device = false, user = true) + withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } } override suspend fun addToPlaylist(songs: List, playlist: Playlist) { val userLibrary = synchronized(this) { userLibrary ?: return } logD("Adding ${songs.size} songs to $playlist") userLibrary.addToPlaylist(playlist, songs) - dispatchLibraryChange(device = false, user = true) + withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } } override suspend fun rewritePlaylist(playlist: Playlist, songs: List) { val userLibrary = synchronized(this) { userLibrary ?: return } logD("Rewriting $playlist with ${songs.size} songs") userLibrary.rewritePlaylist(playlist, songs) - dispatchLibraryChange(device = false, user = true) + withContext(Dispatchers.Main) { dispatchLibraryChange(device = false, user = true) } } @Synchronized diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt index 2374a421f..259d5ab97 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt @@ -153,6 +153,9 @@ interface PlaybackStateHolder { * ack. */ fun applySavedState(parent: MusicParent?, rawQueue: RawQueue, ack: StateAck.NewPlayback?) + + /** Reset this instance to an empty state. */ + fun reset(ack: StateAck.NewPlayback) } /** 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 7498c8e0b..fceb344a5 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 @@ -492,7 +492,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { } else { val stateHolder = stateHolder ?: return logD("Adding ${songs.size} songs to end of queue") - stateHolder.addToQueue(songs, StateAck.AddToQueue(stateMirror.index + 1, songs.size)) + stateHolder.addToQueue(songs, StateAck.AddToQueue(queue.size, songs.size)) } } @@ -717,6 +717,8 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { return } + val stateHolder = stateHolder ?: return + // The heap may not be the same if the song composition changed between state saves/reloads. // This also means that we must modify the shuffled mapping as well, in what it points to // and it's general composition. @@ -741,19 +743,20 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { } // Make sure we re-align the index to point to the previously playing song. - fun pointingAtSong(): Boolean { + fun pointingAtSong(index: Int): Boolean { val currentSong = if (shuffledMapping.isNotEmpty()) { - shuffledMapping.getOrNull(savedState.index)?.let { heap.getOrNull(it) } + shuffledMapping.getOrNull(index)?.let { heap.getOrNull(it) } } else { - heap.getOrNull(savedState.index) + heap.getOrNull(index) } + logD(currentSong) return currentSong?.uid == savedState.songUid } var index = savedState.index - while (!pointingAtSong() && index > -1) { + while (!pointingAtSong(index) && index > -1) { index-- } @@ -763,32 +766,30 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { "Queue inconsistency detected: Shuffled mapping indices out of heap bounds" } + if (index < 0) { + stateHolder.reset(StateAck.NewPlayback) + return + } + val rawQueue = RawQueue( heap = heap, shuffledMapping = shuffledMapping, heapIndex = if (shuffledMapping.isNotEmpty()) { - shuffledMapping[savedState.index] + shuffledMapping[index] } else { index }) - if (index > -1) { - // Valid state where something needs to be played, direct the stateholder to apply - // this new state. - val oldStateMirror = stateMirror - if (oldStateMirror.rawQueue != rawQueue) { - logD("Queue changed, must reload player") - stateHolder?.applySavedState(parent, rawQueue, StateAck.NewPlayback) - stateHolder?.playing(false) - } - - if (oldStateMirror.progression.calculateElapsedPositionMs() != savedState.positionMs) { - logD("Seeking to saved position ${savedState.positionMs}ms") - stateHolder?.seekTo(savedState.positionMs) - stateHolder?.playing(false) - } + // Valid state where something needs to be played, direct the stateholder to apply + // this new state. + val oldStateMirror = stateMirror + if (oldStateMirror.rawQueue != rawQueue) { + logD("Queue changed, must reload player") + stateHolder.playing(false) + stateHolder.applySavedState(parent, rawQueue, StateAck.NewPlayback) + stateHolder.seekTo(savedState.positionMs) } isInitialized = true diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/BetterShuffleOrder.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/BetterShuffleOrder.kt index e09d9ba2a..b3b067fa4 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/BetterShuffleOrder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/BetterShuffleOrder.kt @@ -68,8 +68,16 @@ class BetterShuffleOrder(private val shuffled: IntArray) : ShuffleOrder { return BetterShuffleOrder(insertionCount, -1) } + // TODO: Fix this scuffed hacky logic + // TODO: Play next ordering needs to persist in unshuffle + val newShuffled = IntArray(shuffled.size + insertionCount) - val pivot = indexInShuffled[insertionIndex] + val pivot: Int = + if (insertionIndex < shuffled.size) { + indexInShuffled[insertionIndex] + } else { + indexInShuffled.size + } for (i in shuffled.indices) { var currentIndex = shuffled[i] if (currentIndex > insertionIndex) { @@ -82,8 +90,14 @@ class BetterShuffleOrder(private val shuffled: IntArray) : ShuffleOrder { newShuffled[i + insertionCount] = currentIndex } } - for (i in 0 until insertionCount) { - newShuffled[pivot + i + 1] = insertionIndex + i + 1 + if (insertionIndex < shuffled.size) { + for (i in 0 until insertionCount) { + newShuffled[pivot + i + 1] = insertionIndex + i + 1 + } + } else { + for (i in 0 until insertionCount) { + newShuffled[pivot + i] = insertionIndex + i + } } return BetterShuffleOrder(newShuffled) } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index ba54a1f18..0250a6a85 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -485,6 +485,11 @@ class PlaybackService : ack?.let { playbackManager.ack(this, it) } } + override fun reset(ack: StateAck.NewPlayback) { + player.setMediaItems(emptyList()) + playbackManager.ack(this, ack) + } + // --- PLAYER OVERRIDES --- override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt index 7a3bc6c40..3fb28172d 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt @@ -158,6 +158,7 @@ class WidgetProvider : AppWidgetProvider() { uiSettings, ) .setupCover(context, state.takeIf { canDisplayWaferCover(uiSettings) }) + .setupFillingCover(uiSettings) .setupTimelineControls(context, state) private fun newWideWaferLayout( @@ -170,6 +171,7 @@ class WidgetProvider : AppWidgetProvider() { uiSettings, ) .setupCover(context, state.takeIf { canDisplayWaferCover(uiSettings) }) + .setupFillingCover(uiSettings) .setupFullControls(context, state) private fun newThinDockedLayout( @@ -231,9 +233,9 @@ class WidgetProvider : AppWidgetProvider() { // On API 31+, the bar should always be round in order to fit in with other widgets. val background = if (useRoundedRemoteViews(uiSettings)) { - R.drawable.ui_widget_bar_round + R.drawable.ui_widget_bg_round } else { - R.drawable.ui_widget_bar_system + R.drawable.ui_widget_bg_sharp } setBackgroundResource(R.id.widget_controls, background) return this @@ -253,7 +255,7 @@ class WidgetProvider : AppWidgetProvider() { if (useRoundedRemoteViews(uiSettings)) { R.drawable.ui_widget_bg_round } else { - R.drawable.ui_widget_bg_system + R.drawable.ui_widget_bg_sharp } setBackgroundResource(android.R.id.background, background) return this @@ -292,6 +294,20 @@ class WidgetProvider : AppWidgetProvider() { return this } + private fun RemoteViews.setupFillingCover(uiSettings: UISettings): RemoteViews { + // Below API 31, enable a rounded background only if round mode is enabled. + // On API 31+, the background should always be round in order to fit in with other + // widgets. + val background = + if (useRoundedRemoteViews(uiSettings)) { + R.drawable.ui_widget_bg_round + } else { + R.drawable.ui_widget_bg_sharp + } + setBackgroundResource(R.id.widget_cover, background) + return this + } + /** * Set up the album cover, song title, and artist name in a [RemoteViews] layout that contains * them. diff --git a/app/src/main/res/drawable-v31/ui_widget_bar_system.xml b/app/src/main/res/drawable-v31/ui_widget_bg_round.xml similarity index 100% rename from app/src/main/res/drawable-v31/ui_widget_bar_system.xml rename to app/src/main/res/drawable-v31/ui_widget_bg_round.xml index b6144dd5f..893bae085 100644 --- a/app/src/main/res/drawable-v31/ui_widget_bar_system.xml +++ b/app/src/main/res/drawable-v31/ui_widget_bg_round.xml @@ -1,6 +1,6 @@ - + diff --git a/app/src/main/res/drawable/ui_widget_bar_round.xml b/app/src/main/res/drawable/ui_widget_bar_round.xml deleted file mode 100644 index 9fcd8308b..000000000 --- a/app/src/main/res/drawable/ui_widget_bar_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ui_widget_bar_system.xml b/app/src/main/res/drawable/ui_widget_bg_sharp.xml similarity index 100% rename from app/src/main/res/drawable/ui_widget_bar_system.xml rename to app/src/main/res/drawable/ui_widget_bg_sharp.xml diff --git a/app/src/main/res/drawable/ui_widget_bg_system.xml b/app/src/main/res/drawable/ui_widget_bg_system.xml deleted file mode 100644 index 9d33d5912..000000000 --- a/app/src/main/res/drawable/ui_widget_bg_system.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/app/src/main/res/layout/widget_docked_thin.xml b/app/src/main/res/layout/widget_docked_thin.xml index 2b2566812..086013dcc 100644 --- a/app/src/main/res/layout/widget_docked_thin.xml +++ b/app/src/main/res/layout/widget_docked_thin.xml @@ -61,7 +61,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_gravity="center" - android:background="@drawable/ui_widget_bar_system" + android:background="@drawable/ui_widget_bg_round" android:backgroundTint="?attr/colorSurface" android:orientation="horizontal" android:padding="@dimen/spacing_mid_medium"> diff --git a/app/src/main/res/layout/widget_docked_wide.xml b/app/src/main/res/layout/widget_docked_wide.xml index f0b129ae5..d344799f4 100644 --- a/app/src/main/res/layout/widget_docked_wide.xml +++ b/app/src/main/res/layout/widget_docked_wide.xml @@ -48,7 +48,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_gravity="center" - android:background="@drawable/ui_widget_bar_system" + android:background="@drawable/ui_widget_bg_round" android:backgroundTint="?attr/colorSurface" android:orientation="horizontal" android:padding="@dimen/spacing_mid_medium"> diff --git a/app/src/main/res/layout/widget_pane_thin.xml b/app/src/main/res/layout/widget_pane_thin.xml index 4dc4c394c..2b2361971 100644 --- a/app/src/main/res/layout/widget_pane_thin.xml +++ b/app/src/main/res/layout/widget_pane_thin.xml @@ -4,7 +4,7 @@ android:id="@android:id/background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/ui_widget_bg_system" + android:background="@drawable/ui_widget_bg_sharp" android:backgroundTint="?attr/colorSurface" android:theme="@style/Theme.Auxio.Widget"> diff --git a/app/src/main/res/layout/widget_pane_wide.xml b/app/src/main/res/layout/widget_pane_wide.xml index 2ef525fa3..07c693564 100644 --- a/app/src/main/res/layout/widget_pane_wide.xml +++ b/app/src/main/res/layout/widget_pane_wide.xml @@ -4,7 +4,7 @@ android:id="@android:id/background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/ui_widget_bg_system" + android:background="@drawable/ui_widget_bg_sharp" android:backgroundTint="?attr/colorSurface" android:theme="@style/Theme.Auxio.Widget"> diff --git a/app/src/main/res/layout/widget_wafer_thin.xml b/app/src/main/res/layout/widget_wafer_thin.xml index fe7ec01dc..db12288cf 100644 --- a/app/src/main/res/layout/widget_wafer_thin.xml +++ b/app/src/main/res/layout/widget_wafer_thin.xml @@ -4,7 +4,7 @@ android:id="@android:id/background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/ui_widget_bg_system" + android:background="@drawable/ui_widget_bg_sharp" android:backgroundTint="?attr/colorSurface" android:baselineAligned="false" android:orientation="horizontal" @@ -20,6 +20,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" + android:background="@drawable/ui_widget_bg_round" + android:clipToOutline="true" tools:ignore="ContentDescription" /> -