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.
-
-
+
+
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" />
-