From 245e0f5dc1a83884c2385a8745639cb1299c4e01 Mon Sep 17 00:00:00 2001
From: Clyde <34314096+cccClyde@users.noreply.github.com>
Date: Mon, 7 Feb 2022 12:26:22 +0800
Subject: [PATCH 01/38] Update strings.xml
Update zh-rCN translations accordingly with the latest `strings.xml`.
---
app/src/main/res/values-zh-rCN/strings.xml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 8ccb2373e..a1eabcaa2 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -103,6 +103,8 @@
内容
保存播放状态
立即保存当前播放状态
+ 重新加载音乐
+ 将会重启应用
排除文件夹
被排除文件夹的内容将从媒体库中隐藏
@@ -143,6 +145,7 @@
未知艺术家
未知流派
没有日期
+ 无曲目编号
未播放音乐
歌曲名称
艺术家姓名
@@ -169,10 +172,12 @@
已加载 %d 首曲目
+ - %d 首歌曲
- "%d 首歌曲"
+ - %d 张专辑
- "%d 张专辑"
From 4a326cc4ffc698c3fcf74c48cb7f50444c2920b1 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Mon, 7 Feb 2022 20:08:18 -0700
Subject: [PATCH 02/38] all: fix sloppy code
Fix some dumb and sloppy code that I made in the rush to complete
2.2.0.
---
CHANGELOG.md | 8 +++++---
.../main/java/org/oxycblt/auxio/music/MusicLoader.kt | 8 ++++----
.../auxio/playback/state/PlaybackStateManager.kt | 11 +----------
app/src/main/res/values-ar-rIQ/strings.xml | 2 +-
4 files changed, 11 insertions(+), 18 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 891991233..1b9a0b8dc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,12 @@
# Changelog
## dev [v2.2.1 or v2.3.0]
+- Updated chinese translations [courtesy of cccClyde]
## v2.2.0
#### What's New:
-- Added arabic translations [courtesy of hasanpasha]
-- Better russian translations [courtesy of lisiczka43]
+- Added Arabic translations [Courtesy of hasanpasha]
+- Improved Russian translations [Courtesy of lisiczka43]
- Added option to reload the music library
#### What's Improved:
@@ -18,9 +19,10 @@ artist they are grouped up in
#### What's Fixed:
- Fixed crash on some devices configured to use French or Czech translations
-- Malformed indicies should now be corrected when the playback state is restored
+- Malformed indices should now be corrected when the playback state is restored
- Fixed issue where track numbers would not be shown in the native language's numeric format
- Fixed issue where the preference view would apply the M3 switches inconsistently
+- Fixed issue where the now playing indicator on the playback screen would use an internal name
#### Dev/Meta:
- Removed 1.4.X compat
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index d978a4a71..f6a5cbdc6 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -72,7 +72,6 @@ import java.lang.Exception
*
* @author OxygenCobalt
*/
-@Suppress("InlinedApi")
class MusicLoader {
data class Library(
val genres: List,
@@ -107,6 +106,7 @@ class MusicLoader {
)
}
+ @Suppress("InlinedApi")
private fun loadSongs(context: Context): List {
var songs = mutableListOf()
val blacklistDatabase = ExcludedDatabase.getInstance(context)
@@ -285,7 +285,7 @@ class MusicLoader {
private fun readGenres(context: Context, songs: List): List {
val genres = mutableListOf()
- val genreCursor = context.contentResolver.query(
+ val genreCursor = context.applicationContext.contentResolver.query(
MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
arrayOf(
MediaStore.Audio.Genres._ID,
@@ -338,7 +338,7 @@ class MusicLoader {
val genreSongs = mutableListOf()
// Don't even bother blacklisting here as useless iterations are less expensive than IO
- val songCursor = context.contentResolver.query(
+ val songCursor = context.applicationContext.contentResolver.query(
MediaStore.Audio.Genres.Members.getContentUri("external", genreId),
arrayOf(MediaStore.Audio.Genres.Members._ID),
null, null, null
@@ -356,7 +356,7 @@ class MusicLoader {
}
}
- // Some genres might be empty due to MediaStore empty.
+ // Some genres might be empty due to MediaStore insanity.
// If that is the case, we drop them.
return genreSongs.ifEmpty { null }
}
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 0fe3b6348..171fa7cb1 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
@@ -519,8 +519,6 @@ class PlaybackStateManager private constructor() {
val database = PlaybackStateDatabase.getInstance(context)
- logD("$mPlaybackMode")
-
database.writeState(
PlaybackStateDatabase.SavedState(
mSong, mPosition, mParent, mIndex,
@@ -595,14 +593,6 @@ class PlaybackStateManager private constructor() {
private fun unpackQueue(queue: MutableList) {
mQueue = queue
-
- // Sanity check: Ensure that the
- mSong?.let { song ->
- while (mQueue.getOrNull(mIndex) != song) {
- mIndex--
- }
- }
-
pushQueueUpdate()
}
@@ -633,6 +623,7 @@ class PlaybackStateManager private constructor() {
if (correctedIndex > -1) {
logD("Correcting malformed index to $correctedIndex")
mIndex = correctedIndex
+ pushQueueUpdate()
}
}
}
diff --git a/app/src/main/res/values-ar-rIQ/strings.xml b/app/src/main/res/values-ar-rIQ/strings.xml
index 475457b40..bff4fce1b 100644
--- a/app/src/main/res/values-ar-rIQ/strings.xml
+++ b/app/src/main/res/values-ar-rIQ/strings.xml
@@ -177,7 +177,7 @@
- - ألبومات d%
+ - %d ألبومات
- %d البوم
- %d ألبومات
- %d ألبومات
From f4217a337a2ee05151ef047776d8f892dd04612d Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 8 Feb 2022 06:23:43 -0700
Subject: [PATCH 03/38] style: tweak body typography
Apply body typography in new places in the app.
For awhile, Body and Title typography were used interchangeably, as
they occupy the same text size range. This commit defines the Body
text as to be used for one-line widgets or tertiary widgets, while
the Title text is defined to be used for multi-line or heading widgets.
---
app/src/main/res/layout/fragment_search.xml | 3 +-
app/src/main/res/layout/item_album_song.xml | 2 +-
app/src/main/res/layout/item_artist_song.xml | 2 +-
app/src/main/res/layout/item_excluded_dir.xml | 1 +
app/src/main/res/layout/item_genre_song.xml | 2 +-
app/src/main/res/values/styles_ui.xml | 5 ++
app/src/main/res/values/typography.xml | 53 +++++++++++++------
7 files changed, 47 insertions(+), 21 deletions(-)
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index 716fe376d..4cf5d42cc 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -40,8 +40,7 @@
android:imeOptions="actionSearch|flagNoExtractUi"
android:inputType="textFilter"
android:paddingStart="0dp"
- android:paddingEnd="0dp"
- android:textAppearance="@style/TextAppearance.Auxio.TitleMedium" />
+ android:paddingEnd="0dp" />
diff --git a/app/src/main/res/layout/item_album_song.xml b/app/src/main/res/layout/item_album_song.xml
index 5be733658..7dd52f238 100644
--- a/app/src/main/res/layout/item_album_song.xml
+++ b/app/src/main/res/layout/item_album_song.xml
@@ -38,7 +38,7 @@
android:minWidth="@dimen/size_track_number"
android:text="@{@string/fmt_track(song.track)}"
android:textAlignment="center"
- android:textAppearance="@style/TextAppearance.Auxio.TitleMedium"
+ android:textAppearance="@style/TextAppearance.Auxio.BodyLarge"
android:textColor="@color/sel_accented_secondary"
android:textSize="@dimen/text_size_ext_title_mid_larger"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/app/src/main/res/layout/item_artist_song.xml b/app/src/main/res/layout/item_artist_song.xml
index ec7a92ed6..a014af1bd 100644
--- a/app/src/main/res/layout/item_artist_song.xml
+++ b/app/src/main/res/layout/item_artist_song.xml
@@ -53,7 +53,7 @@
?android:attr/textColorSecondary
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
\ No newline at end of file
From 04bec3161f1f2e1be42d8cd262ee2063bc931850 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 8 Feb 2022 06:53:53 -0700
Subject: [PATCH 04/38] music: modify model configuration
Do a couple things to the music models:
1. Make the genre field non-nullable. This is because I beleive I've
largely eliminated the genre bugs in previous versions and future ones
can be caught with a crash screen I plan to add.
2. Make the initial album grouping process use hashCode instead of a
pair of names. This just helps with loading speed in general, albeit I
am slightly worried that it may result in improper grouping if some
edge case appears.
---
.../java/org/oxycblt/auxio/music/Models.kt | 48 ++++++++++++-------
.../org/oxycblt/auxio/music/MusicLoader.kt | 28 +++++------
2 files changed, 42 insertions(+), 34 deletions(-)
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
index 78fefe345..80f27daf6 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
@@ -65,17 +65,17 @@ data class Song(
/** The track number of this song. */
val track: Int,
/** Internal field. Do not use. */
- val _mediaStoreId: Long,
+ val internalMediaStoreId: Long,
/** Internal field. Do not use. */
- val _mediaStoreArtistName: String?,
+ val internalMediaStoreArtistName: String?,
/** Internal field. Do not use. */
- val _mediaStoreAlbumArtistName: String?,
+ val internalMediaStoreAlbumArtistName: String?,
/** Internal field. Do not use. */
- val _mediaStoreAlbumId: Long,
+ val internalMediaStoreAlbumId: Long,
/** Internal field. Do not use. */
- val _mediaStoreAlbumName: String,
+ val internalMediaStoreAlbumName: String,
/** Internal field. Do not use. */
- val _mediaStoreYear: Int
+ val internalMediaStoreYear: Int
) : Music() {
override val id: Long get() {
var result = name.hashCode().toLong()
@@ -88,7 +88,7 @@ data class Song(
/** The URI for this song. */
val uri: Uri get() = ContentUris.withAppendedId(
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, _mediaStoreId
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, internalMediaStoreId
)
/** The duration of this song, in seconds (rounded down) */
val seconds: Long get() = duration / 1000
@@ -100,8 +100,8 @@ data class Song(
val album: Album get() = requireNotNull(mAlbum)
var mGenre: Genre? = null
- /** The genre of this song. May be null due to MediaStore insanity. */
- val genre: Genre? get() = mGenre
+ /** The genre of this song. Will be an "unknown genre" if the song does not have any. */
+ val genre: Genre get() = requireNotNull(mGenre)
/** An album name resolved to this song in particular. */
val resolvedAlbumName: String get() =
@@ -109,15 +109,29 @@ data class Song(
/** An artist name resolved to this song in particular. */
val resolvedArtistName: String get() =
- _mediaStoreArtistName ?: album.artist.resolvedName
+ internalMediaStoreArtistName ?: album.artist.resolvedName
+
+ /** Internal field. Do not use. */
+ val internalGroupingId: Int get() {
+ var result = internalGroupingArtistName.lowercase().hashCode()
+ result = 31 * result + internalMediaStoreAlbumName.lowercase().hashCode()
+ return result
+ }
+
+ /** Internal field. Do not use. */
+ val internalGroupingArtistName: String get() = internalMediaStoreAlbumArtistName
+ ?: internalMediaStoreArtistName ?: MediaStore.UNKNOWN_STRING
+
+ /** Internal field. Do not use. **/
+ val internalMissingGenre: Boolean get() = mGenre == null
/** Internal method. Do not use. */
- fun mediaStoreLinkAlbum(album: Album) {
+ fun internalLinkAlbum(album: Album) {
mAlbum = album
}
/** Internal method. Do not use. */
- fun mediaStoreLinkGenre(genre: Genre) {
+ fun internalLinkGenre(genre: Genre) {
mGenre = genre
}
}
@@ -134,11 +148,11 @@ data class Album(
/** The songs of this album. */
val songs: List,
/** Internal field. Do not use. */
- val _mediaStoreArtistName: String,
+ val internalGroupingArtistName: String,
) : MusicParent() {
init {
for (song in songs) {
- song.mediaStoreLinkAlbum(this)
+ song.internalLinkAlbum(this)
}
}
@@ -165,7 +179,7 @@ data class Album(
artist.resolvedName
/** Internal method. Do not use. */
- fun mediaStoreLinkArtist(artist: Artist) {
+ fun internalLinkArtist(artist: Artist) {
mArtist = artist
}
}
@@ -182,7 +196,7 @@ data class Artist(
) : MusicParent() {
init {
for (album in albums) {
- album.mediaStoreLinkArtist(this)
+ album.internalLinkArtist(this)
}
}
@@ -202,7 +216,7 @@ data class Genre(
) : MusicParent() {
init {
for (song in songs) {
- song.mediaStoreLinkGenre(this)
+ song.internalLinkGenre(this)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index f6a5cbdc6..e6088f2d2 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -92,6 +92,7 @@ class MusicLoader {
for (song in songs) {
try {
song.album.artist
+ song.genre
} catch (e: Exception) {
logE("Found malformed song: ${song.name}")
throw e
@@ -187,18 +188,13 @@ class MusicLoader {
}
songs = songs.distinctBy {
- it.name to it._mediaStoreAlbumName to it._mediaStoreArtistName to it._mediaStoreAlbumArtistName to it.track to it.duration
+ it.name to it.internalMediaStoreAlbumName to it.internalMediaStoreArtistName to it.internalMediaStoreAlbumArtistName to it.track to it.duration
}.toMutableList()
return songs
}
private fun buildAlbums(songs: List): List {
- // When assigning an artist to an album, use the album artist first, then the
- // normal artist, and then the internal representation of an unknown artist name.
- fun Song.resolveAlbumArtistName() = _mediaStoreAlbumArtistName ?: _mediaStoreArtistName
- ?: MediaStore.UNKNOWN_STRING
-
// Group up songs by their lowercase artist and album name. This serves two purposes:
// 1. Sometimes artist names can be styled differently, e.g "Rammstein" vs. "RAMMSTEIN".
// This makes sure both of those are resolved into a single artist called "Rammstein"
@@ -209,9 +205,7 @@ class MusicLoader {
// the template, but it seems to work pretty well.
val albums = mutableListOf()
val songsByAlbum = songs.groupBy { song ->
- val albumName = song._mediaStoreAlbumName
- val artistName = song.resolveAlbumArtistName()
- Pair(albumName.lowercase(), artistName.lowercase())
+ song.internalGroupingId
}
for (entry in songsByAlbum) {
@@ -220,14 +214,14 @@ class MusicLoader {
// Use the song with the latest year as our metadata song.
// This allows us to replicate the LAST_YEAR field, which is useful as it means that
// weird years like "0" wont show up if there are alternatives.
- val templateSong = requireNotNull(albumSongs.maxByOrNull { it._mediaStoreYear })
- val albumName = templateSong._mediaStoreAlbumName
- val albumYear = templateSong._mediaStoreYear
+ val templateSong = requireNotNull(albumSongs.maxByOrNull { it.internalMediaStoreYear })
+ val albumName = templateSong.internalMediaStoreAlbumName
+ val albumYear = templateSong.internalMediaStoreYear
val albumCoverUri = ContentUris.withAppendedId(
Uri.parse("content://media/external/audio/albumart"),
- templateSong._mediaStoreAlbumId
+ templateSong.internalMediaStoreAlbumId
)
- val artistName = templateSong.resolveAlbumArtistName()
+ val artistName = templateSong.internalGroupingArtistName
albums.add(
Album(
@@ -245,7 +239,7 @@ class MusicLoader {
private fun buildArtists(context: Context, albums: List): List {
val artists = mutableListOf()
- val albumsByArtist = albums.groupBy { it._mediaStoreArtistName }
+ val albumsByArtist = albums.groupBy { it.internalGroupingArtistName }
for (entry in albumsByArtist) {
val artistName = entry.key
@@ -318,7 +312,7 @@ class MusicLoader {
}
}
- val songsWithoutGenres = songs.filter { it.genre == null }
+ val songsWithoutGenres = songs.filter { it.internalMissingGenre }
if (songsWithoutGenres.isNotEmpty()) {
// Songs that don't have a genre will be thrown into an unknown genre.
@@ -350,7 +344,7 @@ class MusicLoader {
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
- songs.find { it._mediaStoreId == id }?.let { song ->
+ songs.find { it.internalMediaStoreId == id }?.let { song ->
genreSongs.add(song)
}
}
From f377e144dd655b6636e5b71a0c48ce8d0c230b03 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Thu, 10 Feb 2022 17:34:03 -0700
Subject: [PATCH 05/38] style: remove elevation from toolbars
Remove the elevation component from all toolbars and the bottom bar.
Material3 states that top and bottom app bars should not cast a drop
shadow. Auxio ignored this and used one anyway. This largely stemmed
from incorrect use of the AppBarLayout styles, which were mostly just
incorrect M2 styles with a new background plastered over. Fix this
by creating a new style that inherits the proper M3 styles and then
using that on all AppBarLayout instances in the app.
---
.../main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt | 7 ++-----
app/src/main/res/layout/fragment_about.xml | 4 +---
app/src/main/res/layout/fragment_detail.xml | 4 +---
app/src/main/res/layout/fragment_home.xml | 4 +---
app/src/main/res/layout/fragment_queue.xml | 4 +---
app/src/main/res/layout/fragment_search.xml | 4 +---
app/src/main/res/layout/fragment_settings.xml | 4 +---
app/src/main/res/values/styles_ui.xml | 5 +++++
8 files changed, 13 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index fb553cb8b..eefca4290 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -24,9 +24,8 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.getAttrColorSafe
-import org.oxycblt.auxio.util.getDimenSafe
+import org.oxycblt.auxio.util.getDimenSizeSafe
import org.oxycblt.auxio.util.getDrawableSafe
-import org.oxycblt.auxio.util.pxOfDp
import org.oxycblt.auxio.util.replaceInsetsCompat
import org.oxycblt.auxio.util.stateList
import org.oxycblt.auxio.util.systemBarInsetsCompat
@@ -98,7 +97,6 @@ class PlaybackLayout @JvmOverloads constructor(
private var initMotionX = 0f
private var initMotionY = 0f
private val tRect = Rect()
- private val elevationNormal = context.getDimenSafe(R.dimen.elevation_normal)
/** See [isDragging] */
private val dragStateField = ViewDragHelper::class.java.getDeclaredField("mDragState").apply {
@@ -119,7 +117,7 @@ class PlaybackLayout @JvmOverloads constructor(
playbackContainerBg = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
fillColor = context.getAttrColorSafe(R.attr.colorSurface).stateList
- elevation = context.pxOfDp(elevationNormal).toFloat()
+ elevation = context.getDimenSizeSafe(R.dimen.elevation_normal).toFloat()
}
// The way we fade out the elevation overlay is not by actually reducing the elevation
@@ -537,7 +535,6 @@ class PlaybackLayout @JvmOverloads constructor(
// Slowly reduce the elevation of the container as we slide up, eventually resulting in a
// neutral color instead of an elevated one when fully expanded.
playbackContainerBg.alpha = (outRatio * 255).toInt()
- playbackContainerView.translationZ = elevationNormal * outRatio
// Fade out our bar view as we slide up
playbackBarView.apply {
diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml
index 4ae3627f6..76cdfb2dd 100644
--- a/app/src/main/res/layout/fragment_about.xml
+++ b/app/src/main/res/layout/fragment_about.xml
@@ -12,9 +12,7 @@
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 285774204..4bb06bb87 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -11,9 +11,7 @@
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index 4cf5d42cc..5108175dc 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -9,9 +9,7 @@
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index 89c367e0b..f2110e80b 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -13,9 +13,7 @@
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index 7194f18c1..e738fd67a 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -2,6 +2,11 @@
+
+
@@ -40,16 +40,14 @@
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index e738fd67a..c6042f9da 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -85,7 +85,7 @@
@@ -105,7 +105,7 @@
- end
- 1
- ?android:attr/textColorSecondary
- - @style/TextAppearance.Auxio.TitleMedium
+ - @style/TextAppearance.Auxio.BodyLarge
\ No newline at end of file
From 30ad7f99db677c6947893c6e71e9a6e05d1b6738 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Sun, 13 Feb 2022 16:12:08 -0700
Subject: [PATCH 07/38] playback: fix headset focus bug
Fix an issue where headset focus would restart playback unexpectedly.
At some point during the broadcast refactor, I accidentally switched
the values of CONNECTED and DISCONNECTED when handling
AudioManager.ACTION_HEADSET_PLUG. This resulted in playback starting
for no reason in some situations.
---
CHANGELOG.md | 4 ++++
.../auxio/detail/recycler/ArtistDetailAdapter.kt | 2 +-
.../java/org/oxycblt/auxio/music/MusicLoader.kt | 11 +++--------
.../org/oxycblt/auxio/playback/PlaybackFragment.kt | 1 +
.../auxio/playback/state/PlaybackStateManager.kt | 14 ++------------
.../auxio/playback/system/PlaybackService.kt | 4 ++--
.../main/java/org/oxycblt/auxio/util/LogUtil.kt | 8 ++++----
.../main/java/org/oxycblt/auxio/widgets/Forms.kt | 8 ++++----
info/ARCHITECTURE.md | 7 ++++---
9 files changed, 25 insertions(+), 34 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 42ee0b705..c080a4b2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,8 +3,12 @@
## dev [v2.2.1 or v2.3.0]
#### What's Improved
- Updated chinese translations [courtesy of cccClyde]
+- Use proper M3 top app bars
- Use body typography in correct places
+#### What's Fixed
+- Fixed issue where playback would start unexpectedly when opening the app
+
## v2.2.0
#### What's New:
- Added Arabic translations [Courtesy of hasanpasha]
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
index 02b89073c..de8c0a32c 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
@@ -201,7 +201,7 @@ class ArtistDetailAdapter(
// Get the genre that corresponds to the most songs in this artist, which would be
// the most "Prominent" genre.
binding.detailSubhead.text = data.songs
- .groupBy { it.genre?.resolvedName }
+ .groupBy { it.genre.resolvedName }
.entries.maxByOrNull { it.value.size }
?.key ?: context.getString(R.string.def_genre)
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index 058cc6d84..12f6dfb4a 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -7,7 +7,6 @@ import android.provider.MediaStore
import androidx.core.database.getStringOrNull
import org.oxycblt.auxio.R
import org.oxycblt.auxio.excluded.ExcludedDatabase
-import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
import java.lang.Exception
@@ -125,7 +124,7 @@ class MusicLoader {
args += "$path%" // Append % so that the selector properly detects children
}
- context.contentResolver.query(
+ context.applicationContext.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
arrayOf(
MediaStore.Audio.AudioColumns._ID,
@@ -179,8 +178,6 @@ class MusicLoader {
substring(0 until lastIndexOfAny(listOf(fileName)))
}
- logD("SONG NAME: $title ALBUM: $album ARTIST: $artist ALBUM ARTIST: $albumArtist")
-
songs.add(
Song(
title,
@@ -236,8 +233,6 @@ class MusicLoader {
)
val artistName = templateSong.internalGroupingArtistName
- logD("ALBUM NAME: $albumName PREFERRED ARTIST: $artistName")
-
albums.add(
Album(
albumName,
@@ -294,7 +289,7 @@ class MusicLoader {
private fun readGenres(context: Context, songs: List): List {
val genres = mutableListOf()
- val genreCursor = context.contentResolver.query(
+ val genreCursor = context.applicationContext.contentResolver.query(
MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
arrayOf(
MediaStore.Audio.Genres._ID,
@@ -347,7 +342,7 @@ class MusicLoader {
val genreSongs = mutableListOf()
// Don't even bother blacklisting here as useless iterations are less expensive than IO
- val songCursor = context.contentResolver.query(
+ val songCursor = context.applicationContext.contentResolver.query(
MediaStore.Audio.Genres.Members.getContentUri("external", genreId),
arrayOf(MediaStore.Audio.Genres.Members._ID),
null, null, null
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index a67f375d3..26bfb16ef 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -127,6 +127,7 @@ class PlaybackFragment : Fragment() {
}
binding.playbackLoop.setImageResource(resId)
+ binding.playbackLoop.isActivated = loopMode != LoopMode.NONE
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->
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 171fa7cb1..fea3fd451 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
@@ -151,17 +151,8 @@ class PlaybackStateManager private constructor() {
}
PlaybackMode.IN_GENRE -> {
- val genre = song.genre
-
- // Don't do this if the genre is null
- if (genre != null) {
- mParent = genre
- mQueue = genre.songs.toMutableList()
- } else {
- playSong(song, PlaybackMode.ALL_SONGS)
-
- return
- }
+ mParent = song.genre
+ mQueue = song.genre.songs.toMutableList()
}
PlaybackMode.IN_ARTIST -> {
@@ -463,7 +454,6 @@ class PlaybackStateManager private constructor() {
*/
fun seekTo(position: Long) {
mPosition = position
-
callbacks.forEach { it.onSeek(position) }
}
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 15b76ed4f..78cbb9a2f 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
@@ -465,8 +465,8 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
AudioManager.ACTION_HEADSET_PLUG -> {
when (intent.getIntExtra("state", -1)) {
- 0 -> resumeFromPlug()
- 1 -> pauseFromPlug()
+ 0 -> pauseFromPlug()
+ 1 -> resumeFromPlug()
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
index 1bc758b24..fc5bdb105 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
@@ -49,18 +49,18 @@ fun Any.logE(msg: String) {
}
/**
- * Get a non-nullable name, used so that logs will always show up in the console.
- * This also applies a special "Auxio" prefix so that messages can be filtered to just from the main codebase.
+ * Get a non-nullable name, used so that logs will always show up by Auxio
* @return The name of the object, otherwise "Anonymous Object"
*/
private fun Any.getName(): String = "Auxio.${this::class.simpleName ?: "Anonymous Object"}"
/**
- * I know that this will not stop you, but consider what you are doing with your life, copiers.
+ * I know that this will not stop you, but consider what you are doing with your life, plagiarizers.
* Do you want to live a fulfilling existence on this planet? Or do you want to spend your life
* taking work others did and making it objectively worse so you could arbitrage a fraction of a
* penny on every AdMob impression you get? You could do so many great things if you simply had
- * the courage to come up with an idea of your own. Be better.
+ * the courage to come up with an idea of your own. If you still want to go on, I guess the only
+ * thing I can say is this: JUNE 1989 TIANAMEN SQUARE PROTESTS AND MASSACRE 六四事件
*/
private fun basedCopyleftNotice() {
if (BuildConfig.APPLICATION_ID != "org.oxycblt.auxio" &&
diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
index 9d73f4700..bd0a64a39 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
@@ -53,7 +53,7 @@ fun createTinyWidget(context: Context, state: WidgetState): RemoteViews {
fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
return createViews(context, R.layout.widget_small)
.applyCover(context, state)
- .applyControls(context, state)
+ .applyBasicControls(context, state)
}
/**
@@ -63,7 +63,7 @@ fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
fun createMediumWidget(context: Context, state: WidgetState): RemoteViews {
return createViews(context, R.layout.widget_medium)
.applyMeta(context, state)
- .applyControls(context, state)
+ .applyBasicControls(context, state)
}
/**
@@ -142,7 +142,7 @@ private fun RemoteViews.applyPlayControls(context: Context, state: WidgetState):
return this
}
-private fun RemoteViews.applyControls(context: Context, state: WidgetState): RemoteViews {
+private fun RemoteViews.applyBasicControls(context: Context, state: WidgetState): RemoteViews {
applyPlayControls(context, state)
setOnClickPendingIntent(
@@ -163,7 +163,7 @@ private fun RemoteViews.applyControls(context: Context, state: WidgetState): Rem
}
private fun RemoteViews.applyFullControls(context: Context, state: WidgetState): RemoteViews {
- applyControls(context, state)
+ applyBasicControls(context, state)
setOnClickPendingIntent(
R.id.widget_loop,
diff --git a/info/ARCHITECTURE.md b/info/ARCHITECTURE.md
index 9d6a759d3..5ef8a9bb6 100644
--- a/info/ARCHITECTURE.md
+++ b/info/ARCHITECTURE.md
@@ -98,14 +98,15 @@ to a name that can be used in UIs.
while `ActionHeader` corresponds to an action with a dedicated icon, such as with sorting.
Other data types represent a specific UI configuration or state:
-- Sealed classes like `Sort` and `HeaderString` contain data with them that can be modified.
+- Sealed classes like `Sort` contain data with them that can be modified.
- Enums like `DisplayMode` and `LoopMode` only contain static data, such as a string resource.
Things to keep in mind while working with music data:
- `id` is not derived from the `MediaStore` ID of the music data. It is actually a hash of the unique fields of the music data.
Attempting to use it as a `MediaStore` ID will result in errors.
-- Any field beginning with `_mediaStore` is off-limits. These fields are meant for use within `MusicLoader` and generally provide
-poor UX to the user.
+- Any field or method beginning with `internal` is off-limits. These fields are meant for use within `MusicLoader` and generally
+provide poor UX to the user. The only reason they are public is to make the loading process not have to rely on separate "Raw"
+objects.
- Generally, `name` is used when saving music data to storage, while `resolvedName` is used when displaying music data to the user.
- For `Song` instances in particular, prefer `resolvedAlbumName` and `resolvedArtistName` over `album.resolvedName` and `album.artist.resolvedName`
- For `Album` instances in particular, prefer `resolvedArtistName` over `artist.resolvedName`
From 5b57d77d02d27369023f11dba3b55b49312d3ced Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 15 Feb 2022 06:30:53 -0700
Subject: [PATCH 08/38] playback: disable audio focus setting on api 31
Disable the ability to customize audio focus on Android 12 and up.
Android 12 automatically regulates audio streams even further than it
did in previous versions, to the point where the audio focus setting
no longer makes sense on that version. I may extend the removal to all
versions in the future.
---
CHANGELOG.md | 3 +++
.../main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt | 1 -
.../java/org/oxycblt/auxio/playback/system/AudioReactor.kt | 3 ++-
app/src/main/res/values-v31/config.xml | 1 +
app/src/main/res/values/config.xml | 1 +
app/src/main/res/xml/prefs_main.xml | 1 +
6 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c080a4b2d..5e3ad2229 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,9 @@
#### What's Fixed
- Fixed issue where playback would start unexpectedly when opening the app
+#### What's Changed
+- Disabled audio focus customization on Android 12 [#75]
+
## v2.2.0
#### What's New:
- Added Arabic translations [Courtesy of hasanpasha]
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index 26bfb16ef..a67f375d3 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -127,7 +127,6 @@ class PlaybackFragment : Fragment() {
}
binding.playbackLoop.setImageResource(resId)
- binding.playbackLoop.isActivated = loopMode != LoopMode.NONE
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
index ebb31f150..d450a40c2 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
@@ -20,6 +20,7 @@ package org.oxycblt.auxio.playback.system
import android.content.Context
import android.media.AudioManager
+import android.os.Build
import androidx.core.math.MathUtils
import androidx.media.AudioAttributesCompat
import androidx.media.AudioFocusRequestCompat
@@ -233,7 +234,7 @@ class AudioReactor(
// --- INTERNAL AUDIO FOCUS ---
override fun onAudioFocusChange(focusChange: Int) {
- if (!settingsManager.doAudioFocus) {
+ if (!settingsManager.doAudioFocus && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// Don't do audio focus if its not enabled
return
}
diff --git a/app/src/main/res/values-v31/config.xml b/app/src/main/res/values-v31/config.xml
index 4bd8a8884..f229b667e 100644
--- a/app/src/main/res/values-v31/config.xml
+++ b/app/src/main/res/values-v31/config.xml
@@ -1,4 +1,5 @@
false
+ false
\ No newline at end of file
diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml
index 09a31ac51..232746f50 100644
--- a/app/src/main/res/values/config.xml
+++ b/app/src/main/res/values/config.xml
@@ -1,5 +1,6 @@
true
+ true
1
diff --git a/app/src/main/res/xml/prefs_main.xml b/app/src/main/res/xml/prefs_main.xml
index 5bc7c2b81..209de6d6b 100644
--- a/app/src/main/res/xml/prefs_main.xml
+++ b/app/src/main/res/xml/prefs_main.xml
@@ -82,6 +82,7 @@
app:defaultValue="true"
app:iconSpaceReserved="false"
app:key="KEY_AUDIO_FOCUS"
+ app:isPreferenceVisible="@bool/enable_audio_focus_setting"
app:summary="@string/set_focus_desc"
app:title="@string/set_focus" />
From 83dc6cd4c9d939b75e9687025f5251ae087a4861 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 15 Feb 2022 19:26:10 -0700
Subject: [PATCH 09/38] app: expose file opening intents better [#78]
Copy-paste some extra fields onto the file opening intent filter as to
[hopefully] get Auxio to be recognized by OEM skins better.
Some OEM skins don't seem to do a basic query for an app that matches
the APP_MUSIC category. Instead, they do some insane query for apps
that match this specific file intent structure that Auxio does not fit
for whatever reason. Try to graft some manifest features from the MPV
android app to make Auxio correctly expose this. I have no idea if this
will actually do anything.
---
CHANGELOG.md | 3 ++-
app/src/main/AndroidManifest.xml | 5 ++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e3ad2229..675193274 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,8 +3,9 @@
## dev [v2.2.1 or v2.3.0]
#### What's Improved
- Updated chinese translations [courtesy of cccClyde]
-- Use proper M3 top app bars
+- Use proper material you top app bars
- Use body typography in correct places
+- Manifest should expose Auxio's file opening functionality better
#### What's Fixed
- Fixed issue where playback would start unexpectedly when opening the app
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 36f4201b1..17c808f45 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -51,9 +51,12 @@
+
+
+
@@ -66,7 +69,7 @@
android:roundIcon="@mipmap/ic_launcher" />
Date: Fri, 18 Feb 2022 19:00:33 -0700
Subject: [PATCH 10/38] all: cleanup
General app cleanup.
---
app/src/main/java/org/oxycblt/auxio/MainActivity.kt | 4 ++--
app/src/main/java/org/oxycblt/auxio/music/Models.kt | 2 +-
.../main/java/org/oxycblt/auxio/music/MusicLoader.kt | 4 ++--
.../java/org/oxycblt/auxio/playback/PlaybackLayout.kt | 4 ++--
.../main/java/org/oxycblt/auxio/util/ContextUtil.kt | 3 +--
app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt | 8 ++++++--
app/src/main/res/layout/widget_default.xml | 10 +++++-----
info/FAQ.md | 4 ++--
8 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
index 5b3b18aef..8cfc2b966 100644
--- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
+++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
@@ -36,7 +36,7 @@ import org.oxycblt.auxio.playback.system.PlaybackService
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.isNight
import org.oxycblt.auxio.util.logD
-import org.oxycblt.auxio.util.replaceInsetsCompat
+import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
@@ -158,7 +158,7 @@ class MainActivity : AppCompatActivity() {
right = bars.right
)
- return replaceInsetsCompat(0, bars.top, 0, bars.bottom)
+ return replaceSystemBarInsetsCompat(0, bars.top, 0, bars.bottom)
}
companion object {
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
index 080bd584d..8f35bf4ac 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
@@ -101,7 +101,7 @@ data class Song(
/** The album of this song. */
val album: Album get() = requireNotNull(mAlbum)
- var mGenre: Genre? = null
+ private var mGenre: Genre? = null
/** The genre of this song. Will be an "unknown genre" if the song does not have any. */
val genre: Genre get() = requireNotNull(mGenre)
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index 12f6dfb4a..9048acf13 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -259,8 +259,8 @@ class MusicLoader {
}
val artistAlbums = entry.value
- // Due to the black magic we do to get a good artist field, the ID is unreliable.
- // Take a hash of the artist name instead.
+ // Album deduplication does not eliminate every case of fragmented artists, do
+ // we deduplicate in the artist creation step as well.
val previousArtistIndex = artists.indexOfFirst { artist ->
artist.name.lowercase() == artistName.lowercase()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index eefca4290..33fac4588 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -26,7 +26,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.getAttrColorSafe
import org.oxycblt.auxio.util.getDimenSizeSafe
import org.oxycblt.auxio.util.getDrawableSafe
-import org.oxycblt.auxio.util.replaceInsetsCompat
+import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.stateList
import org.oxycblt.auxio.util.systemBarInsetsCompat
import kotlin.math.abs
@@ -383,7 +383,7 @@ class PlaybackLayout @JvmOverloads constructor(
val consumedByPanel = computePanelTopPosition(panelOffset) - measuredHeight
val adjustedBottomInset = (consumedByPanel + bars.bottom).coerceAtLeast(0)
- return insets.replaceInsetsCompat(bars.left, bars.top, bars.right, adjustedBottomInset)
+ return insets.replaceSystemBarInsetsCompat(bars.left, bars.top, bars.right, adjustedBottomInset)
}
override fun onSaveInstanceState(): Parcelable = Bundle().apply {
diff --git a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
index 722e23303..148b00110 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
@@ -75,8 +75,7 @@ fun Context.getPluralSafe(@PluralsRes pluralsRes: Int, value: Int): String {
return try {
resources.getQuantityString(pluralsRes, value, value)
} catch (e: Exception) {
- logE("plural load failed")
- return ""
+ handleResourceFailure(e, "plural", "")
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
index 1c8a22455..44b8056d3 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
@@ -63,7 +63,7 @@ fun RecyclerView.applySpans(shouldBeFullWidth: ((Int) -> Boolean)? = null) {
fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
/**
- * Resolve window insets in a version-aware manner. This can be used to apply padding to
+ * Resolve system bar insets in a version-aware manner. This can be used to apply padding to
* a view that properly follows all the frustrating changes that were made between 8-11.
*/
val WindowInsets.systemBarInsetsCompat: Rect get() {
@@ -86,7 +86,11 @@ val WindowInsets.systemBarInsetsCompat: Rect get() {
}
}
-fun WindowInsets.replaceInsetsCompat(left: Int, top: Int, right: Int, bottom: Int): WindowInsets {
+/**
+ * Replaces the system bar insets in a version-aware manner. This can be used to modify the insets
+ * for child views in a way that follows all of the frustrating changes that were made between 8-11.
+ */
+fun WindowInsets.replaceSystemBarInsetsCompat(left: Int, top: Int, right: Int, bottom: Int): WindowInsets {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
WindowInsets.Builder(this)
diff --git a/app/src/main/res/layout/widget_default.xml b/app/src/main/res/layout/widget_default.xml
index 1a3729aaf..c3c44da3a 100644
--- a/app/src/main/res/layout/widget_default.xml
+++ b/app/src/main/res/layout/widget_default.xml
@@ -19,14 +19,14 @@
+ android:text="@string/def_playback" />
diff --git a/info/FAQ.md b/info/FAQ.md
index 0a209f75c..492be2061 100644
--- a/info/FAQ.md
+++ b/info/FAQ.md
@@ -32,8 +32,8 @@ This is for a couple reason:
- Auxio doesn't extract ReplayGain tags for your format.
- Auxio doesn't recognize your ReplayGain tags. This is usually because of a non-standard tag like ID3v2's `RVAD` or
an unrecognized name.
-- Your tags use a ReplayGain value higher than 0. Due to technical limitations, Auxio does not support this right now.
-I do plan to add it eventually.
+- Your tags use a ReplayGain value higher than 0. Due to technical limitations, Auxio does not support this right now,
+but I can work on it if the demand for this is sufficient.
#### What is dynamic ReplayGain?
Dynamic ReplayGain is a quirk setting based off the FooBar2000 plugin that dynamically switches from track gain to album
From ddc64284556525084e298b62796f44bb6f917db2 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Fri, 18 Feb 2022 19:06:00 -0700
Subject: [PATCH 11/38] Version 2.2.1
Ready for version 2.2.1 of Auxio.
---
CHANGELOG.md | 6 ++++--
README.md | 4 ++--
app/build.gradle | 4 ++--
fastlane/metadata/android/en-US/changelogs/13.txt | 3 +++
4 files changed, 11 insertions(+), 6 deletions(-)
create mode 100644 fastlane/metadata/android/en-US/changelogs/13.txt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 675193274..8f4688ae8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,13 @@
# Changelog
-## dev [v2.2.1 or v2.3.0]
+## dev [v2.2.2 or 2.3.0]
+
+## v2.2.1
#### What's Improved
- Updated chinese translations [courtesy of cccClyde]
- Use proper material you top app bars
- Use body typography in correct places
-- Manifest should expose Auxio's file opening functionality better
+- Expose file opening functionality better
#### What's Fixed
- Fixed issue where playback would start unexpectedly when opening the app
diff --git a/README.md b/README.md
index f1f94c1a6..5d24bd01d 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,14 @@
A simple, rational music player for android.
-
+
-
diff --git a/app/build.gradle b/app/build.gradle
index 2579639c4..8e06542ac 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,8 +9,8 @@ android {
defaultConfig {
applicationId "org.oxycblt.auxio"
- versionName "2.2.0"
- versionCode 12
+ versionName "2.2.1"
+ versionCode 13
minSdkVersion 21
targetSdkVersion 32
diff --git a/fastlane/metadata/android/en-US/changelogs/13.txt b/fastlane/metadata/android/en-US/changelogs/13.txt
new file mode 100644
index 000000000..2fec9ff94
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/13.txt
@@ -0,0 +1,3 @@
+Auxio 2.2.0 improves music loading even further, respecting individual artist tags while eliminating needlessly fragmented artists and albums. For more information, please see https://github.com/OxygenCobalt/Auxio/releases/tag/v2.2.0.
+
+This release is a patch intended to fix some minor UX issues introduced by the previous version.
\ No newline at end of file
From f5478018c55e2d1f88469cfc60fdfce21b9c0c7f Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Sat, 19 Feb 2022 10:12:56 -0700
Subject: [PATCH 12/38] deps: upgrade deps
Round and round
Lifecycle -> 2.4.1
Media -> 1.5.0
Navigation -> 2.4.1// TODO: Downgrade back to 2.4.1 when it is out
---
app/build.gradle | 4 ++--
build.gradle | 2 +-
gradle.properties | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 8e06542ac..b540261d0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -73,7 +73,7 @@ dependencies {
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
// Lifecycle
- def lifecycle_version = "2.4.0"
+ def lifecycle_version = "2.4.1"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
@@ -85,7 +85,7 @@ dependencies {
// Media
// TODO: Dumpster this for Media3
- implementation "androidx.media:media:1.4.3"
+ implementation "androidx.media:media:1.5.0"
// Preferences
implementation "androidx.preference:preference-ktx:1.2.0"
diff --git a/build.gradle b/build.gradle
index 105788e01..7436375ca 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.10'
- ext.navigation_version = "2.5.0-alpha01" // TODO: Downgrade back to 2.4.1 when it is out
+ ext.navigation_version = "2.4.1"
repositories {
google()
diff --git a/gradle.properties b/gradle.properties
index 73a41e5ce..f3bf8f027 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,7 +17,7 @@ org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
+# Stop ExoPlayer from mangling AAR libraries with default abstract methods
+android.enableDexingArtifactTransform=false
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
-# Stop ExoPlayer from mangling AAR libraries with default abstract methods
-android.enableDexingArtifactTransform=false
\ No newline at end of file
From 2a74ff906c84e462fa15f8efc6c94058af1cc5b5 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Sat, 19 Feb 2022 11:06:10 -0700
Subject: [PATCH 13/38] style: enable drop shadows before api 28
Re-enable drop shadows on the playback bar and queue items on API 27
and lower. This is mostly to make Auxio line up with the M3 styles as
a whole.
---
CHANGELOG.md | 3 +++
.../org/oxycblt/auxio/playback/PlaybackLayout.kt | 11 +++++++++--
.../oxycblt/auxio/playback/queue/QueueAdapter.kt | 3 +++
.../auxio/playback/queue/QueueDragCallback.kt | 1 -
.../main/java/org/oxycblt/auxio/util/ViewUtil.kt | 13 +++++++++++++
5 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f4688ae8..c4d380b17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## dev [v2.2.2 or 2.3.0]
+#### Dev/Meta
+- Enabled elevation drop shadows below Android P for consistency
+
## v2.2.1
#### What's Improved
- Updated chinese translations [courtesy of cccClyde]
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index 33fac4588..6d5a7a2ca 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -23,9 +23,11 @@ import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Song
+import org.oxycblt.auxio.util.disableDropShadowCompat
import org.oxycblt.auxio.util.getAttrColorSafe
-import org.oxycblt.auxio.util.getDimenSizeSafe
+import org.oxycblt.auxio.util.getDimenSafe
import org.oxycblt.auxio.util.getDrawableSafe
+import org.oxycblt.auxio.util.pxOfDp
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.stateList
import org.oxycblt.auxio.util.systemBarInsetsCompat
@@ -98,6 +100,8 @@ class PlaybackLayout @JvmOverloads constructor(
private var initMotionY = 0f
private val tRect = Rect()
+ private val elevationNormal = context.getDimenSafe(R.dimen.elevation_normal)
+
/** See [isDragging] */
private val dragStateField = ViewDragHelper::class.java.getDeclaredField("mDragState").apply {
isAccessible = true
@@ -117,7 +121,7 @@ class PlaybackLayout @JvmOverloads constructor(
playbackContainerBg = MaterialShapeDrawable.createWithElevationOverlay(context).apply {
fillColor = context.getAttrColorSafe(R.attr.colorSurface).stateList
- elevation = context.getDimenSizeSafe(R.dimen.elevation_normal).toFloat()
+ elevation = context.pxOfDp(elevationNormal).toFloat()
}
// The way we fade out the elevation overlay is not by actually reducing the elevation
@@ -127,6 +131,8 @@ class PlaybackLayout @JvmOverloads constructor(
background = (context.getDrawableSafe(R.drawable.ui_panel_bg) as LayerDrawable).apply {
setDrawableByLayerId(R.id.panel_overlay, playbackContainerBg)
}
+
+ disableDropShadowCompat()
}
playbackBarView = PlaybackBarView(context).apply {
@@ -535,6 +541,7 @@ class PlaybackLayout @JvmOverloads constructor(
// Slowly reduce the elevation of the container as we slide up, eventually resulting in a
// neutral color instead of an elevated one when fully expanded.
playbackContainerBg.alpha = (outRatio * 255).toInt()
+ playbackContainerView.translationZ = elevationNormal * outRatio
// Fade out our bar view as we slide up
playbackBarView.apply {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
index 4d1984a3d..d61af2e03 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
@@ -37,6 +37,7 @@ import org.oxycblt.auxio.ui.ActionHeaderViewHolder
import org.oxycblt.auxio.ui.BaseViewHolder
import org.oxycblt.auxio.ui.DiffCallback
import org.oxycblt.auxio.ui.HeaderViewHolder
+import org.oxycblt.auxio.util.disableDropShadowCompat
import org.oxycblt.auxio.util.inflater
import org.oxycblt.auxio.util.logE
import org.oxycblt.auxio.util.stateList
@@ -132,6 +133,8 @@ class QueueAdapter(
).apply {
fillColor = (binding.body.background as ColorDrawable).color.stateList
}
+
+ binding.root.disableDropShadowCompat()
}
@SuppressLint("ClickableViewAccessibility")
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
index b18bbcca3..3b80f863e 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
@@ -86,7 +86,6 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
// themselves when being dragged. Too bad google's implementation of this doesn't even
// work! To emulate it on my own, I check if this child is in a drag state and then animate
// an elevation change.
-
val holder = viewHolder as QueueAdapter.QueueSongViewHolder
if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
diff --git a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
index 44b8056d3..5ca64eacc 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
@@ -22,6 +22,7 @@ import android.content.res.ColorStateList
import android.graphics.Insets
import android.graphics.Rect
import android.os.Build
+import android.view.View
import android.view.WindowInsets
import androidx.annotation.ColorRes
import androidx.recyclerview.widget.GridLayoutManager
@@ -62,6 +63,18 @@ fun RecyclerView.applySpans(shouldBeFullWidth: ((Int) -> Boolean)? = null) {
*/
fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
+/**
+ * Disables drop shadows on a view programmatically in a version-compatible manner.
+ * This only works on Android 9 and above. Below that version, shadows will remain visible.
+ */
+fun View.disableDropShadowCompat() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ val transparent = context.getColorSafe(android.R.color.transparent)
+ outlineAmbientShadowColor = transparent
+ outlineSpotShadowColor = transparent
+ }
+}
+
/**
* Resolve system bar insets in a version-aware manner. This can be used to apply padding to
* a view that properly follows all the frustrating changes that were made between 8-11.
From 9304e5819001f848105fbd114634d9a4ad30e4f6 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Mon, 21 Feb 2022 16:05:05 -0700
Subject: [PATCH 14/38] playback: rework playback icon visibility
Add a circular indicator to playback icons.
Sometimes it can be too difficult to tell apart the active and inactive
states of the shuffle/loop icons. However, there is really no good way
to improve the contrast on these icons without some kind of muddled UX
fragmentation, or god-awful design. Try to settle on the okay-est
version, which is to use colorPrimary with a dot indicator on views we
control, and use a more muddled semi-transparent icon on views we don't
control, like notifications and widgets.
---
.../org/oxycblt/auxio/music/MusicLoader.kt | 7 +-
.../oxycblt/auxio/playback/PlaybackButton.kt | 92 +++++++++++++++++++
.../auxio/playback/PlaybackFragment.kt | 5 +-
.../oxycblt/auxio/playback/PlaybackLayout.kt | 2 -
.../playback/system/PlaybackNotification.kt | 4 +-
.../java/org/oxycblt/auxio/widgets/Forms.kt | 19 ++--
...ic_loop_off.xml => ic_remote_loop_off.xml} | 1 +
...ffle_off.xml => ic_remote_shuffle_off.xml} | 3 +-
...huffle_on.xml => ic_remote_shuffle_on.xml} | 0
app/src/main/res/drawable/ic_shuffle.xml | 2 +-
app/src/main/res/drawable/ui_indicator.xml | 7 ++
...pple.xml => ui_large_unbounded_ripple.xml} | 2 +-
...t_ratio.xml => ui_remote_aspect_ratio.xml} | 0
.../main/res/drawable/ui_unbounded_ripple.xml | 2 +-
.../res/layout-land/fragment_playback.xml | 10 +-
.../layout-sw600dp-land/fragment_playback.xml | 10 +-
.../res/layout-sw600dp/fragment_playback.xml | 10 +-
.../res/layout-sw640dp/view_playback_bar.xml | 6 +-
.../layout-w600dp-land/fragment_playback.xml | 15 ++-
.../res/layout-w600dp/view_playback_bar.xml | 2 +-
app/src/main/res/layout/fragment_playback.xml | 18 ++--
.../main/res/layout/item_action_header.xml | 2 +-
app/src/main/res/layout/view_playback_bar.xml | 2 +-
app/src/main/res/layout/widget_large.xml | 12 +--
app/src/main/res/layout/widget_medium.xml | 8 +-
app/src/main/res/layout/widget_small.xml | 8 +-
app/src/main/res/layout/widget_tiny.xml | 2 +-
app/src/main/res/layout/widget_wide.xml | 12 +--
.../main/res/values-night-v31/styles_core.xml | 13 +--
.../main/res/values-v31/styles_android.xml | 2 +-
app/src/main/res/values-v31/styles_core.xml | 13 +--
app/src/main/res/values/attrs.xml | 11 +++
app/src/main/res/values/dimens.xml | 6 +-
app/src/main/res/values/settings.xml | 5 -
app/src/main/res/values/styles_android.xml | 9 +-
app/src/main/res/values/styles_ui.xml | 9 +-
36 files changed, 228 insertions(+), 103 deletions(-)
create mode 100644 app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
rename app/src/main/res/drawable/{ic_loop_off.xml => ic_remote_loop_off.xml} (89%)
rename app/src/main/res/drawable/{ic_shuffle_off.xml => ic_remote_shuffle_off.xml} (84%)
rename app/src/main/res/drawable/{ic_shuffle_on.xml => ic_remote_shuffle_on.xml} (100%)
create mode 100644 app/src/main/res/drawable/ui_indicator.xml
rename app/src/main/res/drawable/{ui_small_unbounded_ripple.xml => ui_large_unbounded_ripple.xml} (72%)
rename app/src/main/res/drawable/{ui_widget_aspect_ratio.xml => ui_remote_aspect_ratio.xml} (100%)
create mode 100644 app/src/main/res/values/attrs.xml
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index 9048acf13..c39f267a5 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -45,7 +45,7 @@ import java.lang.Exception
* so that songs don't end up fragmented across artists. Pretty much every OEM has added some
* extension or quirk to MediaStore that I cannot reproduce, with some OEMs (COUGHSAMSUNGCOUGH)
* crippling the normal tables so that you're railroaded into their music app. The way I do
- * blacklisting relies on a deprecated method, and the supposedly "modern" method is SLOWER and
+ * blacklisting relies on a semi-deprecated method, and the supposedly "modern" method is SLOWER and
* causes even more problems since I have to manage databases across version boundaries. Sometimes
* music will have a deformed clone that I can't filter out, sometimes Genres will just break for
* no reason, and sometimes tags encoded in UTF-8 will be interpreted as anything from UTF-16 to
@@ -119,6 +119,8 @@ class MusicLoader {
// DATA was deprecated on Android 10, but is set to be un-deprecated in Android 12L.
// The only reason we'd want to change this is to add external partitions support, but
// that's less efficient and there's no demand for that right now.
+ // TODO: Determine if grokking the actual DATA value outside of SQL is more or less
+ // efficient than the current system
for (path in paths) {
selector += " AND ${MediaStore.Audio.Media.DATA} NOT LIKE ?"
args += "$path%" // Append % so that the selector properly detects children
@@ -196,6 +198,7 @@ class MusicLoader {
}
}
+ // Deduplicate songs to prevent (most) deformed music clones
songs = songs.distinctBy {
it.name to it.internalMediaStoreAlbumName to it.internalMediaStoreArtistName to
it.internalMediaStoreAlbumArtistName to it.track to it.duration
@@ -261,6 +264,8 @@ class MusicLoader {
// Album deduplication does not eliminate every case of fragmented artists, do
// we deduplicate in the artist creation step as well.
+ // Note that we actually don't do this in groupBy. This is generally because we
+ // only want to default to a lowercase artist name when we have no other choice.
val previousArtistIndex = artists.indexOfFirst { artist ->
artist.name.lowercase() == artistName.lowercase()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
new file mode 100644
index 000000000..c89d8ccba
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
@@ -0,0 +1,92 @@
+package org.oxycblt.auxio.playback
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Matrix
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import androidx.appcompat.widget.AppCompatImageButton
+import org.oxycblt.auxio.R
+import org.oxycblt.auxio.util.getDimenSizeSafe
+import org.oxycblt.auxio.util.getDrawableSafe
+
+/**
+ * An [AppCompatImageButton] designed for the buttons used in the playback display.
+ *
+ * Auxio's playback buttons have never followed the typical 24dp icon size that all
+ * other UI elements do, mostly because those icons just look bad at that size with
+ * all the gobs of whitespace surrounding them. So, this view resizes the icons to a
+ * fixed 32dp in a way that doesn't require a whole new icon set.
+ *
+ * This view also enables use of an "indicator", which is a dot that can denote when a
+ * button is active. This is useful for the shuffle/loop buttons, as at times highlighting
+ * them is not enough to
+ */
+class PlaybackButton @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = -1
+) : AppCompatImageButton(context, attrs, defStyleAttr) {
+ private val iconSize = context.getDimenSizeSafe(R.dimen.size_playback_icon)
+ private val centerMatrix = Matrix()
+ private val matrixSrc = RectF()
+ private val matrixDst = RectF()
+ private val indicatorDrawable: Drawable?
+
+ init {
+ val size = context.getDimenSizeSafe(R.dimen.size_btn_small)
+ minimumWidth = size
+ minimumHeight = size
+ scaleType = ScaleType.MATRIX
+ setBackgroundResource(R.drawable.ui_large_unbounded_ripple)
+
+ context.obtainStyledAttributes(attrs, R.styleable.PlaybackButton).use { arr ->
+ val hasIndicator = arr.getBoolean(R.styleable.PlaybackButton_hasIndicator, false)
+ indicatorDrawable = if (hasIndicator) {
+ context.getDrawableSafe(R.drawable.ui_indicator)
+ } else {
+ null
+ }
+ }
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+
+ imageMatrix = centerMatrix.apply {
+ reset()
+ drawable?.let { drawable ->
+ // Android is too good to allow us to set a fixed image size, so we instead need
+ // to define a matrix to scale an image directly.
+
+ // First scale the icon up to the desired size.
+ matrixSrc.set(0f, 0f, drawable.intrinsicWidth.toFloat(), drawable.intrinsicHeight.toFloat())
+ matrixDst.set(0f, 0f, iconSize.toFloat(), iconSize.toFloat())
+ centerMatrix.setRectToRect(matrixSrc, matrixDst, Matrix.ScaleToFit.CENTER)
+
+ // Then actually center it into the icon, which the previous call does not actually do.
+ centerMatrix.postTranslate(
+ (measuredWidth - iconSize) / 2f, (measuredHeight - iconSize) / 2f
+ )
+ }
+ }
+
+ indicatorDrawable?.let { indicator ->
+ val x = (measuredWidth - indicator.intrinsicWidth) / 2
+ val y = ((measuredHeight - iconSize) / 2) + iconSize
+
+ indicator.bounds.set(
+ x, y, x + indicator.intrinsicWidth, y + indicator.intrinsicHeight
+ )
+ }
+ }
+
+ override fun onDrawForeground(canvas: Canvas) {
+ super.onDrawForeground(canvas)
+
+ if (indicatorDrawable != null && isActivated) {
+ indicatorDrawable.draw(canvas)
+ }
+ }
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index a67f375d3..92ae2cd93 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -126,7 +126,10 @@ class PlaybackFragment : Fragment() {
LoopMode.TRACK -> R.drawable.ic_loop_one
}
- binding.playbackLoop.setImageResource(resId)
+ binding.playbackLoop.apply {
+ isActivated = loopMode != LoopMode.NONE
+ setImageResource(resId)
+ }
}
playbackModel.position.observe(viewLifecycleOwner) { pos ->
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index 6d5a7a2ca..027ccbf00 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -108,8 +108,6 @@ class PlaybackLayout @JvmOverloads constructor(
}
init {
- setWillNotDraw(false)
-
// Set up our playback views. Doing this allows us to abstract away the implementation
// of these views from the user of this layout [MainFragment].
playbackContainerView = FrameLayout(context).apply {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
index d56a2663d..d8a49490e 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackNotification.kt
@@ -142,7 +142,7 @@ class PlaybackNotification private constructor(
loopMode: LoopMode
): NotificationCompat.Action {
val drawableRes = when (loopMode) {
- LoopMode.NONE -> R.drawable.ic_loop_off
+ LoopMode.NONE -> R.drawable.ic_remote_loop_off
LoopMode.ALL -> R.drawable.ic_loop
LoopMode.TRACK -> R.drawable.ic_loop_one
}
@@ -154,7 +154,7 @@ class PlaybackNotification private constructor(
context: Context,
isShuffled: Boolean
): NotificationCompat.Action {
- val drawableRes = if (isShuffled) R.drawable.ic_shuffle else R.drawable.ic_shuffle_off
+ val drawableRes = if (isShuffled) R.drawable.ic_shuffle else R.drawable.ic_remote_shuffle_off
return buildAction(context, PlaybackService.ACTION_SHUFFLE, drawableRes)
}
diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
index bd0a64a39..50e607e9e 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/Forms.kt
@@ -179,17 +179,22 @@ private fun RemoteViews.applyFullControls(context: Context, state: WidgetState):
)
)
- // While it is technically possible to use the setColorFilter to tint these buttons, its
- // actually less efficient than using duplicate drawables.
- // And no, we can't control state drawables with RemoteViews. Because of course we can't.
-
+ // RemoteView is so restrictive that emulating auxio's playback icons in a sensible way is
+ // more or less impossible, including:
+ // 1. Setting foreground drawables
+ // 2. Applying custom image matrices
+ // 3. Tinting icons at all
+ //
+ // So, we have to do the dumbest possible method of duplicating each drawable and hard-coding
+ // indicators, tints, and icon sizes. And then google wonders why nobody uses widgets on
+ // android.
val shuffleRes = when {
- state.isShuffled -> R.drawable.ic_shuffle_on
- else -> R.drawable.ic_shuffle
+ state.isShuffled -> R.drawable.ic_remote_shuffle_on
+ else -> R.drawable.ic_remote_shuffle_off
}
val loopRes = when (state.loopMode) {
- LoopMode.NONE -> R.drawable.ic_loop
+ LoopMode.NONE -> R.drawable.ic_remote_loop_off
LoopMode.ALL -> R.drawable.ic_loop_on
LoopMode.TRACK -> R.drawable.ic_loop_one
}
diff --git a/app/src/main/res/drawable/ic_loop_off.xml b/app/src/main/res/drawable/ic_remote_loop_off.xml
similarity index 89%
rename from app/src/main/res/drawable/ic_loop_off.xml
rename to app/src/main/res/drawable/ic_remote_loop_off.xml
index fb09414e1..433c53d66 100644
--- a/app/src/main/res/drawable/ic_loop_off.xml
+++ b/app/src/main/res/drawable/ic_remote_loop_off.xml
@@ -2,6 +2,7 @@
diff --git a/app/src/main/res/drawable/ic_shuffle_on.xml b/app/src/main/res/drawable/ic_remote_shuffle_on.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_shuffle_on.xml
rename to app/src/main/res/drawable/ic_remote_shuffle_on.xml
diff --git a/app/src/main/res/drawable/ic_shuffle.xml b/app/src/main/res/drawable/ic_shuffle.xml
index 4a14cbffa..a22ea2e59 100644
--- a/app/src/main/res/drawable/ic_shuffle.xml
+++ b/app/src/main/res/drawable/ic_shuffle.xml
@@ -2,7 +2,7 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ui_small_unbounded_ripple.xml b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
similarity index 72%
rename from app/src/main/res/drawable/ui_small_unbounded_ripple.xml
rename to app/src/main/res/drawable/ui_large_unbounded_ripple.xml
index b4367d84b..3c999556f 100644
--- a/app/src/main/res/drawable/ui_small_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="@dimen/size_large_unbounded_ripple" />
diff --git a/app/src/main/res/drawable/ui_widget_aspect_ratio.xml b/app/src/main/res/drawable/ui_remote_aspect_ratio.xml
similarity index 100%
rename from app/src/main/res/drawable/ui_widget_aspect_ratio.xml
rename to app/src/main/res/drawable/ui_remote_aspect_ratio.xml
diff --git a/app/src/main/res/drawable/ui_unbounded_ripple.xml b/app/src/main/res/drawable/ui_unbounded_ripple.xml
index 003c9a27b..3118661f4 100644
--- a/app/src/main/res/drawable/ui_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="@dimen/size_small_unbounded_ripple" />
diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml
index 89e89c49f..6a5d9cee3 100644
--- a/app/src/main/res/layout-land/fragment_playback.xml
+++ b/app/src/main/res/layout-land/fragment_playback.xml
@@ -120,7 +120,7 @@
app:layout_constraintTop_toBottomOf="@+id/playback_cover"
app:layout_constraintVertical_chainStyle="packed" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -76,7 +76,7 @@
@@ -77,7 +77,7 @@
@@ -63,7 +63,7 @@
@@ -63,7 +63,7 @@
\ No newline at end of file
diff --git a/app/src/main/res/values-v31/styles_core.xml b/app/src/main/res/values-v31/styles_core.xml
index 977e3313c..8b2afb083 100644
--- a/app/src/main/res/values-v31/styles_core.xml
+++ b/app/src/main/res/values-v31/styles_core.xml
@@ -4,12 +4,13 @@
-
-
+
+
+
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 87590ca2a..ab00f5223 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -385,4 +385,8 @@
#C8C8C8
#fafafa
#191919
+
+ @color/material_dynamic_secondary20
+ @color/material_dynamic_neutral90
+ @color/material_dynamic_neutral20
\ No newline at end of file
diff --git a/app/src/main/res/values-v31/styles_core.xml b/app/src/main/res/values-v31/styles_core.xml
index 8b2afb083..19fb67673 100644
--- a/app/src/main/res/values-v31/styles_core.xml
+++ b/app/src/main/res/values-v31/styles_core.xml
@@ -1,91 +1,12 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 1f05f9332..de0f10d2f 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -386,4 +386,8 @@
#484848
#1f1f1f
#F0F0F0
+
+ @color/material_dynamic_primary95
+ @color/material_dynamic_neutral80
+ @color/material_dynamic_neutral95
\ No newline at end of file
diff --git a/app/src/main/res/values/styles_android.xml b/app/src/main/res/values/styles_android.xml
index b58b27e90..4b794a828 100644
--- a/app/src/main/res/values/styles_android.xml
+++ b/app/src/main/res/values/styles_android.xml
@@ -67,11 +67,6 @@
- @dimen/spacing_small
-
-
-
-
-
\ No newline at end of file
From 3aaa2ab0e07ab0b4c23ae4e88c27cd24a8b20f29 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 22 Feb 2022 17:08:57 -0700
Subject: [PATCH 17/38] all: rework logging
Rework logging to be clearer and more standardized.
Rework all usages of lossing to follow a single unified style,
introducing a new "warn" option alongside this.
---
.../java/org/oxycblt/auxio/MainActivity.kt | 16 +--
.../java/org/oxycblt/auxio/MainFragment.kt | 5 +-
.../java/org/oxycblt/auxio/accent/Accent.kt | 35 +-----
.../org/oxycblt/auxio/accent/AccentAdapter.kt | 2 -
...centDialog.kt => AccentCustomizeDialog.kt} | 12 +-
...tManager.kt => AccentGridLayoutManager.kt} | 2 +-
.../org/oxycblt/auxio/coil/AuxioFetcher.kt | 12 +-
.../java/org/oxycblt/auxio/coil/Fetchers.kt | 1 -
.../auxio/detail/AlbumDetailFragment.kt | 13 +-
.../auxio/detail/ArtistDetailFragment.kt | 29 +++--
.../auxio/detail/DetailAppBarLayout.kt | 23 ++--
.../oxycblt/auxio/detail/DetailFragment.kt | 6 +-
.../oxycblt/auxio/detail/DetailViewModel.kt | 17 +--
.../auxio/detail/GenreDetailFragment.kt | 38 +++---
.../detail/recycler/AlbumDetailAdapter.kt | 3 -
.../detail/recycler/ArtistDetailAdapter.kt | 10 +-
.../detail/recycler/GenreDetailAdapter.kt | 10 +-
.../auxio/excluded/ExcludedDatabase.kt | 8 +-
.../oxycblt/auxio/excluded/ExcludedDialog.kt | 9 +-
.../auxio/excluded/ExcludedViewModel.kt | 11 +-
.../home/AdaptiveFloatingActionButton.kt | 5 +-
.../oxycblt/auxio/home/AdaptiveTabStrategy.kt | 14 ++-
.../org/oxycblt/auxio/home/HomeFragment.kt | 23 ++--
.../org/oxycblt/auxio/home/HomeViewModel.kt | 4 +-
.../java/org/oxycblt/auxio/home/tabs/Tab.kt | 2 +-
.../auxio/home/tabs/TabCustomizeDialog.kt | 7 +-
.../java/org/oxycblt/auxio/music/Models.kt | 9 +-
.../org/oxycblt/auxio/music/MusicLoader.kt | 31 +++--
.../org/oxycblt/auxio/music/MusicStore.kt | 10 +-
.../org/oxycblt/auxio/music/MusicUtils.kt | 52 +++++++-
.../org/oxycblt/auxio/music/MusicViewModel.kt | 7 +-
.../auxio/playback/PlaybackFragment.kt | 7 +-
.../oxycblt/auxio/playback/PlaybackLayout.kt | 13 +-
.../oxycblt/auxio/playback/PlaybackSeekBar.kt | 2 +
.../auxio/playback/PlaybackViewModel.kt | 20 ++-
.../auxio/playback/queue/QueueAdapter.kt | 8 +-
.../auxio/playback/queue/QueueDragCallback.kt | 7 +-
.../auxio/playback/queue/QueueFragment.kt | 5 +-
.../playback/state/PlaybackStateDatabase.kt | 117 +++++++++---------
.../playback/state/PlaybackStateManager.kt | 29 ++---
.../auxio/playback/system/AudioReactor.kt | 10 +-
.../playback/system/MediaButtonReceiver.kt | 2 +
.../auxio/playback/system/PlaybackService.kt | 17 ++-
.../system/PlaybackSessionConnector.kt | 3 +
.../org/oxycblt/auxio/search/SearchAdapter.kt | 3 +-
.../oxycblt/auxio/search/SearchFragment.kt | 3 +-
.../oxycblt/auxio/search/SearchViewModel.kt | 8 +-
.../oxycblt/auxio/settings/AboutFragment.kt | 4 +-
.../oxycblt/auxio/settings/SettingsCompat.kt | 3 +-
.../auxio/settings/SettingsListFragment.kt | 7 +-
.../oxycblt/auxio/settings/SettingsManager.kt | 2 +-
.../org/oxycblt/auxio/ui/EdgeAppBarLayout.kt | 9 +-
.../java/org/oxycblt/auxio/ui/MemberBinder.kt | 2 +-
.../org/oxycblt/auxio/util/ContextUtil.kt | 14 +--
.../java/org/oxycblt/auxio/util/DbUtil.kt | 2 +-
.../java/org/oxycblt/auxio/util/LogUtil.kt | 19 +++
.../java/org/oxycblt/auxio/util/ViewUtil.kt | 1 +
.../oxycblt/auxio/widgets/WidgetController.kt | 3 +
.../oxycblt/auxio/widgets/WidgetProvider.kt | 9 +-
.../res/layout-sw640dp/view_playback_bar.xml | 2 +-
.../res/layout-w600dp/view_playback_bar.xml | 2 +-
app/src/main/res/layout/dialog_accent.xml | 2 +-
app/src/main/res/layout/item_album.xml | 2 +-
app/src/main/res/layout/item_artist.xml | 2 +-
app/src/main/res/layout/item_genre.xml | 2 +-
app/src/main/res/layout/item_genre_song.xml | 2 +-
app/src/main/res/layout/item_queue_song.xml | 2 +-
app/src/main/res/layout/item_song.xml | 2 +-
app/src/main/res/layout/view_playback_bar.xml | 2 +-
69 files changed, 436 insertions(+), 339 deletions(-)
rename app/src/main/java/org/oxycblt/auxio/accent/{AccentDialog.kt => AccentCustomizeDialog.kt} (90%)
rename app/src/main/java/org/oxycblt/auxio/accent/{AutoGridLayoutManager.kt => AccentGridLayoutManager.kt} (98%)
diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
index 8cfc2b966..4c05f784d 100644
--- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
+++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt
@@ -29,7 +29,6 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.core.view.updatePadding
import androidx.databinding.DataBindingUtil
import androidx.viewbinding.ViewBinding
-import org.oxycblt.auxio.accent.Accent
import org.oxycblt.auxio.databinding.ActivityMainBinding
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.system.PlaybackService
@@ -56,7 +55,7 @@ class MainActivity : AppCompatActivity() {
applyEdgeToEdgeWindow(binding)
- logD("Activity created.")
+ logD("Activity created")
}
override fun onStart() {
@@ -94,26 +93,29 @@ class MainActivity : AppCompatActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12, let dynamic colors be our accent and only enable the black theme option
if (isNight && settingsManager.useBlackTheme) {
+ logD("Applying black theme [dynamic colors]")
setTheme(R.style.Theme_Auxio_Black)
}
} else {
// Below android 12, load the accent and enable theme customization
AppCompatDelegate.setDefaultNightMode(settingsManager.theme)
- val newAccent = Accent.set(settingsManager.accent)
+ val accent = settingsManager.accent
// The black theme has a completely separate set of styles since style attributes cannot
// be modified at runtime.
if (isNight && settingsManager.useBlackTheme) {
- setTheme(newAccent.blackTheme)
+ logD("Applying black theme [with accent $accent]")
+ setTheme(accent.blackTheme)
} else {
- setTheme(newAccent.theme)
+ logD("Applying normal theme [with accent $accent]")
+ setTheme(accent.theme)
}
}
}
private fun applyEdgeToEdgeWindow(binding: ViewBinding) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- logD("Doing R+ edge-to-edge.")
+ logD("Doing R+ edge-to-edge")
window?.setDecorFitsSystemWindows(false)
@@ -136,7 +138,7 @@ class MainActivity : AppCompatActivity() {
}
} else {
// Do old edge-to-edge otherwise.
- logD("Doing legacy edge-to-edge.")
+ logD("Doing legacy edge-to-edge")
@Suppress("DEPRECATION")
binding.root.apply {
diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
index 0da9ef221..a19d45c0a 100644
--- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
@@ -36,6 +36,7 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
/**
* A wrapper around the home fragment that shows the playback fragment and controls
@@ -110,7 +111,7 @@ class MainFragment : Fragment() {
// Error, show the error to the user
is MusicStore.Response.Err -> {
- logD("Received Error")
+ logW("Received Error")
val errorRes = when (response.kind) {
MusicStore.ErrorKind.NO_MUSIC -> R.string.err_no_music
@@ -142,7 +143,7 @@ class MainFragment : Fragment() {
}
}
- logD("Fragment Created.")
+ logD("Fragment Created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
index ccec41a24..97205cb44 100644
--- a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
+++ b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
@@ -100,6 +100,9 @@ private val ACCENT_PRIMARY_COLORS = arrayOf(
/**
* The data object for an accent. In the UI this is known as a "Color Scheme."
+ * This can be nominally used to gleam some attributes about a given color scheme, but this
+ * is not recommended. Attributes are usually the better option in nearly all cases.
+ *
* @property name The name of this accent
* @property theme The theme resource for this accent
* @property blackTheme The black theme resource for this accent
@@ -111,36 +114,4 @@ data class Accent(val index: Int) {
val theme: Int get() = ACCENT_THEMES[index]
val blackTheme: Int get() = ACCENT_BLACK_THEMES[index]
val primary: Int get() = ACCENT_PRIMARY_COLORS[index]
-
- companion object {
- @Volatile
- private var CURRENT: Accent? = null
-
- /**
- * Get the current accent.
- * @return The current accent
- * @throws IllegalStateException When the accent has not been set.
- */
- fun get(): Accent {
- val cur = CURRENT
-
- if (cur != null) {
- return cur
- }
-
- error("Accent must be set before retrieving it.")
- }
-
- /**
- * Set the current accent.
- * @return The new accent
- */
- fun set(accent: Accent): Accent {
- synchronized(this) {
- CURRENT = accent
- }
-
- return accent
- }
- }
}
diff --git a/app/src/main/java/org/oxycblt/auxio/accent/AccentAdapter.kt b/app/src/main/java/org/oxycblt/auxio/accent/AccentAdapter.kt
index f42ecb3c9..0eaa56242 100644
--- a/app/src/main/java/org/oxycblt/auxio/accent/AccentAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/accent/AccentAdapter.kt
@@ -77,12 +77,10 @@ class AccentAdapter(
val context = binding.accent.context
binding.accent.isEnabled = !isSelected
-
binding.accent.imageTintList = if (isSelected) {
// Switch out the currently selected ViewHolder with this one.
selectedViewHolder?.setSelected(false)
selectedViewHolder = this
-
context.getAttrColorSafe(R.attr.colorSurface).stateList
} else {
context.getColorSafe(android.R.color.transparent).stateList
diff --git a/app/src/main/java/org/oxycblt/auxio/accent/AccentDialog.kt b/app/src/main/java/org/oxycblt/auxio/accent/AccentCustomizeDialog.kt
similarity index 90%
rename from app/src/main/java/org/oxycblt/auxio/accent/AccentDialog.kt
rename to app/src/main/java/org/oxycblt/auxio/accent/AccentCustomizeDialog.kt
index 3a6df6535..6c2321e5a 100644
--- a/app/src/main/java/org/oxycblt/auxio/accent/AccentDialog.kt
+++ b/app/src/main/java/org/oxycblt/auxio/accent/AccentCustomizeDialog.kt
@@ -34,9 +34,9 @@ import org.oxycblt.auxio.util.logD
* Dialog responsible for showing the list of accents to select.
* @author OxygenCobalt
*/
-class AccentDialog : LifecycleDialog() {
+class AccentCustomizeDialog : LifecycleDialog() {
private val settingsManager = SettingsManager.getInstance()
- private var pendingAccent = Accent.get()
+ private var pendingAccent = settingsManager.accent
override fun onCreateView(
inflater: LayoutInflater,
@@ -53,18 +53,18 @@ class AccentDialog : LifecycleDialog() {
binding.accentRecycler.apply {
adapter = AccentAdapter(pendingAccent) { accent ->
+ logD("Switching selected accent to $accent")
pendingAccent = accent
}
}
- logD("Dialog created.")
+ logD("Dialog created")
return binding.root
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
-
outState.putInt(KEY_PENDING_ACCENT, pendingAccent.index)
}
@@ -72,9 +72,9 @@ class AccentDialog : LifecycleDialog() {
builder.setTitle(R.string.set_accent)
builder.setPositiveButton(android.R.string.ok) { _, _ ->
- if (pendingAccent != Accent.get()) {
+ if (pendingAccent != settingsManager.accent) {
+ logD("Applying new accent")
settingsManager.accent = pendingAccent
-
requireActivity().recreate()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/accent/AutoGridLayoutManager.kt b/app/src/main/java/org/oxycblt/auxio/accent/AccentGridLayoutManager.kt
similarity index 98%
rename from app/src/main/java/org/oxycblt/auxio/accent/AutoGridLayoutManager.kt
rename to app/src/main/java/org/oxycblt/auxio/accent/AccentGridLayoutManager.kt
index 4d371927e..48ed253ee 100644
--- a/app/src/main/java/org/oxycblt/auxio/accent/AutoGridLayoutManager.kt
+++ b/app/src/main/java/org/oxycblt/auxio/accent/AccentGridLayoutManager.kt
@@ -30,7 +30,7 @@ import kotlin.math.max
* of the RecyclerView.
* Adapted from this StackOverflow answer: https://stackoverflow.com/a/30256880/14143986
*/
-class AutoGridLayoutManager(
+class AccentGridLayoutManager(
context: Context,
attrs: AttributeSet,
defStyleAttr: Int,
diff --git a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt
index 910e4c43c..266227ae2 100644
--- a/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt
+++ b/app/src/main/java/org/oxycblt/auxio/coil/AuxioFetcher.kt
@@ -27,6 +27,7 @@ import okio.source
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
import java.io.ByteArrayInputStream
import java.io.InputStream
import android.util.Size as AndroidSize
@@ -55,6 +56,7 @@ abstract class AuxioFetcher : Fetcher {
fetchMediaStoreCovers(context, album)
}
} catch (e: Exception) {
+ logW("Unable to extract album art due to an error")
null
}
}
@@ -80,7 +82,6 @@ abstract class AuxioFetcher : Fetcher {
// music app which relies on proprietary OneUI extensions instead of AOSP. That means
// we have to have another layer of redundancy to retain quality. Thanks samsung. Prick.
val result = fetchAospMetadataCovers(context, album)
-
if (result != null) {
return result
}
@@ -88,7 +89,6 @@ abstract class AuxioFetcher : Fetcher {
// Our next fallback is to rely on ExoPlayer's largely half-baked and undocumented
// metadata system.
val exoResult = fetchExoplayerCover(context, album)
-
if (exoResult != null) {
return exoResult
}
@@ -97,7 +97,6 @@ abstract class AuxioFetcher : Fetcher {
// going against the point of this setting. The previous two calls are just too unreliable
// and we can't do any filesystem traversing due to scoped storage.
val mediaStoreResult = fetchMediaStoreCovers(context, album)
-
if (mediaStoreResult != null) {
return mediaStoreResult
}
@@ -192,7 +191,7 @@ abstract class AuxioFetcher : Fetcher {
} else if (stream != null) {
// In the case a front cover is not found, use the first image in the tag instead.
// This can be corrected later on if a front cover frame is found.
- logD("No front cover image, using image of type $type instead")
+ logW("No front cover image, using image of type $type instead")
stream = ByteArrayInputStream(pic)
}
@@ -223,9 +222,10 @@ abstract class AuxioFetcher : Fetcher {
val increment = AndroidSize(mosaicSize.width / 2, mosaicSize.height / 2)
val mosaicBitmap = Bitmap.createBitmap(
- mosaicSize.width, mosaicSize.height, Bitmap.Config.ARGB_8888
+ mosaicSize.width,
+ mosaicSize.height,
+ Bitmap.Config.ARGB_8888
)
-
val canvas = Canvas(mosaicBitmap)
var x = 0
diff --git a/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt b/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt
index 418083170..f3119c7b7 100644
--- a/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt
+++ b/app/src/main/java/org/oxycblt/auxio/coil/Fetchers.kt
@@ -79,7 +79,6 @@ class ArtistImageFetcher private constructor(
override suspend fun fetch(): FetchResult? {
val albums = Sort.ByName(true)
.sortAlbums(artist.albums)
-
val results = albums.mapAtMost(4) { album ->
fetchArt(context, album)
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
index 689f8113d..18dcc05f3 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt
@@ -40,6 +40,7 @@ import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
import org.oxycblt.auxio.util.showToast
/**
@@ -111,10 +112,11 @@ class AlbumDetailFragment : DetailFragment() {
// fragment should be launched otherwise.
is Song -> {
if (detailModel.curAlbum.value!!.id == item.album.id) {
+ logD("Navigating to a song in this album")
scrollToItem(item.id, detailAdapter)
-
detailModel.finishNavToItem()
} else {
+ logD("Navigating to another album")
findNavController().navigate(
AlbumDetailFragmentDirections.actionShowAlbum(item.album.id)
)
@@ -125,9 +127,11 @@ class AlbumDetailFragment : DetailFragment() {
// detail fragment.
is Album -> {
if (detailModel.curAlbum.value!!.id == item.id) {
+ logD("Navigating to the top of this album")
binding.detailRecycler.scrollToPosition(0)
detailModel.finishNavToItem()
} else {
+ logD("Navigating to another album")
findNavController().navigate(
AlbumDetailFragmentDirections.actionShowAlbum(item.id)
)
@@ -136,13 +140,14 @@ class AlbumDetailFragment : DetailFragment() {
// Always launch a new ArtistDetailFragment.
is Artist -> {
+ logD("Navigating to another artist")
findNavController().navigate(
AlbumDetailFragmentDirections.actionShowArtist(item.id)
)
}
- else -> {
- }
+ null -> {}
+ else -> logW("Unsupported navigation item ${item::class.java}")
}
}
@@ -161,7 +166,7 @@ class AlbumDetailFragment : DetailFragment() {
}
}
- logD("Fragment created.")
+ logD("Fragment created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt
index eaa875e0b..60f112b4c 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt
@@ -35,6 +35,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
/**
* The [DetailFragment] for an artist.
@@ -98,25 +99,33 @@ class ArtistDetailFragment : DetailFragment() {
when (item) {
is Artist -> {
if (item.id == detailModel.curArtist.value?.id) {
+ logD("Navigating to the top of this artist")
binding.detailRecycler.scrollToPosition(0)
detailModel.finishNavToItem()
} else {
+ logD("Navigating to another artist")
findNavController().navigate(
ArtistDetailFragmentDirections.actionShowArtist(item.id)
)
}
}
- is Album -> findNavController().navigate(
- ArtistDetailFragmentDirections.actionShowAlbum(item.id)
- )
-
- is Song -> findNavController().navigate(
- ArtistDetailFragmentDirections.actionShowAlbum(item.album.id)
- )
-
- else -> {
+ is Album -> {
+ logD("Navigating to another album")
+ findNavController().navigate(
+ ArtistDetailFragmentDirections.actionShowAlbum(item.id)
+ )
}
+
+ is Song -> {
+ logD("Navigating to another album")
+ findNavController().navigate(
+ ArtistDetailFragmentDirections.actionShowAlbum(item.album.id)
+ )
+ }
+
+ null -> {}
+ else -> logW("Unsupported navigation item ${item::class.java}")
}
}
@@ -141,7 +150,7 @@ class ArtistDetailFragment : DetailFragment() {
}
}
- logD("Fragment created.")
+ logD("Fragment created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
index 516eff0d6..6a073aff6 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
@@ -14,6 +14,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.appbar.AppBarLayout
import org.oxycblt.auxio.R
import org.oxycblt.auxio.ui.EdgeAppBarLayout
+import org.oxycblt.auxio.util.logE
+import org.oxycblt.auxio.util.logTraceOrThrow
+import java.lang.Exception
/**
* An [EdgeAppBarLayout] variant that also shows the name of the toolbar whenever the detail
@@ -39,9 +42,8 @@ class DetailAppBarLayout @JvmOverloads constructor(
(layoutParams as CoordinatorLayout.LayoutParams).behavior = Behavior(context)
}
- private fun findTitleView(): AppCompatTextView {
+ private fun findTitleView(): AppCompatTextView? {
val titleView = mTitleView
-
if (titleView != null) {
return titleView
}
@@ -49,13 +51,18 @@ class DetailAppBarLayout @JvmOverloads constructor(
val toolbar = findViewById(R.id.detail_toolbar)
// Reflect to get the actual title view to do transformations on
- val newTitleView = Toolbar::class.java.getDeclaredField("mTitleTextView").run {
- isAccessible = true
- get(toolbar) as AppCompatTextView
+ val newTitleView = try {
+ Toolbar::class.java.getDeclaredField("mTitleTextView").run {
+ isAccessible = true
+ get(toolbar) as AppCompatTextView
+ }
+ } catch (e: Exception) {
+ logE("Could not get toolbar title view (likely an internal code change)")
+ e.logTraceOrThrow()
+ return null
}
newTitleView.alpha = 0f
-
mTitleView = newTitleView
return newTitleView
}
@@ -95,11 +102,11 @@ class DetailAppBarLayout @JvmOverloads constructor(
to = 0f
}
- if (titleView.alpha == to) return
+ if (titleView?.alpha == to) return
mTitleAnimator = ValueAnimator.ofFloat(from, to).apply {
addUpdateListener {
- titleView.alpha = it.animatedValue as Float
+ titleView?.alpha = it.animatedValue as Float
}
duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong()
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt
index b085c946f..a29a934f3 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt
@@ -31,6 +31,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.applySpans
+import org.oxycblt.auxio.util.logD
/**
* A Base [Fragment] implementing the base features shared across all detail fragments.
@@ -43,13 +44,11 @@ abstract class DetailFragment : Fragment() {
override fun onResume() {
super.onResume()
-
detailModel.setNavigating(false)
}
override fun onStop() {
super.onStop()
-
// Cancel all pending menus when this fragment stops to prevent bugs/crashes
detailModel.finishShowMenu(null)
}
@@ -94,7 +93,6 @@ abstract class DetailFragment : Fragment() {
binding.detailRecycler.apply {
adapter = detailAdapter
setHasFixedSize(true)
-
applySpans(gridLookup)
}
}
@@ -105,6 +103,8 @@ abstract class DetailFragment : Fragment() {
* @param showItem Which menu items to keep
*/
protected fun showMenu(config: DetailViewModel.MenuConfig, showItem: ((Int) -> Boolean)? = null) {
+ logD("Launching menu [$config]")
+
PopupMenu(config.anchor.context, config.anchor).apply {
inflate(R.menu.menu_detail_sort)
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
index 3c1db8a1b..fed77ca96 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
@@ -33,6 +33,7 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.Sort
+import org.oxycblt.auxio.util.logD
/**
* ViewModel that stores data for the [DetailFragment]s. This includes:
@@ -77,12 +78,10 @@ class DetailViewModel : ViewModel() {
private set
private var currentMenuContext: DisplayMode? = null
-
private val settingsManager = SettingsManager.getInstance()
fun setGenre(id: Long) {
if (mCurGenre.value?.id == id) return
-
val musicStore = MusicStore.requireInstance()
mCurGenre.value = musicStore.genres.find { it.id == id }
refreshGenreData()
@@ -90,7 +89,6 @@ class DetailViewModel : ViewModel() {
fun setArtist(id: Long) {
if (mCurArtist.value?.id == id) return
-
val musicStore = MusicStore.requireInstance()
mCurArtist.value = musicStore.artists.find { it.id == id }
refreshArtistData()
@@ -98,7 +96,6 @@ class DetailViewModel : ViewModel() {
fun setAlbum(id: Long) {
if (mCurAlbum.value?.id == id) return
-
val musicStore = MusicStore.requireInstance()
mCurAlbum.value = musicStore.albums.find { it.id == id }
refreshAlbumData()
@@ -112,6 +109,7 @@ class DetailViewModel : ViewModel() {
mShowMenu.value = null
if (newMode != null) {
+ logD("Applying new sort mode")
when (currentMenuContext) {
DisplayMode.SHOW_ALBUMS -> {
settingsManager.detailAlbumSort = newMode
@@ -154,7 +152,9 @@ class DetailViewModel : ViewModel() {
}
private fun refreshGenreData() {
- val data = mutableListOf(curGenre.value!!)
+ logD("Refreshing genre data")
+ val genre = requireNotNull(curGenre.value)
+ val data = mutableListOf(genre)
data.add(
ActionHeader(
@@ -175,7 +175,8 @@ class DetailViewModel : ViewModel() {
}
private fun refreshArtistData() {
- val artist = curArtist.value!!
+ logD("Refreshing artist data")
+ val artist = requireNotNull(curArtist.value)
val data = mutableListOf(artist)
data.add(
@@ -206,7 +207,9 @@ class DetailViewModel : ViewModel() {
}
private fun refreshAlbumData() {
- val data = mutableListOf(curAlbum.value!!)
+ logD("Refreshing album data")
+ val album = requireNotNull(curAlbum.value)
+ val data = mutableListOf(album)
data.add(
ActionHeader(
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt
index 8bb6d9ac9..a14d4378e 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt
@@ -35,6 +35,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
/**
* The [DetailFragment] for a genre.
@@ -79,20 +80,29 @@ class GenreDetailFragment : DetailFragment() {
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
when (item) {
// All items will launch new detail fragments.
- is Artist -> findNavController().navigate(
- GenreDetailFragmentDirections.actionShowArtist(item.id)
- )
-
- is Album -> findNavController().navigate(
- GenreDetailFragmentDirections.actionShowAlbum(item.id)
- )
-
- is Song -> findNavController().navigate(
- GenreDetailFragmentDirections.actionShowAlbum(item.album.id)
- )
-
- else -> {
+ is Artist -> {
+ logD("Navigating to another artist")
+ findNavController().navigate(
+ GenreDetailFragmentDirections.actionShowArtist(item.id)
+ )
}
+
+ is Album -> {
+ logD("Navigating to another album")
+ findNavController().navigate(
+ GenreDetailFragmentDirections.actionShowAlbum(item.id)
+ )
+ }
+
+ is Song -> {
+ logD("Navigating to another song")
+ findNavController().navigate(
+ GenreDetailFragmentDirections.actionShowAlbum(item.album.id)
+ )
+ }
+
+ null -> {}
+ else -> logW("Unsupported navigation command ${item::class.java}")
}
}
@@ -115,7 +125,7 @@ class GenreDetailFragment : DetailFragment() {
}
}
- logD("Fragment created.")
+ logD("Fragment created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
index c48a069d3..f09acf501 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
@@ -58,7 +58,6 @@ class AlbumDetailAdapter(
is Album -> ALBUM_DETAIL_ITEM_TYPE
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
is Song -> ALBUM_SONG_ITEM_TYPE
-
else -> -1
}
}
@@ -86,7 +85,6 @@ class AlbumDetailAdapter(
is Album -> (holder as AlbumDetailViewHolder).bind(item)
is Song -> (holder as AlbumSongViewHolder).bind(item)
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
-
else -> {
}
}
@@ -127,7 +125,6 @@ class AlbumDetailAdapter(
recycler.layoutManager?.findViewByPosition(pos)?.let { child ->
recycler.getChildViewHolder(child)?.let {
currentHolder = it as Highlightable
-
currentHolder?.setHighlighted(true)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
index de8c0a32c..614522ca7 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
@@ -33,12 +33,12 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
+import org.oxycblt.auxio.music.bindArtistInfo
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
import org.oxycblt.auxio.ui.BaseViewHolder
import org.oxycblt.auxio.ui.DiffCallback
import org.oxycblt.auxio.ui.HeaderViewHolder
-import org.oxycblt.auxio.util.getPluralSafe
import org.oxycblt.auxio.util.inflater
/**
@@ -64,7 +64,6 @@ class ArtistDetailAdapter(
is Song -> ARTIST_SONG_ITEM_TYPE
is Header -> HeaderViewHolder.ITEM_TYPE
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
-
else -> -1
}
}
@@ -174,7 +173,6 @@ class ArtistDetailAdapter(
recycler.layoutManager?.findViewByPosition(pos)?.let { child ->
recycler.getChildViewHolder(child)?.let {
currentSongHolder = it as Highlightable
-
currentSongHolder?.setHighlighted(true)
}
}
@@ -205,11 +203,7 @@ class ArtistDetailAdapter(
.entries.maxByOrNull { it.value.size }
?.key ?: context.getString(R.string.def_genre)
- binding.detailInfo.text = context.getString(
- R.string.fmt_counts,
- context.getPluralSafe(R.plurals.fmt_album_count, data.albums.size),
- context.getPluralSafe(R.plurals.fmt_song_count, data.songs.size)
- )
+ binding.detailInfo.bindArtistInfo(data)
binding.detailPlayButton.setOnClickListener {
playbackModel.playArtist(data, false)
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
index 9f0e8366c..804b33561 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
@@ -30,11 +30,11 @@ import org.oxycblt.auxio.music.ActionHeader
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
+import org.oxycblt.auxio.music.bindGenreInfo
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
import org.oxycblt.auxio.ui.BaseViewHolder
import org.oxycblt.auxio.ui.DiffCallback
-import org.oxycblt.auxio.util.getPluralSafe
import org.oxycblt.auxio.util.inflater
/**
@@ -54,7 +54,6 @@ class GenreDetailAdapter(
is Genre -> GENRE_DETAIL_ITEM_TYPE
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
is Song -> GENRE_SONG_ITEM_TYPE
-
else -> -1
}
}
@@ -121,7 +120,6 @@ class GenreDetailAdapter(
recycler.layoutManager?.findViewByPosition(pos)?.let { child ->
recycler.getChildViewHolder(child)?.let {
currentHolder = it as Highlightable
-
currentHolder?.setHighlighted(true)
}
}
@@ -143,11 +141,7 @@ class GenreDetailAdapter(
}
binding.detailName.text = data.resolvedName
-
- binding.detailSubhead.apply {
- text = context.getPluralSafe(R.plurals.fmt_song_count, data.songs.size)
- }
-
+ binding.detailSubhead.bindGenreInfo(data)
binding.detailInfo.text = data.totalDuration
binding.detailPlayButton.setOnClickListener {
diff --git a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDatabase.kt b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDatabase.kt
index a5d434f95..b3562d563 100644
--- a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDatabase.kt
+++ b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDatabase.kt
@@ -55,7 +55,6 @@ class ExcludedDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
writableDatabase.transaction {
delete(TABLE_NAME, null, null)
-
logD("Deleted paths db")
for (path in paths) {
@@ -66,6 +65,8 @@ class ExcludedDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
}
)
}
+
+ logD("Successfully wrote ${paths.size} paths to db")
}
}
@@ -76,17 +77,20 @@ class ExcludedDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, nu
assertBackgroundThread()
val paths = mutableListOf()
-
readableDatabase.queryAll(TABLE_NAME) { cursor ->
while (cursor.moveToNext()) {
paths.add(cursor.getString(0))
}
}
+ logD("Successfully read ${paths.size} paths from db")
+
return paths
}
companion object {
+ // Blacklist is still used here for compatibility reasons, please don't get
+ // your pants in a twist about it.
const val DB_VERSION = 1
const val DB_NAME = "auxio_blacklist_database.db"
diff --git a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDialog.kt b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDialog.kt
index bb339023d..c5b9be463 100644
--- a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDialog.kt
+++ b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedDialog.kt
@@ -77,13 +77,16 @@ class ExcludedDialog : LifecycleDialog() {
dialog.setOnShowListener {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.setOnClickListener {
+ logD("Opening launcher")
launcher.launch(null)
}
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.setOnClickListener {
if (excludedModel.isModified) {
+ logD("Committing changes")
saveAndRestart()
} else {
+ logD("Dropping changes")
dismiss()
}
}
@@ -93,11 +96,10 @@ class ExcludedDialog : LifecycleDialog() {
excludedModel.paths.observe(viewLifecycleOwner) { paths ->
adapter.submitList(paths)
-
binding.excludedEmpty.isVisible = paths.isEmpty()
}
- logD("Dialog created.")
+ logD("Dialog created")
return binding.root
}
@@ -114,6 +116,7 @@ class ExcludedDialog : LifecycleDialog() {
private fun addDocTreePath(uri: Uri?) {
// A null URI means that the user left the file picker without picking a directory
if (uri == null) {
+ logD("No URI given (user closed the dialog)")
return
}
@@ -142,6 +145,7 @@ class ExcludedDialog : LifecycleDialog() {
return getRootPath() + "/" + typeAndPath.last()
}
+ logD("Unsupported volume ${typeAndPath[0]}")
return null
}
@@ -156,7 +160,6 @@ class ExcludedDialog : LifecycleDialog() {
/**
* Get *just* the root path, nothing else is really needed.
*/
- @Suppress("DEPRECATION")
private fun getRootPath(): String {
return Environment.getExternalStorageDirectory().absolutePath
}
diff --git a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedViewModel.kt b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedViewModel.kt
index 504cb4f73..8de9ccc9b 100644
--- a/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/excluded/ExcludedViewModel.kt
@@ -27,6 +27,7 @@ import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import org.oxycblt.auxio.util.logD
/**
* ViewModel that acts as a wrapper around [ExcludedDatabase], allowing for the addition/removal
@@ -73,10 +74,13 @@ class ExcludedViewModel(private val excludedDatabase: ExcludedDatabase) : ViewMo
*/
fun save(onDone: () -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
+ val start = System.currentTimeMillis()
excludedDatabase.writePaths(mPaths.value!!)
dbPaths = mPaths.value!!
-
onDone()
+ this@ExcludedViewModel.logD(
+ "Path save completed successfully in ${System.currentTimeMillis() - start}ms"
+ )
}
}
@@ -85,11 +89,14 @@ class ExcludedViewModel(private val excludedDatabase: ExcludedDatabase) : ViewMo
*/
private fun loadDatabasePaths() {
viewModelScope.launch(Dispatchers.IO) {
+ val start = System.currentTimeMillis()
dbPaths = excludedDatabase.readPaths()
-
withContext(Dispatchers.Main) {
mPaths.value = dbPaths.toMutableList()
}
+ this@ExcludedViewModel.logD(
+ "Path load completed successfully in ${System.currentTimeMillis() - start}ms"
+ )
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt b/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
index 434820910..f6b405332 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.util.AttributeSet
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.oxycblt.auxio.util.getDimenSizeSafe
+import org.oxycblt.auxio.util.logD
import com.google.android.material.R as MaterialR
/**
@@ -20,7 +21,10 @@ class AdaptiveFloatingActionButton @JvmOverloads constructor(
init {
size = SIZE_NORMAL
+ // Use a large FAB on large screens, as it makes it easier to touch.
if (resources.configuration.smallestScreenWidthDp >= 640) {
+ logD("Using large FAB configuration")
+
val largeFabSize = context.getDimenSizeSafe(
MaterialR.dimen.m3_large_fab_size
)
@@ -29,7 +33,6 @@ class AdaptiveFloatingActionButton @JvmOverloads constructor(
MaterialR.dimen.m3_large_fab_max_image_size
)
- // Use a large FAB on large screens, as it makes it easier to touch.
customSize = largeFabSize
setMaxImageSize(largeImageSize)
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveTabStrategy.kt b/app/src/main/java/org/oxycblt/auxio/home/AdaptiveTabStrategy.kt
index d81da4d20..e76f5941a 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveTabStrategy.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/AdaptiveTabStrategy.kt
@@ -3,6 +3,7 @@ package org.oxycblt.auxio.home
import android.content.Context
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
+import org.oxycblt.auxio.util.logD
/**
* A tag configuration strategy that automatically adapts the tab layout to the screen size.
@@ -20,15 +21,22 @@ class AdaptiveTabStrategy(
val tabMode = homeModel.tabs[position]
when {
- width < 370 ->
+ width < 370 -> {
+ logD("Using icon-only configuration")
tab.setIcon(tabMode.icon)
.setContentDescription(tabMode.string)
+ }
- width < 640 -> tab.setText(tabMode.string)
+ width < 640 -> {
+ logD("Using text-only configuration")
+ tab.setText(tabMode.string)
+ }
- else ->
+ else -> {
+ logD("Using icon-and-text configuration")
tab.setIcon(tabMode.icon)
.setText(tabMode.string)
+ }
}
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
index 7143169f9..b5b447ad4 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
@@ -49,6 +49,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
+import org.oxycblt.auxio.util.logTraceOrThrow
/**
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail
@@ -77,16 +78,19 @@ class HomeFragment : Fragment() {
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_search -> {
+ logD("Navigating to search")
findNavController().navigate(HomeFragmentDirections.actionShowSearch())
}
R.id.action_settings -> {
+ logD("Navigating to settings")
parentFragment?.parentFragment?.findNavController()?.navigate(
MainFragmentDirections.actionShowSettings()
)
}
R.id.action_about -> {
+ logD("Navigating to about")
parentFragment?.parentFragment?.findNavController()?.navigate(
MainFragmentDirections.actionShowAbout()
)
@@ -96,20 +100,16 @@ class HomeFragment : Fragment() {
R.id.option_sort_asc -> {
item.isChecked = !item.isChecked
-
val new = homeModel.getSortForDisplay(homeModel.curTab.value!!)
.ascending(item.isChecked)
-
homeModel.updateCurrentSort(new)
}
// Sorting option was selected, mark it as selected and update the mode
else -> {
item.isChecked = true
-
val new = homeModel.getSortForDisplay(homeModel.curTab.value!!)
.assignId(item.itemId)
-
homeModel.updateCurrentSort(requireNotNull(new))
}
}
@@ -141,8 +141,8 @@ class HomeFragment : Fragment() {
set(recycler, slop * 3) // 3x seems to be the best fit here
}
} catch (e: Exception) {
- logE("Unable to reduce ViewPager sensitivity")
- logE(e.stackTraceToString())
+ logE("Unable to reduce ViewPager sensitivity (likely an internal code change)")
+ e.logTraceOrThrow()
}
// We know that there will only be a fixed amount of tabs, so we manually set this
@@ -174,7 +174,7 @@ class HomeFragment : Fragment() {
is MusicStore.Response.Ok -> binding.homeFab.show()
// While loading or during an error, make sure we keep the shuffle fab hidden so
- // that any kind of loading is impossible. PlaybackStateManager also relies on this
+ // that any kind of playback is impossible. PlaybackStateManager also relies on this
// invariant, so please don't change it.
else -> binding.homeFab.hide()
}
@@ -207,7 +207,7 @@ class HomeFragment : Fragment() {
homeModel.curTab.observe(viewLifecycleOwner) { t ->
val tab = requireNotNull(t)
- // Make sure that we update the scrolling view and allowed menu items before whenever
+ // Make sure that we update the scrolling view and allowed menu items whenever
// the tab changes.
when (tab) {
DisplayMode.SHOW_SONGS -> updateSortMenu(sortItem, tab)
@@ -229,8 +229,9 @@ class HomeFragment : Fragment() {
}
detailModel.navToItem.observe(viewLifecycleOwner) { item ->
- // The AppBarLayout gets confused and collapses when we navigate too fast, wait for it
- // to draw before we continue.
+ // The AppBarLayout gets confused when we navigate too fast, wait for it to draw
+ // before we navigate.
+ // This is only here just in case a collapsing toolbar is re-added.
binding.homeAppbar.post {
when (item) {
is Song -> findNavController().navigate(
@@ -255,7 +256,7 @@ class HomeFragment : Fragment() {
}
}
- logD("Fragment Created.")
+ logD("Fragment Created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt
index 0765d3ee1..9089b23f4 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt
@@ -32,6 +32,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.Sort
+import org.oxycblt.auxio.util.logD
/**
* The ViewModel for managing [HomeFragment]'s data, sorting modes, and tab state.
@@ -78,7 +79,6 @@ class HomeViewModel : ViewModel(), SettingsManager.Callback {
viewModelScope.launch {
val musicStore = MusicStore.awaitInstance()
-
mSongs.value = settingsManager.libSongSort.sortSongs(musicStore.songs)
mAlbums.value = settingsManager.libAlbumSort.sortAlbums(musicStore.albums)
mArtists.value = settingsManager.libArtistSort.sortParents(musicStore.artists)
@@ -90,6 +90,7 @@ class HomeViewModel : ViewModel(), SettingsManager.Callback {
* Update the current tab based off of the new ViewPager position.
*/
fun updateCurrentTab(pos: Int) {
+ logD("Updating current tab to ${tabs[pos]}")
mCurTab.value = tabs[pos]
}
@@ -110,6 +111,7 @@ class HomeViewModel : ViewModel(), SettingsManager.Callback {
* Update the currently displayed item's [Sort].
*/
fun updateCurrentSort(sort: Sort) {
+ logD("Updating ${mCurTab.value} sort to $sort")
when (mCurTab.value) {
DisplayMode.SHOW_SONGS -> {
settingsManager.libSongSort = sort
diff --git a/app/src/main/java/org/oxycblt/auxio/home/tabs/Tab.kt b/app/src/main/java/org/oxycblt/auxio/home/tabs/Tab.kt
index a4166a1ec..3f8a30686 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/tabs/Tab.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/tabs/Tab.kt
@@ -109,7 +109,7 @@ sealed class Tab(open val mode: DisplayMode) {
// For safety, return null if we have an empty or larger-than-expected tab array.
if (distinct.isEmpty() || distinct.size < SEQUENCE_LEN) {
- logE("Sequence size was ${distinct.size}, which is invalid.")
+ logE("Sequence size was ${distinct.size}, which is invalid")
return null
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/tabs/TabCustomizeDialog.kt b/app/src/main/java/org/oxycblt/auxio/home/tabs/TabCustomizeDialog.kt
index 5dfa61a1f..93b478f7d 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/tabs/TabCustomizeDialog.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/tabs/TabCustomizeDialog.kt
@@ -29,6 +29,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogTabsBinding
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.LifecycleDialog
+import org.oxycblt.auxio.util.logD
/**
* The dialog for customizing library tabs. This dialog does not rely on any specific ViewModel
@@ -49,7 +50,6 @@ class TabCustomizeDialog : LifecycleDialog() {
if (savedInstanceState != null) {
// Restore any pending tab configurations
val tabs = Tab.fromSequence(savedInstanceState.getInt(KEY_TABS))
-
if (tabs != null) {
pendingTabs = tabs
}
@@ -66,10 +66,9 @@ class TabCustomizeDialog : LifecycleDialog() {
// of how ViewHolders are bound], but instead simply look for the mode in
// the list of pending tabs and update that instead.
val index = pendingTabs.indexOfFirst { it.mode == tab.mode }
-
if (index != -1) {
val curTab = pendingTabs[index]
-
+ logD("Updating tab $curTab to $tab")
pendingTabs[index] = when (curTab) {
is Tab.Visible -> Tab.Invisible(curTab.mode)
is Tab.Invisible -> Tab.Visible(curTab.mode)
@@ -93,7 +92,6 @@ class TabCustomizeDialog : LifecycleDialog() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
-
outState.putInt(KEY_TABS, Tab.toSequence(pendingTabs))
}
@@ -101,6 +99,7 @@ class TabCustomizeDialog : LifecycleDialog() {
builder.setTitle(R.string.set_lib_tabs)
builder.setPositiveButton(android.R.string.ok) { _, _ ->
+ logD("Committing tab changes")
settingsManager.libTabs = pendingTabs
}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
index 8f35bf4ac..d4f7e296c 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
@@ -124,8 +124,12 @@ data class Song(
val internalGroupingArtistName: String get() = internalMediaStoreAlbumArtistName
?: internalMediaStoreArtistName ?: MediaStore.UNKNOWN_STRING
+ /** Internal field. Do not use. */
+ val internalIsMissingAlbum: Boolean get() = mAlbum == null
+ /** Internal field. Do not use. */
+ val internalIsMissingArtist: Boolean get() = mAlbum?.internalIsMissingArtist ?: true
/** Internal field. Do not use. **/
- val internalMissingGenre: Boolean get() = mGenre == null
+ val internalIsMissingGenre: Boolean get() = mGenre == null
/** Internal method. Do not use. */
fun internalLinkAlbum(album: Album) {
@@ -180,6 +184,9 @@ data class Album(
val resolvedArtistName: String get() =
artist.resolvedName
+ /** Internal field. Do not use. */
+ val internalIsMissingArtist: Boolean = mArtist != null
+
/** Internal method. Do not use. */
fun internalLinkArtist(artist: Artist) {
mArtist = artist
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
index c39f267a5..286cb13b1 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt
@@ -7,8 +7,8 @@ import android.provider.MediaStore
import androidx.core.database.getStringOrNull
import org.oxycblt.auxio.R
import org.oxycblt.auxio.excluded.ExcludedDatabase
+import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
-import java.lang.Exception
/**
* This class acts as the base for most the black magic required to get a remotely sensible music
@@ -26,7 +26,7 @@ import java.lang.Exception
* have to query for each genre, query all the songs in each genre, and then iterate through those
* songs to link every song with their genre. This is not documented anywhere, and the
* O(mom im scared) algorithm you have to run to get it working single-handedly DOUBLES Auxio's
- * loading times. At no point have the devs considered that this column is absolutely insane, and
+ * loading times. At no point have the devs considered that this system is absolutely insane, and
* instead focused on adding infuriat- I mean nice proprietary extensions to MediaStore for their
* own Google Play Music, and of course every Google Play Music user knew how great that turned
* out!
@@ -88,14 +88,14 @@ class MusicLoader {
val artists = buildArtists(context, albums)
val genres = readGenres(context, songs)
- // Sanity check: Ensure that all songs are well-formed.
+ // Sanity check: Ensure that all songs are linked up to albums/artists/genres.
for (song in songs) {
- try {
- song.album.artist
- song.genre
- } catch (e: Exception) {
+ if (song.internalIsMissingAlbum ||
+ song.internalIsMissingArtist ||
+ song.internalIsMissingGenre
+ ) {
logE("Found malformed song: ${song.name}")
- throw e
+ throw IllegalStateException()
}
}
@@ -204,6 +204,8 @@ class MusicLoader {
it.internalMediaStoreAlbumArtistName to it.track to it.duration
}.toMutableList()
+ logD("Successfully loaded ${songs.size} songs")
+
return songs
}
@@ -247,6 +249,8 @@ class MusicLoader {
)
}
+ logD("Successfully built ${albums.size} albums")
+
return albums
}
@@ -264,14 +268,15 @@ class MusicLoader {
// Album deduplication does not eliminate every case of fragmented artists, do
// we deduplicate in the artist creation step as well.
- // Note that we actually don't do this in groupBy. This is generally because we
- // only want to default to a lowercase artist name when we have no other choice.
+ // Note that we actually don't do this in groupBy. This is generally because using
+ // a template song may not result in the best possible artist name in all cases.
val previousArtistIndex = artists.indexOfFirst { artist ->
artist.name.lowercase() == artistName.lowercase()
}
if (previousArtistIndex > -1) {
val previousArtist = artists[previousArtistIndex]
+ logD("Merging duplicate artist into pre-existing artist ${previousArtist.name}")
artists[previousArtistIndex] = Artist(
previousArtist.name,
previousArtist.resolvedName,
@@ -288,6 +293,8 @@ class MusicLoader {
}
}
+ logD("Successfully built ${artists.size} artists")
+
return artists
}
@@ -327,7 +334,7 @@ class MusicLoader {
}
}
- val songsWithoutGenres = songs.filter { it.internalMissingGenre }
+ val songsWithoutGenres = songs.filter { it.internalIsMissingGenre }
if (songsWithoutGenres.isNotEmpty()) {
// Songs that don't have a genre will be thrown into an unknown genre.
@@ -340,6 +347,8 @@ class MusicLoader {
genres.add(unknownGenre)
}
+ logD("Successfully loaded ${genres.size} genres")
+
return genres
}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt
index 22addc747..7cc5782bc 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt
@@ -55,7 +55,7 @@ class MusicStore private constructor() {
* Load/Sort the entire music library. Should always be ran on a coroutine.
*/
private fun load(context: Context): Response {
- logD("Starting initial music load...")
+ logD("Starting initial music load")
val notGranted = ContextCompat.checkSelfPermission(
context, Manifest.permission.READ_EXTERNAL_STORAGE
@@ -76,11 +76,10 @@ class MusicStore private constructor() {
mArtists = library.artists
mGenres = library.genres
- logD("Music load completed successfully in ${System.currentTimeMillis() - start}ms.")
+ logD("Music load completed successfully in ${System.currentTimeMillis() - start}ms")
} catch (e: Exception) {
- logE("Something went horribly wrong.")
+ logE("Something went horribly wrong")
logE(e.stackTraceToString())
-
return Response.Err(ErrorKind.FAILED)
}
@@ -117,6 +116,7 @@ class MusicStore private constructor() {
/**
* A response that [MusicStore] returns when loading music.
* And before you ask, yes, I do like rust.
+ * TODO: Replace this with the kotlin builtin
*/
sealed class Response {
class Ok(val musicStore: MusicStore) : Response()
@@ -201,7 +201,7 @@ class MusicStore private constructor() {
*/
fun requireInstance(): MusicStore {
return requireNotNull(maybeGetInstance()) {
- "Required MusicStore instance was not available."
+ "Required MusicStore instance was not available"
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt
index cc3d87b49..0dc9c2d29 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt
@@ -25,6 +25,8 @@ import androidx.core.text.isDigitsOnly
import androidx.databinding.BindingAdapter
import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.getPluralSafe
+import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
/**
* A complete array of all the hardcoded genre values for ID3(v2), contains standard genres and
@@ -98,6 +100,7 @@ fun String.getGenreNameCompat(): String? {
*/
fun Long.toDuration(isElapsed: Boolean): String {
if (!isElapsed && this == 0L) {
+ logD("Non-elapsed duration is zero, using --:--")
return "--:--"
}
@@ -121,14 +124,53 @@ fun Int.toDate(context: Context): String {
// --- BINDING ADAPTERS ---
-/**
- * Bind the album + song counts for an artist
- */
-@BindingAdapter("artistCounts")
-fun TextView.bindArtistCounts(artist: Artist) {
+@BindingAdapter("songInfo")
+fun TextView.bindSongInfo(song: Song?) {
+ if (song == null) {
+ logW("Song was null, not applying info")
+ return
+ }
+
+ text = context.getString(
+ R.string.fmt_two,
+ song.resolvedArtistName,
+ song.resolvedAlbumName
+ )
+}
+
+@BindingAdapter("albumInfo")
+fun TextView.bindAlbumInfo(album: Album?) {
+ if (album == null) {
+ logW("Album was null, not applying info")
+ return
+ }
+
+ text = context.getString(
+ R.string.fmt_two, album.resolvedArtistName,
+ context.getPluralSafe(R.plurals.fmt_song_count, album.songs.size)
+ )
+}
+
+@BindingAdapter("artistInfo")
+fun TextView.bindArtistInfo(artist: Artist?) {
+ if (artist == null) {
+ logW("Artist was null, not applying info")
+ return
+ }
+
text = context.getString(
R.string.fmt_counts,
context.getPluralSafe(R.plurals.fmt_album_count, artist.albums.size),
context.getPluralSafe(R.plurals.fmt_song_count, artist.songs.size)
)
}
+
+@BindingAdapter("genreInfo")
+fun TextView.bindGenreInfo(genre: Genre?) {
+ if (genre == null) {
+ logW("Genre was null, not applying info")
+ return
+ }
+
+ text = context.getPluralSafe(R.plurals.fmt_song_count, genre.songs.size)
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt
index a71e76c19..b91adb12f 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt
@@ -24,6 +24,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
+import org.oxycblt.auxio.util.logD
class MusicViewModel : ViewModel() {
private val mLoaderResponse = MutableLiveData(null)
@@ -37,6 +38,7 @@ class MusicViewModel : ViewModel() {
*/
fun loadMusic(context: Context) {
if (mLoaderResponse.value != null || isBusy) {
+ logD("Loader is busy/already completed, not reloading")
return
}
@@ -45,15 +47,14 @@ class MusicViewModel : ViewModel() {
viewModelScope.launch {
val result = MusicStore.initInstance(context)
-
- isBusy = false
mLoaderResponse.value = result
+ isBusy = false
}
}
fun reloadMusic(context: Context) {
+ logD("Reloading music library")
mLoaderResponse.value = null
-
loadMusic(context)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
index 92ae2cd93..b9864f665 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt
@@ -92,7 +92,6 @@ class PlaybackFragment : Fragment() {
// Make marquee of song title work
binding.playbackSong.isSelected = true
binding.playbackSeekBar.onConfirmListener = playbackModel::setPosition
-
binding.playbackPlayPause.post {
binding.playbackPlayPause.stateListAnimator = null
}
@@ -101,11 +100,11 @@ class PlaybackFragment : Fragment() {
playbackModel.song.observe(viewLifecycleOwner) { song ->
if (song != null) {
- logD("Updating song display to ${song.name}.")
+ logD("Updating song display to ${song.name}")
binding.song = song
binding.playbackSeekBar.setDuration(song.seconds)
} else {
- logD("No song is being played, leaving.")
+ logD("No song is being played, leaving")
findNavController().navigateUp()
}
}
@@ -152,7 +151,7 @@ class PlaybackFragment : Fragment() {
}
}
- logD("Fragment Created.")
+ logD("Fragment Created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
index 027ccbf00..f5d984841 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackLayout.kt
@@ -27,6 +27,7 @@ import org.oxycblt.auxio.util.disableDropShadowCompat
import org.oxycblt.auxio.util.getAttrColorSafe
import org.oxycblt.auxio.util.getDimenSafe
import org.oxycblt.auxio.util.getDrawableSafe
+import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.pxOfDp
import org.oxycblt.auxio.util.replaceSystemBarInsetsCompat
import org.oxycblt.auxio.util.stateList
@@ -225,6 +226,8 @@ class PlaybackLayout @JvmOverloads constructor(
}
private fun applyState(state: PanelState) {
+ logD("Applying panel state $state")
+
// Dragging events are really complex and we don't want to mess up the state
// while we are in one.
if (state == panelState || panelState == PanelState.DRAGGING) {
@@ -357,10 +360,8 @@ class PlaybackLayout @JvmOverloads constructor(
// bottom navigation is consumed by a bar. To fix this, we modify the bottom insets
// to reflect the presence of the panel [at least in it's collapsed state]
playbackContainerView.dispatchApplyWindowInsets(insets)
-
lastInsets = insets
applyContentWindowInsets()
-
return insets
}
@@ -370,7 +371,6 @@ class PlaybackLayout @JvmOverloads constructor(
*/
private fun applyContentWindowInsets() {
val insets = lastInsets
-
if (insets != null) {
contentView.dispatchApplyWindowInsets(adjustInsets(insets))
}
@@ -386,8 +386,9 @@ class PlaybackLayout @JvmOverloads constructor(
val bars = insets.systemBarInsetsCompat
val consumedByPanel = computePanelTopPosition(panelOffset) - measuredHeight
val adjustedBottomInset = (consumedByPanel + bars.bottom).coerceAtLeast(0)
-
- return insets.replaceSystemBarInsetsCompat(bars.left, bars.top, bars.right, adjustedBottomInset)
+ return insets.replaceSystemBarInsetsCompat(
+ bars.left, bars.top, bars.right, adjustedBottomInset
+ )
}
override fun onSaveInstanceState(): Parcelable = Bundle().apply {
@@ -586,6 +587,8 @@ class PlaybackLayout @JvmOverloads constructor(
(computePanelTopPosition(0f) - topPosition).toFloat() / panelRange
private fun smoothSlideTo(offset: Float) {
+ logD("Smooth sliding to $offset")
+
val okay = dragHelper.smoothSlideViewTo(
playbackContainerView, playbackContainerView.left, computePanelTopPosition(offset)
)
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
index f2e517cd0..274995005 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
@@ -29,6 +29,7 @@ import org.oxycblt.auxio.databinding.ViewSeekBarBinding
import org.oxycblt.auxio.music.toDuration
import org.oxycblt.auxio.util.getAttrColorSafe
import org.oxycblt.auxio.util.inflater
+import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.stateList
/**
@@ -73,6 +74,7 @@ class PlaybackSeekBar @JvmOverloads constructor(
// - The duration of the song was so low as to be rounded to zero when converted
// to seconds.
// In either of these cases, the seekbar is more or less useless. Disable it.
+ logD("Duration is 0, entering disabled state")
binding.seekBar.apply {
valueTo = 1f
isEnabled = false
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
index a741adafe..1fcb41a35 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt
@@ -111,7 +111,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
*/
fun playAlbum(album: Album, shuffled: Boolean) {
if (album.songs.isEmpty()) {
- logE("Album is empty, Not playing.")
+ logE("Album is empty, Not playing")
return
}
@@ -125,7 +125,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
*/
fun playArtist(artist: Artist, shuffled: Boolean) {
if (artist.songs.isEmpty()) {
- logE("Artist is empty, Not playing.")
+ logE("Artist is empty, Not playing")
return
}
@@ -139,7 +139,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
*/
fun playGenre(genre: Genre, shuffled: Boolean) {
if (genre.songs.isEmpty()) {
- logE("Genre is empty, Not playing.")
+ logE("Genre is empty, Not playing")
return
}
@@ -156,7 +156,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
if (playbackManager.isRestored && MusicStore.loaded()) {
playWithUriInternal(uri, context)
} else {
- logD("Cant play this URI right now, waiting...")
+ logD("Cant play this URI right now, waiting")
mIntentUri = uri
}
@@ -213,12 +213,10 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
* [apply] is called just before the change is committed so that the adapter can be updated.
*/
fun removeQueueDataItem(adapterIndex: Int, apply: () -> Unit) {
- val adjusted = adapterIndex + (playbackManager.queue.size - mNextUp.value!!.size)
- logD("$adjusted")
-
- if (adjusted in playbackManager.queue.indices) {
+ val index = adapterIndex + (playbackManager.queue.size - mNextUp.value!!.size)
+ if (index in playbackManager.queue.indices) {
apply()
- playbackManager.removeQueueItem(adjusted)
+ playbackManager.removeQueueItem(index)
}
}
/**
@@ -227,10 +225,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
*/
fun moveQueueDataItems(adapterFrom: Int, adapterTo: Int, apply: () -> Unit): Boolean {
val delta = (playbackManager.queue.size - mNextUp.value!!.size)
-
val from = adapterFrom + delta
val to = adapterTo + delta
-
if (from in playbackManager.queue.indices && to in playbackManager.queue.indices) {
apply()
playbackManager.moveQueueItems(from, to)
@@ -332,7 +328,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
* [PlaybackStateManager] instance.
*/
private fun restorePlaybackState() {
- logD("Attempting to restore playback state.")
+ logD("Attempting to restore playback state")
onSongUpdate(playbackManager.song)
onPositionUpdate(playbackManager.position)
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
index d61af2e03..6c6970662 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
@@ -70,11 +70,9 @@ class QueueAdapter(
QUEUE_SONG_ITEM_TYPE -> QueueSongViewHolder(
ItemQueueSongBinding.inflate(parent.context.inflater)
)
-
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder.from(parent.context)
-
- else -> error("Invalid ViewHolder item type $viewType.")
+ else -> error("Invalid ViewHolder item type $viewType")
}
}
@@ -83,8 +81,7 @@ class QueueAdapter(
is Song -> (holder as QueueSongViewHolder).bind(item)
is Header -> (holder as HeaderViewHolder).bind(item)
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
-
- else -> logE("Bad data given to QueueAdapter.")
+ else -> logE("Bad data given to QueueAdapter")
}
}
@@ -95,7 +92,6 @@ class QueueAdapter(
fun submitList(newData: MutableList) {
if (data != newData) {
data = newData
-
listDiffer.submitList(newData)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
index 3b80f863e..3478f6a9e 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt
@@ -27,6 +27,7 @@ import com.google.android.material.shape.MaterialShapeDrawable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.getDimenSafe
+import org.oxycblt.auxio.util.logD
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -89,9 +90,10 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
val holder = viewHolder as QueueAdapter.QueueSongViewHolder
if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
+ logD("Lifting queue item")
+
val bg = holder.bodyView.background as MaterialShapeDrawable
val elevation = recyclerView.context.getDimenSafe(R.dimen.elevation_small)
-
holder.itemView.animate()
.translationZ(elevation)
.setDuration(100)
@@ -127,8 +129,9 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
val holder = viewHolder as QueueAdapter.QueueSongViewHolder
if (holder.itemView.translationZ != 0.0f) {
- val bg = holder.bodyView.background as MaterialShapeDrawable
+ logD("Dropping queue item")
+ val bg = holder.bodyView.background as MaterialShapeDrawable
holder.itemView.animate()
.translationZ(0.0f)
.setDuration(100)
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt
index cc9d2bb27..8765c4d98 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt
@@ -28,6 +28,7 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.ItemTouchHelper
import org.oxycblt.auxio.databinding.FragmentQueueBinding
import org.oxycblt.auxio.playback.PlaybackViewModel
+import org.oxycblt.auxio.util.logD
/**
* A [Fragment] that shows the queue and enables editing as well.
@@ -77,9 +78,11 @@ class QueueFragment : Fragment() {
}
playbackModel.isShuffling.observe(viewLifecycleOwner) { isShuffling ->
+ // Try to prevent the queue adapter from going spastic during reshuffle events
+ // by just scrolling back to the top.
if (isShuffling != lastShuffle) {
+ logD("Reshuffle event, scrolling to top")
lastShuffle = isShuffling
-
binding.queueRecycler.scrollToPosition(0)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt
index 5ed2a2b35..3450096f3 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt
@@ -48,10 +48,10 @@ class PlaybackStateDatabase(context: Context) :
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = nuke(db)
private fun nuke(db: SQLiteDatabase) {
+ logD("Nuking database")
db.apply {
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_STATE")
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_QUEUE")
-
onCreate(this)
}
}
@@ -103,34 +103,6 @@ class PlaybackStateDatabase(context: Context) :
// --- INTERFACE FUNCTIONS ---
- /**
- * Clear the previously written [SavedState] and write a new one.
- */
- fun writeState(state: SavedState) {
- assertBackgroundThread()
-
- writableDatabase.transaction {
- delete(TABLE_NAME_STATE, null, null)
-
- this@PlaybackStateDatabase.logD("Wiped state db.")
-
- val stateData = ContentValues(10).apply {
- put(StateColumns.COLUMN_ID, 0)
- put(StateColumns.COLUMN_SONG_HASH, state.song?.id)
- put(StateColumns.COLUMN_POSITION, state.position)
- put(StateColumns.COLUMN_PARENT_HASH, state.parent?.id)
- put(StateColumns.COLUMN_QUEUE_INDEX, state.queueIndex)
- put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
- put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
- put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
- }
-
- insert(TABLE_NAME_STATE, null, stateData)
- }
-
- logD("Wrote state to database.")
- }
-
/**
* Read the stored [SavedState] from the database, if there is one.
* @param musicStore Required to transform database songs/parents into actual instances
@@ -178,11 +150,69 @@ class PlaybackStateDatabase(context: Context) :
isShuffling = cursor.getInt(shuffleIndex) == 1,
loopMode = LoopMode.fromInt(cursor.getInt(loopModeIndex)) ?: LoopMode.NONE,
)
+
+ logD("Successfully read playback state: $state")
}
return state
}
+ /**
+ * Clear the previously written [SavedState] and write a new one.
+ */
+ fun writeState(state: SavedState) {
+ assertBackgroundThread()
+
+ writableDatabase.transaction {
+ delete(TABLE_NAME_STATE, null, null)
+
+ this@PlaybackStateDatabase.logD("Wiped state db")
+
+ val stateData = ContentValues(10).apply {
+ put(StateColumns.COLUMN_ID, 0)
+ put(StateColumns.COLUMN_SONG_HASH, state.song?.id)
+ put(StateColumns.COLUMN_POSITION, state.position)
+ put(StateColumns.COLUMN_PARENT_HASH, state.parent?.id)
+ put(StateColumns.COLUMN_QUEUE_INDEX, state.queueIndex)
+ put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
+ put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
+ put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
+ }
+
+ insert(TABLE_NAME_STATE, null, stateData)
+ }
+
+ logD("Wrote state to database")
+ }
+
+ /**
+ * Read a list of queue items from this database.
+ * @param musicStore Required to transform database songs into actual song instances
+ */
+ fun readQueue(musicStore: MusicStore): MutableList {
+ assertBackgroundThread()
+
+ val queue = mutableListOf()
+
+ readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
+ if (cursor.count == 0) return@queryAll
+
+ val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_HASH)
+ val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_HASH)
+
+ while (cursor.moveToNext()) {
+ musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))
+ ?.let { song ->
+ queue.add(song)
+ }
+ }
+ }
+
+ logD("Successfully read queue of ${queue.size} songs")
+
+ return queue
+ }
+
/**
* Write a queue to the database.
*/
@@ -190,12 +220,11 @@ class PlaybackStateDatabase(context: Context) :
assertBackgroundThread()
val database = writableDatabase
-
database.transaction {
delete(TABLE_NAME_QUEUE, null, null)
}
- logD("Wiped queue db.")
+ logD("Wiped queue db")
writeQueueBatch(queue, queue.size)
}
@@ -232,32 +261,6 @@ class PlaybackStateDatabase(context: Context) :
}
}
- /**
- * Read a list of queue items from this database.
- * @param musicStore Required to transform database songs into actual song instances
- */
- fun readQueue(musicStore: MusicStore): MutableList {
- assertBackgroundThread()
-
- val queue = mutableListOf()
-
- readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
- if (cursor.count == 0) return@queryAll
-
- val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_HASH)
- val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_HASH)
-
- while (cursor.moveToNext()) {
- musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))
- ?.let { song ->
- queue.add(song)
- }
- }
- }
-
- return queue
- }
-
data class SavedState(
val song: Song?,
val position: Long,
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 fea3fd451..50ce3a643 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
@@ -224,7 +224,6 @@ class PlaybackStateManager private constructor() {
private fun updatePlayback(song: Song, shouldPlay: Boolean = true) {
mSong = song
mPosition = 0
-
setPlaying(shouldPlay)
}
@@ -271,18 +270,14 @@ class PlaybackStateManager private constructor() {
* Remove a queue item at [index]. Will ignore invalid indexes.
*/
fun removeQueueItem(index: Int): Boolean {
- logD("Removing item ${mQueue[index].name}.")
-
if (index > mQueue.size || index < 0) {
- logE("Index is out of bounds, did not remove queue item.")
-
+ logE("Index is out of bounds, did not remove queue item")
return false
}
+ logD("Removing item ${mQueue[index].name}")
mQueue.removeAt(index)
-
pushQueueUpdate()
-
return true
}
@@ -292,15 +287,12 @@ class PlaybackStateManager private constructor() {
fun moveQueueItems(from: Int, to: Int): Boolean {
if (from > mQueue.size || from < 0 || to > mQueue.size || to < 0) {
logE("Indices were out of bounds, did not move queue item")
-
return false
}
- val item = mQueue.removeAt(from)
- mQueue.add(to, item)
-
+ logD("Moving item $from to position $to")
+ mQueue.add(to, mQueue.removeAt(from))
pushQueueUpdate()
-
return true
}
@@ -501,7 +493,7 @@ class PlaybackStateManager private constructor() {
* @param context [Context] required
*/
suspend fun saveStateToDatabase(context: Context) {
- logD("Saving state to DB.")
+ logD("Saving state to DB")
// Pack the entire state and save it to the database.
withContext(Dispatchers.IO) {
@@ -519,7 +511,7 @@ class PlaybackStateManager private constructor() {
database.writeQueue(mQueue)
this@PlaybackStateManager.logD(
- "Save finished in ${System.currentTimeMillis() - start}ms"
+ "State save completed successfully in ${System.currentTimeMillis() - start}ms"
)
}
}
@@ -529,10 +521,9 @@ class PlaybackStateManager private constructor() {
* @param context [Context] required.
*/
suspend fun restoreFromDatabase(context: Context) {
- logD("Getting state from DB.")
+ logD("Getting state from DB")
val musicStore = MusicStore.maybeGetInstance() ?: return
-
val start: Long
val playbackState: PlaybackStateDatabase.SavedState?
val queue: MutableList
@@ -549,15 +540,13 @@ class PlaybackStateManager private constructor() {
// Get off the IO coroutine since it will cause LiveData updates to throw an exception
if (playbackState != null) {
- logD("Found playback state $playbackState")
-
unpackFromPlaybackState(playbackState)
unpackQueue(queue)
doParentSanityCheck()
doIndexSanityCheck()
}
- logD("Restore finished in ${System.currentTimeMillis() - start}ms")
+ logD("State load completed successfully in ${System.currentTimeMillis() - start}ms")
markRestored()
}
@@ -592,7 +581,7 @@ class PlaybackStateManager private constructor() {
private fun doParentSanityCheck() {
// Check if the parent was lost while in the DB.
if (mSong != null && mParent == null && mPlaybackMode != PlaybackMode.ALL_SONGS) {
- logD("Parent lost, attempting restore.")
+ logD("Parent lost, attempting restore")
mParent = when (mPlaybackMode) {
PlaybackMode.IN_ALBUM -> mQueue.firstOrNull()?.album
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
index d450a40c2..23b7c3a16 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt
@@ -33,6 +33,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.util.getSystemServiceSafe
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
import kotlin.math.pow
/**
@@ -85,6 +86,7 @@ class AudioReactor(
* Request the android system for audio focus
*/
fun requestFocus() {
+ logD("Requesting audio focus")
AudioManagerCompat.requestAudioFocus(audioManager, request)
}
@@ -94,7 +96,7 @@ class AudioReactor(
*/
fun applyReplayGain(metadata: Metadata?) {
if (metadata == null) {
- logD("No metadata.")
+ logW("No metadata could be extracted from this track")
volume = 1f
return
}
@@ -102,7 +104,7 @@ class AudioReactor(
// ReplayGain is configurable, so determine what to do based off of the mode.
val useAlbumGain: (Gain) -> Boolean = when (settingsManager.replayGainMode) {
ReplayGainMode.OFF -> {
- logD("ReplayGain is off.")
+ logD("ReplayGain is off")
volume = 1f
return
}
@@ -132,10 +134,10 @@ class AudioReactor(
val adjust = if (gain != null) {
if (useAlbumGain(gain)) {
- logD("Using album gain.")
+ logD("Using album gain")
gain.album
} else {
- logD("Using track gain.")
+ logD("Using track gain")
gain.track
}
} else {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt
index 0173fc7b5..8cde108b2 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt
@@ -5,6 +5,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
+import org.oxycblt.auxio.util.logD
/**
* Some apps like to party like it's 2011 and just blindly query for the ACTION_MEDIA_BUTTON
@@ -20,6 +21,7 @@ import androidx.core.content.ContextCompat
class MediaButtonReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_MEDIA_BUTTON) {
+ logD("Received external media button intent")
intent.component = ComponentName(context, PlaybackService::class.java)
ContextCompat.startForegroundService(context, intent)
}
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 78cbb9a2f..d9842ffa9 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
@@ -180,7 +180,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
settingsManager.addCallback(this)
- logD("Service created.")
+ logD("Service created")
}
override fun onDestroy() {
@@ -207,7 +207,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
serviceJob.cancel()
}
- logD("Service destroyed.")
+ logD("Service destroyed")
}
// --- PLAYER EVENT LISTENER OVERRIDES ---
@@ -260,22 +260,21 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
override fun onSongUpdate(song: Song?) {
if (song != null) {
+ logD("Setting player to ${song.name}")
player.setMediaItem(MediaItem.fromUri(song.uri))
player.prepare()
-
notification.setMetadata(song, ::startForegroundOrNotify)
-
return
}
// Clear if there's nothing to play.
+ logD("Nothing playing, stopping playback")
player.stop()
stopForegroundAndNotification()
}
override fun onParentUpdate(parent: MusicParent?) {
notification.setParent(parent)
-
startForegroundOrNotify()
}
@@ -295,7 +294,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
override fun onLoopUpdate(loopMode: LoopMode) {
if (!settingsManager.useAltNotifAction) {
notification.setLoop(loopMode)
-
startForegroundOrNotify()
}
}
@@ -303,7 +301,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
override fun onShuffleUpdate(isShuffling: Boolean) {
if (settingsManager.useAltNotifAction) {
notification.setShuffle(isShuffling)
-
startForegroundOrNotify()
}
}
@@ -334,7 +331,6 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
override fun onShowCoverUpdate(showCovers: Boolean) {
playbackManager.song?.let { song ->
connector.onSongUpdate(song)
-
notification.setMetadata(song, ::startForegroundOrNotify)
}
}
@@ -449,6 +445,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
/**
* A [BroadcastReceiver] for receiving general playback events from the system.
+ * TODO: Don't fire when the service initially starts?
*/
private inner class PlaybackReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -501,7 +498,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
*/
private fun resumeFromPlug() {
if (playbackManager.song != null && settingsManager.doPlugMgt) {
- logD("Device connected, resuming...")
+ logD("Device connected, resuming")
playbackManager.setPlaying(true)
}
}
@@ -511,7 +508,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateManager.Callbac
*/
private fun pauseFromPlug() {
if (playbackManager.song != null && settingsManager.doPlugMgt) {
- logD("Device disconnected, pausing...")
+ logD("Device disconnected, pausing")
playbackManager.setPlaying(false)
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt
index 336667cf3..0c211c683 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackSessionConnector.kt
@@ -29,6 +29,7 @@ import org.oxycblt.auxio.coil.loadBitmap
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackStateManager
+import org.oxycblt.auxio.util.logD
/**
* Nightmarish class that coordinates communication between [MediaSessionCompat], [Player],
@@ -158,6 +159,8 @@ class PlaybackSessionConnector(
// --- MISC ---
private fun invalidateSessionState() {
+ logD("Updating media session state")
+
// Position updates arrive faster when you upload STATE_PAUSED for some insane reason.
val state = PlaybackStateCompat.Builder()
.setActions(ACTIONS)
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
index 8104c55a0..b7ea8a1ec 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
@@ -52,7 +52,6 @@ class SearchAdapter(
is Album -> AlbumViewHolder.ITEM_TYPE
is Song -> SongViewHolder.ITEM_TYPE
is Header -> HeaderViewHolder.ITEM_TYPE
-
else -> -1
}
}
@@ -77,7 +76,7 @@ class SearchAdapter(
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
- else -> error("Invalid ViewHolder item type.")
+ else -> error("Invalid ViewHolder item type")
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
index b39647326..f83e3b566 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt
@@ -114,7 +114,6 @@ class SearchFragment : Fragment() {
if (!launchedKeyboard) {
// Auto-open the keyboard when this view is shown
requestFocus()
-
postDelayed(200) {
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
}
@@ -162,7 +161,7 @@ class SearchFragment : Fragment() {
imm.hide()
}
- logD("Fragment created.")
+ logD("Fragment created")
return binding.root
}
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
index 4f9604edc..6c84a9157 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
@@ -33,6 +33,7 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.Sort
+import org.oxycblt.auxio.util.logD
import java.text.Normalizer
/**
@@ -70,11 +71,14 @@ class SearchViewModel : ViewModel() {
mLastQuery = query
if (query.isEmpty() || musicStore == null) {
+ logD("No music/query, ignoring search")
mSearchResults.value = listOf()
return
}
- // Searching can be quite expensive, so hop on a co-routine
+ logD("Performing search for $query")
+
+ // Searching can be quite expensive, so get on a co-routine
viewModelScope.launch {
val sort = Sort.ByName(true)
val results = mutableListOf()
@@ -127,6 +131,8 @@ class SearchViewModel : ViewModel() {
else -> null
}
+ logD("Updating filter mode to $mFilterMode")
+
settingsManager.searchFilterMode = mFilterMode
search(mLastQuery)
diff --git a/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt
index 64cb1bf26..f24110e70 100644
--- a/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/settings/AboutFragment.kt
@@ -74,7 +74,7 @@ class AboutFragment : Fragment() {
)
}
- logD("Dialog created.")
+ logD("Dialog created")
return binding.root
}
@@ -83,6 +83,8 @@ class AboutFragment : Fragment() {
* Go through the process of opening a [link] in a browser.
*/
private fun openLinkInBrowser(link: String) {
+ logD("Opening $link")
+
val browserIntent = Intent(Intent.ACTION_VIEW, link.toUri()).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
)
diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt
index 7c678052e..8ee87c17e 100644
--- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt
+++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsCompat.kt
@@ -22,8 +22,7 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import org.oxycblt.auxio.accent.Accent
-// A couple of utils for migrating from old settings values to the new
-// formats used in 1.3.2 & 1.4.0
+// A couple of utils for migrating from old settings values to the new formats
fun handleAccentCompat(prefs: SharedPreferences): Accent {
if (prefs.contains(OldKeys.KEY_ACCENT2)) {
diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt
index e20a184fb..525d5654d 100644
--- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt
@@ -31,7 +31,7 @@ import androidx.preference.children
import androidx.recyclerview.widget.RecyclerView
import coil.Coil
import org.oxycblt.auxio.R
-import org.oxycblt.auxio.accent.AccentDialog
+import org.oxycblt.auxio.accent.AccentCustomizeDialog
import org.oxycblt.auxio.excluded.ExcludedDialog
import org.oxycblt.auxio.home.tabs.TabCustomizeDialog
import org.oxycblt.auxio.playback.PlaybackViewModel
@@ -68,7 +68,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
}
}
- logD("Fragment created.")
+ logD("Fragment created")
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -119,7 +119,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
SettingsManager.KEY_ACCENT -> {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
- AccentDialog().show(childFragmentManager, AccentDialog.TAG)
+ AccentCustomizeDialog().show(childFragmentManager, AccentCustomizeDialog.TAG)
true
}
@@ -182,7 +182,6 @@ class SettingsListFragment : PreferenceFragmentCompat() {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto
AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day
AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night
-
else -> R.drawable.ic_auto
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt
index c2e3c311b..8557cabc2 100644
--- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt
+++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt
@@ -331,7 +331,7 @@ class SettingsManager private constructor(context: Context) :
return instance
}
- error("SettingsManager must be initialized with init() before getting its instance.")
+ error("SettingsManager must be initialized with init() before getting its instance")
}
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
index 60f6ff74c..8ecaf814a 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
@@ -29,7 +29,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updatePadding
import com.google.android.material.appbar.AppBarLayout
-import org.oxycblt.auxio.util.logE
+import org.oxycblt.auxio.util.logW
import org.oxycblt.auxio.util.systemBarInsetsCompat
/**
@@ -51,7 +51,6 @@ open class EdgeAppBarLayout @JvmOverloads constructor(
if (child != null) {
val coordinator = parent as CoordinatorLayout
-
(layoutParams as CoordinatorLayout.LayoutParams).behavior?.onNestedPreScroll(
coordinator, this, coordinator, 0, 0, tConsumed, 0
)
@@ -66,15 +65,12 @@ open class EdgeAppBarLayout @JvmOverloads constructor(
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
super.onApplyWindowInsets(insets)
-
updatePadding(top = insets.systemBarInsetsCompat.top)
-
return insets
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
-
viewTreeObserver.removeOnPreDrawListener(onPreDraw)
}
@@ -94,9 +90,10 @@ open class EdgeAppBarLayout @JvmOverloads constructor(
if (liftOnScrollTargetViewId != ResourcesCompat.ID_NULL) {
scrollingChild = (parent as ViewGroup).findViewById(liftOnScrollTargetViewId)
} else {
- logE("liftOnScrollTargetViewId was not specified. ignoring scroll events.")
+ logW("liftOnScrollTargetViewId was not specified. ignoring scroll events")
}
}
+
return scrollingChild
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt b/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt
index b460b7290..811f8d2e1 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt
@@ -73,7 +73,7 @@ class MemberBinder(
val lifecycle = fragment.viewLifecycleOwner.lifecycle
check(lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
- "Fragment views are destroyed."
+ "Fragment views are destroyed"
}
// Otherwise create the binding and return that.
diff --git a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
index 148b00110..77ca4c7b2 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/ContextUtil.kt
@@ -39,7 +39,6 @@ import androidx.annotation.PluralsRes
import androidx.annotation.Px
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
-import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.MainActivity
import kotlin.reflect.KClass
import kotlin.system.exitProcess
@@ -190,16 +189,9 @@ fun Context.pxOfDp(@Dimension dp: Float): Int {
}
private fun Context.handleResourceFailure(e: Exception, what: String, default: T): T {
- logE("$what load failed.")
-
- if (BuildConfig.DEBUG) {
- // I'd rather be aware of a sudden crash when debugging.
- throw e
- } else {
- // Not so much when the app is in production.
- logE(e.stackTraceToString())
- return default
- }
+ logE("$what load failed")
+ e.logTraceOrThrow()
+ return default
}
/**
diff --git a/app/src/main/java/org/oxycblt/auxio/util/DbUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/DbUtil.kt
index 8895960a6..5d09b5096 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/DbUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/DbUtil.kt
@@ -34,7 +34,7 @@ fun SQLiteDatabase.queryAll(tableName: String, block: (Cursor) -> R) =
*/
fun assertBackgroundThread() {
check(Looper.myLooper() != Looper.getMainLooper()) {
- "This operation must be ran on a background thread."
+ "This operation must be ran on a background thread"
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
index fc5bdb105..500b65df9 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/LogUtil.kt
@@ -41,6 +41,13 @@ fun Any.logD(msg: String) {
}
}
+/**
+ * Shortcut method for logging [msg] as a warning to the console. Handles anonymous objects
+ */
+fun Any.logW(msg: String) {
+ Log.w(getName(), msg)
+}
+
/**
* Shortcut method for logging [msg] as an error to the console. Handles anonymous objects
*/
@@ -48,6 +55,18 @@ fun Any.logE(msg: String) {
Log.e(getName(), msg)
}
+/**
+ * Logs an error in production while still throwing it in debug mode. This is useful for
+ * non-showstopper bugs that I would still prefer to be caught in debug mode.
+ */
+fun Throwable.logTraceOrThrow() {
+ if (BuildConfig.DEBUG) {
+ throw this
+ } else {
+ logE(stackTraceToString())
+ }
+}
+
/**
* Get a non-nullable name, used so that logs will always show up by Auxio
* @return The name of the object, otherwise "Anonymous Object"
diff --git a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
index 5ca64eacc..7f1c9773e 100644
--- a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
+++ b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt
@@ -69,6 +69,7 @@ fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
*/
fun View.disableDropShadowCompat() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ logD("Disabling drop shadows")
val transparent = context.getColorSafe(android.R.color.transparent)
outlineAmbientShadowColor = transparent
outlineSpotShadowColor = transparent
diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt
index c28a5b560..10bd5026c 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetController.kt
@@ -23,6 +23,7 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager
+import org.oxycblt.auxio.util.logD
/**
* A wrapper around each [WidgetProvider] that plugs into the main Auxio process and updates the
@@ -53,6 +54,8 @@ class WidgetController(private val context: Context) :
* Release this instance, removing the callbacks and resetting all widgets
*/
fun release() {
+ logD("Releasing instance")
+
widget.reset(context)
playbackManager.removeCallback(this)
settingsManager.removeCallback(this)
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 dad9770de..96dbf54ed 100644
--- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt
+++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetProvider.kt
@@ -40,6 +40,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.getDimenSizeSafe
import org.oxycblt.auxio.util.isLandscape
import org.oxycblt.auxio.util.logD
+import org.oxycblt.auxio.util.logW
import kotlin.math.min
/**
@@ -87,6 +88,10 @@ class WidgetProvider : AppWidgetProvider() {
}
}
+ /**
+ * Custom function for loading bitmaps to the widget in a way that works with the
+ * widget ImageView instances.
+ */
private fun loadWidgetBitmap(context: Context, song: Song, onDone: (Bitmap?) -> Unit) {
val coverRequest = ImageRequest.Builder(context)
.data(song.album)
@@ -152,6 +157,8 @@ class WidgetProvider : AppWidgetProvider() {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ logD("Requesting new view from PlaybackService")
+
// We can't resize the widget until we can generate the views, so request an update
// from PlaybackService.
requestUpdate(context)
@@ -234,7 +241,7 @@ class WidgetProvider : AppWidgetProvider() {
continue
} else {
// Default to the smallest view if no layout fits
- logD("No widget layout found")
+ logW("No good widget layout found")
val minimum = requireNotNull(
views.minByOrNull { it.key.width * it.key.height }?.value
diff --git a/app/src/main/res/layout-sw640dp/view_playback_bar.xml b/app/src/main/res/layout-sw640dp/view_playback_bar.xml
index d2238a9fd..19356f679 100644
--- a/app/src/main/res/layout-sw640dp/view_playback_bar.xml
+++ b/app/src/main/res/layout-sw640dp/view_playback_bar.xml
@@ -52,7 +52,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
android:ellipsize="end"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
app:layout_constraintEnd_toEndOf="@+id/playback_song"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
diff --git a/app/src/main/res/layout-w600dp/view_playback_bar.xml b/app/src/main/res/layout-w600dp/view_playback_bar.xml
index 94a1484f3..63dbed1bc 100644
--- a/app/src/main/res/layout-w600dp/view_playback_bar.xml
+++ b/app/src/main/res/layout-w600dp/view_playback_bar.xml
@@ -50,7 +50,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
android:ellipsize="end"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
app:layout_constraintEnd_toEndOf="@+id/playback_song"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
diff --git a/app/src/main/res/layout/dialog_accent.xml b/app/src/main/res/layout/dialog_accent.xml
index 31af1a5a7..68c6f1609 100644
--- a/app/src/main/res/layout/dialog_accent.xml
+++ b/app/src/main/res/layout/dialog_accent.xml
@@ -12,7 +12,7 @@
android:paddingTop="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_medium"
android:paddingBottom="@dimen/spacing_small"
- app:layoutManager="org.oxycblt.auxio.accent.AutoGridLayoutManager"
+ app:layoutManager="org.oxycblt.auxio.accent.AccentGridLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/accent_cancel"
app:layout_constraintTop_toBottomOf="@+id/accent_header"
tools:itemCount="18"
diff --git a/app/src/main/res/layout/item_album.xml b/app/src/main/res/layout/item_album.xml
index 898936cac..66eb8006f 100644
--- a/app/src/main/res/layout/item_album.xml
+++ b/app/src/main/res/layout/item_album.xml
@@ -41,7 +41,7 @@
style="@style/Widget.Auxio.TextView.Item.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@{@string/fmt_two(album.resolvedArtistName, @plurals/fmt_song_count(album.songs.size, album.songs.size))}"
+ app:albumInfo="@{album}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_cover"
diff --git a/app/src/main/res/layout/item_artist.xml b/app/src/main/res/layout/item_artist.xml
index c4e4c138b..81ce94239 100644
--- a/app/src/main/res/layout/item_artist.xml
+++ b/app/src/main/res/layout/item_artist.xml
@@ -41,7 +41,7 @@
style="@style/Widget.Auxio.TextView.Item.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:artistCounts="@{artist}"
+ app:artistInfo="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
diff --git a/app/src/main/res/layout/item_genre.xml b/app/src/main/res/layout/item_genre.xml
index 8fb90cb02..5d2109065 100644
--- a/app/src/main/res/layout/item_genre.xml
+++ b/app/src/main/res/layout/item_genre.xml
@@ -41,7 +41,7 @@
style="@style/Widget.Auxio.TextView.Item.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@{@plurals/fmt_song_count(genre.songs.size(), genre.songs.size())}"
+ app:genreInfo="@{genre}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/genre_image"
diff --git a/app/src/main/res/layout/item_genre_song.xml b/app/src/main/res/layout/item_genre_song.xml
index 7d6278c9d..4070e6393 100644
--- a/app/src/main/res/layout/item_genre_song.xml
+++ b/app/src/main/res/layout/item_genre_song.xml
@@ -44,7 +44,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/song_duration"
app:layout_constraintStart_toEndOf="@+id/album_cover"
diff --git a/app/src/main/res/layout/item_queue_song.xml b/app/src/main/res/layout/item_queue_song.xml
index cf9580e97..2f12c6da8 100644
--- a/app/src/main/res/layout/item_queue_song.xml
+++ b/app/src/main/res/layout/item_queue_song.xml
@@ -69,7 +69,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_medium"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/song_drag_handle"
app:layout_constraintStart_toEndOf="@+id/album_cover"
diff --git a/app/src/main/res/layout/item_song.xml b/app/src/main/res/layout/item_song.xml
index 1082b050f..3e4313525 100644
--- a/app/src/main/res/layout/item_song.xml
+++ b/app/src/main/res/layout/item_song.xml
@@ -42,7 +42,7 @@
style="@style/Widget.Auxio.TextView.Item.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/album_cover"
diff --git a/app/src/main/res/layout/view_playback_bar.xml b/app/src/main/res/layout/view_playback_bar.xml
index d9034843e..e05fb7eab 100644
--- a/app/src/main/res/layout/view_playback_bar.xml
+++ b/app/src/main/res/layout/view_playback_bar.xml
@@ -51,7 +51,7 @@
android:layout_marginStart="@dimen/spacing_small"
android:layout_marginEnd="@dimen/spacing_small"
android:ellipsize="end"
- android:text="@{@string/fmt_two(song.resolvedArtistName, song.resolvedAlbumName)}"
+ app:songInfo="@{song}"
app:layout_constraintBottom_toBottomOf="@+id/playback_cover"
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
From 35b75b5f8132a837f44d3894d08deafab5896789 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 22 Feb 2022 17:43:21 -0700
Subject: [PATCH 18/38] music: rename BaseModel to Item
Rename BaseModel to Item to make the meaning of it clearer.
---
CHANGELOG.md | 1 +
.../oxycblt/auxio/detail/DetailViewModel.kt | 26 +++++++++----------
.../detail/recycler/AlbumDetailAdapter.kt | 4 +--
.../detail/recycler/ArtistDetailAdapter.kt | 6 ++---
.../detail/recycler/GenreDetailAdapter.kt | 4 +--
.../auxio/home/list/HomeListFragment.kt | 6 ++---
.../java/org/oxycblt/auxio/music/Models.kt | 26 +++++++++++--------
.../auxio/playback/queue/QueueAdapter.kt | 6 ++---
.../org/oxycblt/auxio/search/SearchAdapter.kt | 4 +--
.../oxycblt/auxio/search/SearchViewModel.kt | 8 +++---
.../java/org/oxycblt/auxio/ui/ActionMenu.kt | 10 +++----
.../java/org/oxycblt/auxio/ui/DiffCallback.kt | 6 ++---
.../java/org/oxycblt/auxio/ui/ViewHolders.kt | 8 +++---
13 files changed, 60 insertions(+), 55 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4625c7228..c2a609d7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
#### Dev/Meta
- Enabled elevation drop shadows below Android P for consistency
- Reworked dynamic color usage
+- Reworked logging
## v2.2.1
#### What's Improved
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
index fed77ca96..31a41dc57 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt
@@ -26,9 +26,9 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.ActionHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.DisplayMode
@@ -49,30 +49,30 @@ class DetailViewModel : ViewModel() {
private val mCurGenre = MutableLiveData()
val curGenre: LiveData get() = mCurGenre
- private val mGenreData = MutableLiveData(listOf())
- val genreData: LiveData> = mGenreData
+ private val mGenreData = MutableLiveData(listOf- ())
+ val genreData: LiveData
> = mGenreData
private val mCurArtist = MutableLiveData()
val curArtist: LiveData get() = mCurArtist
- private val mArtistData = MutableLiveData(listOf())
- val artistData: LiveData> = mArtistData
+ private val mArtistData = MutableLiveData(listOf- ())
+ val artistData: LiveData
> = mArtistData
private val mCurAlbum = MutableLiveData()
val curAlbum: LiveData get() = mCurAlbum
- private val mAlbumData = MutableLiveData(listOf())
- val albumData: LiveData> get() = mAlbumData
+ private val mAlbumData = MutableLiveData(listOf- ())
+ val albumData: LiveData
> get() = mAlbumData
data class MenuConfig(val anchor: View, val sortMode: Sort)
private val mShowMenu = MutableLiveData(null)
val showMenu: LiveData = mShowMenu
- private val mNavToItem = MutableLiveData()
+ private val mNavToItem = MutableLiveData- ()
/** Flag for unified navigation. Observe this to coordinate navigation to an item's UI. */
- val navToItem: LiveData get() = mNavToItem
+ val navToItem: LiveData
- get() = mNavToItem
var isNavigating = false
private set
@@ -133,7 +133,7 @@ class DetailViewModel : ViewModel() {
/**
* Navigate to an item, whether a song/album/artist
*/
- fun navToItem(item: BaseModel) {
+ fun navToItem(item: Item) {
mNavToItem.value = item
}
@@ -154,7 +154,7 @@ class DetailViewModel : ViewModel() {
private fun refreshGenreData() {
logD("Refreshing genre data")
val genre = requireNotNull(curGenre.value)
- val data = mutableListOf(genre)
+ val data = mutableListOf
- (genre)
data.add(
ActionHeader(
@@ -177,7 +177,7 @@ class DetailViewModel : ViewModel() {
private fun refreshArtistData() {
logD("Refreshing artist data")
val artist = requireNotNull(curArtist.value)
- val data = mutableListOf(artist)
+ val data = mutableListOf
- (artist)
data.add(
Header(
@@ -209,7 +209,7 @@ class DetailViewModel : ViewModel() {
private fun refreshAlbumData() {
logD("Refreshing album data")
val album = requireNotNull(curAlbum.value)
- val data = mutableListOf(album)
+ val data = mutableListOf
- (album)
data.add(
ActionHeader(
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
index f09acf501..49eb15549 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt
@@ -30,7 +30,7 @@ import org.oxycblt.auxio.databinding.ItemDetailBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.ActionHeader
import org.oxycblt.auxio.music.Album
-import org.oxycblt.auxio.music.BaseModel
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.toDate
import org.oxycblt.auxio.playback.PlaybackViewModel
@@ -49,7 +49,7 @@ class AlbumDetailAdapter(
private val detailModel: DetailViewModel,
private val doOnClick: (data: Song) -> Unit,
private val doOnLongClick: (view: View, data: Song) -> Unit
-) : ListAdapter(DiffCallback()) {
+) : ListAdapter
- (DiffCallback()) {
private var currentSong: Song? = null
private var currentHolder: Highlightable? = null
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
index 614522ca7..e80eb2ce2 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt
@@ -30,8 +30,8 @@ import org.oxycblt.auxio.databinding.ItemDetailBinding
import org.oxycblt.auxio.music.ActionHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.bindArtistInfo
import org.oxycblt.auxio.playback.PlaybackViewModel
@@ -49,8 +49,8 @@ class ArtistDetailAdapter(
private val playbackModel: PlaybackViewModel,
private val doOnClick: (data: Album) -> Unit,
private val doOnSongClick: (data: Song) -> Unit,
- private val doOnLongClick: (view: View, data: BaseModel) -> Unit,
-) : ListAdapter(DiffCallback()) {
+ private val doOnLongClick: (view: View, data: Item) -> Unit,
+) : ListAdapter
- (DiffCallback()) {
private var currentAlbum: Album? = null
private var currentAlbumHolder: Highlightable? = null
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
index 804b33561..11b2affbf 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt
@@ -27,8 +27,8 @@ import org.oxycblt.auxio.coil.bindGenreImage
import org.oxycblt.auxio.databinding.ItemDetailBinding
import org.oxycblt.auxio.databinding.ItemGenreSongBinding
import org.oxycblt.auxio.music.ActionHeader
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.bindGenreInfo
import org.oxycblt.auxio.playback.PlaybackViewModel
@@ -45,7 +45,7 @@ class GenreDetailAdapter(
private val playbackModel: PlaybackViewModel,
private val doOnClick: (data: Song) -> Unit,
private val doOnLongClick: (view: View, data: Song) -> Unit
-) : ListAdapter(DiffCallback()) {
+) : ListAdapter
- (DiffCallback()) {
private var currentSong: Song? = null
private var currentHolder: Highlightable? = null
diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt
index 8b5f8ecc3..25a73343c 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/list/HomeListFragment.kt
@@ -26,7 +26,7 @@ import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.home.HomeViewModel
-import org.oxycblt.auxio.music.BaseModel
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.util.applySpans
@@ -48,7 +48,7 @@ abstract class HomeListFragment : Fragment() {
*/
abstract val listPopupProvider: (Int) -> String
- protected fun setupRecycler(
+ protected fun setupRecycler(
@IdRes uniqueId: Int,
homeAdapter: HomeAdapter,
homeData: LiveData
>,
@@ -71,7 +71,7 @@ abstract class HomeListFragment : Fragment() {
}
}
- abstract class HomeAdapter : RecyclerView.Adapter() {
+ abstract class HomeAdapter : RecyclerView.Adapter() {
protected var data = listOf()
@SuppressLint("NotifyDataSetChanged")
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
index d4f7e296c..c42b7a6c7 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
@@ -28,33 +28,37 @@ import androidx.annotation.StringRes
// --- MUSIC MODELS ---
/**
- * The base data object for all music.
- * @property id A unique ID for this object. ***THIS IS NOT A MEDIASTORE ID!**
+ * The template for all items in Auxio.
*/
-sealed class BaseModel {
+sealed class Item {
+ /** A unique ID for this item. ***THIS IS NOT A MEDIASTORE ID!** */
abstract val id: Long
}
/**
- * A [BaseModel] variant that represents a music item.
- * @property name The raw name of this track
+ * A [Item] variant that represents a music item.
+ * @property name
*/
-sealed class Music : BaseModel() {
+sealed class Music : Item() {
+ /** The raw name of this item. */
abstract val name: String
}
/**
* [Music] variant that denotes that this object is a parent of other data objects, such
* as an [Album] or [Artist]
- * @property resolvedName A name resolved from it's raw form to a form suitable to be shown in
- * a ui. Ex. "unknown" would become Unknown Artist, (124) would become its proper genre name, etc.
+ * @property resolvedName
*/
sealed class MusicParent : Music() {
+ /**
+ * A name resolved from it's raw form to a form suitable to be shown in a ui.
+ * Ex. "unknown" would become Unknown Artist, (124) would become its proper genre name, etc.
+ */
abstract val resolvedName: String
}
/**
- * The data object for a song. Inherits [BaseModel].
+ * The data object for a song. Inherits [Item].
*/
data class Song(
override val name: String,
@@ -243,7 +247,7 @@ data class Header(
override val id: Long,
/** The string resource used for the header. */
@StringRes val string: Int
-) : BaseModel()
+) : Item()
/**
* A data object used for an action header. Like [Header], but with a button.
@@ -259,7 +263,7 @@ data class ActionHeader(
@StringRes val desc: Int,
/** A callback for when this item is clicked. */
val onClick: (View) -> Unit,
-) : BaseModel() {
+) : Item() {
// All lambdas are not equal to each-other, so we override equals/hashCode and exclude them.
override fun equals(other: Any?): Boolean {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
index 6c6970662..cfc18f848 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt
@@ -30,8 +30,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.shape.MaterialShapeDrawable
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
import org.oxycblt.auxio.music.ActionHeader
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.ActionHeaderViewHolder
import org.oxycblt.auxio.ui.BaseViewHolder
@@ -50,7 +50,7 @@ import org.oxycblt.auxio.util.stateList
class QueueAdapter(
private val touchHelper: ItemTouchHelper
) : RecyclerView.Adapter() {
- private var data = mutableListOf()
+ private var data = mutableListOf- ()
private var listDiffer = AsyncListDiffer(this, DiffCallback())
override fun getItemCount(): Int = data.size
@@ -89,7 +89,7 @@ class QueueAdapter(
* Submit data using [AsyncListDiffer].
* **Only use this if you have no idea what changes occurred to the data**
*/
- fun submitList(newData: MutableList) {
+ fun submitList(newData: MutableList
- ) {
if (data != newData) {
data = newData
listDiffer.submitList(newData)
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
index b7ea8a1ec..3a7059520 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt
@@ -24,9 +24,9 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.AlbumViewHolder
@@ -43,7 +43,7 @@ import org.oxycblt.auxio.ui.SongViewHolder
class SearchAdapter(
private val doOnClick: (data: Music) -> Unit,
private val doOnLongClick: (view: View, data: Music) -> Unit
-) : ListAdapter(DiffCallback()) {
+) : ListAdapter
- (DiffCallback
- ()) {
override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
index 6c84a9157..2f5445651 100644
--- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt
@@ -25,8 +25,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import org.oxycblt.auxio.R
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicStore
@@ -41,13 +41,13 @@ import java.text.Normalizer
* @author OxygenCobalt
*/
class SearchViewModel : ViewModel() {
- private val mSearchResults = MutableLiveData(listOf())
+ private val mSearchResults = MutableLiveData(listOf
- ())
private var mIsNavigating = false
private var mFilterMode: DisplayMode? = null
private var mLastQuery = ""
/** Current search results from the last [search] call. */
- val searchResults: LiveData
> get() = mSearchResults
+ val searchResults: LiveData> get() = mSearchResults
val isNavigating: Boolean get() = mIsNavigating
val filterMode: DisplayMode? get() = mFilterMode
@@ -81,7 +81,7 @@ class SearchViewModel : ViewModel() {
// Searching can be quite expensive, so get on a co-routine
viewModelScope.launch {
val sort = Sort.ByName(true)
- val results = mutableListOf()
+ val results = mutableListOf- ()
// Note: a filter mode of null means to not filter at all.
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt
index d38095891..222870782 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/ActionMenu.kt
@@ -30,8 +30,8 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.showToast
@@ -39,11 +39,11 @@ import org.oxycblt.auxio.util.showToast
/**
* Extension method for creating and showing a new [ActionMenu].
* @param anchor [View] This should be centered around
- * @param data [BaseModel] this menu corresponds to
+ * @param data [Item] this menu corresponds to
* @param flag (Optional, defaults to [ActionMenu.FLAG_NONE]) Any extra flags to accompany the data.
* @see ActionMenu
*/
-fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_NONE) {
+fun Fragment.newMenu(anchor: View, data: Item, flag: Int = ActionMenu.FLAG_NONE) {
ActionMenu(requireActivity() as AppCompatActivity, anchor, data, flag).show()
}
@@ -51,7 +51,7 @@ fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_
* A wrapper around [PopupMenu] that automates the menu creation for nearly every datatype in Auxio.
* @param activity [AppCompatActivity] required as both a context and ViewModelStore owner.
* @param anchor [View] This should be centered around
- * @param data [BaseModel] this menu corresponds to
+ * @param data [Item] this menu corresponds to
* @param flag Any extra flags to accompany the data. See [FLAG_NONE], [FLAG_IN_ALBUM], [FLAG_IN_ARTIST], [FLAG_IN_GENRE] for more details.
* @throws IllegalStateException When there is no menu for this specific datatype/flag
* @author OxygenCobalt
@@ -59,7 +59,7 @@ fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_
class ActionMenu(
activity: AppCompatActivity,
anchor: View,
- private val data: BaseModel,
+ private val data: Item,
private val flag: Int
) : PopupMenu(activity, anchor) {
private val context = activity.applicationContext
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/DiffCallback.kt b/app/src/main/java/org/oxycblt/auxio/ui/DiffCallback.kt
index 768a3a63d..5ed786da4 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/DiffCallback.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/DiffCallback.kt
@@ -19,14 +19,14 @@
package org.oxycblt.auxio.ui
import androidx.recyclerview.widget.DiffUtil
-import org.oxycblt.auxio.music.BaseModel
+import org.oxycblt.auxio.music.Item
/**
- * A re-usable diff callback for all [BaseModel] implementations.
+ * A re-usable diff callback for all [Item] implementations.
* **Use this instead of creating a DiffCallback for each adapter.**
* @author OxygenCobalt
*/
-class DiffCallback : DiffUtil.ItemCallback() {
+class DiffCallback : DiffUtil.ItemCallback() {
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt b/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt
index 2b700a774..975c23ca1 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/ViewHolders.kt
@@ -32,21 +32,21 @@ import org.oxycblt.auxio.databinding.ItemSongBinding
import org.oxycblt.auxio.music.ActionHeader
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
-import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header
+import org.oxycblt.auxio.music.Item
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.inflater
/**
* A [RecyclerView.ViewHolder] that streamlines a lot of the common things across all viewholders.
- * @param T The datatype, inheriting [BaseModel] for this ViewHolder.
+ * @param T The datatype, inheriting [Item] for this ViewHolder.
* @param binding Basic [ViewDataBinding] required to set up click listeners & sizing.
* @param doOnClick (Optional) Function that calls on a click.
* @param doOnLongClick (Optional) Functions that calls on a long-click.
* @author OxygenCobalt
*/
-abstract class BaseViewHolder(
+abstract class BaseViewHolder(
private val binding: ViewDataBinding,
private val doOnClick: ((data: T) -> Unit)? = null,
private val doOnLongClick: ((view: View, data: T) -> Unit)? = null
@@ -59,7 +59,7 @@ abstract class BaseViewHolder(
}
/**
- * Bind the viewholder with whatever [BaseModel] instance that has been specified.
+ * Bind the viewholder with whatever [Item] instance that has been specified.
* Will call [onBind] on the inheriting ViewHolder.
* @param data Data that the viewholder should be bound with
*/
From 22258a3e6bee2b299c2947b96a836d818ee0ddda Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Tue, 22 Feb 2022 17:44:56 -0700
Subject: [PATCH 19/38] style: add disabled state to switches
Add a disabled state to the M3 switches.
---
CHANGELOG.md | 1 +
app/src/main/res/color/sel_m3_switch_thumb.xml | 1 +
app/src/main/res/color/sel_m3_switch_track.xml | 1 +
3 files changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2a609d7f..4a3c820ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
#### Dev/Meta
- Enabled elevation drop shadows below Android P for consistency
+- Switches now have a disabled state
- Reworked dynamic color usage
- Reworked logging
diff --git a/app/src/main/res/color/sel_m3_switch_thumb.xml b/app/src/main/res/color/sel_m3_switch_thumb.xml
index 61c22f9ad..6dc73cbb1 100644
--- a/app/src/main/res/color/sel_m3_switch_thumb.xml
+++ b/app/src/main/res/color/sel_m3_switch_thumb.xml
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/color/sel_m3_switch_track.xml b/app/src/main/res/color/sel_m3_switch_track.xml
index 5d2e6df2f..c3abdac07 100644
--- a/app/src/main/res/color/sel_m3_switch_track.xml
+++ b/app/src/main/res/color/sel_m3_switch_track.xml
@@ -1,5 +1,6 @@
+
\ No newline at end of file
From 87035805d284a584ed9601d12220d7b0214a8f9c Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Wed, 23 Feb 2022 15:22:41 -0700
Subject: [PATCH 20/38] ui: general cleanup
Clean up a bunch of UI issues that have accumulated.
---
CHANGELOG.md | 2 +-
.../java/org/oxycblt/auxio/accent/Accent.kt | 2 +-
.../auxio/detail/DetailAppBarLayout.kt | 6 +++---
.../home/FloatingActionButtonContainer.kt | 3 ++-
.../home/fastscroll/FastScrollRecyclerView.kt | 3 ++-
.../java/org/oxycblt/auxio/music/Models.kt | 4 ++--
.../oxycblt/auxio/playback/PlaybackBarView.kt | 2 +-
.../oxycblt/auxio/playback/PlaybackButton.kt | 5 +++--
.../oxycblt/auxio/playback/PlaybackSeekBar.kt | 2 +-
.../auxio/settings/pref/IntListPreference.kt | 19 ++++++++++---------
.../org/oxycblt/auxio/ui/EdgeAppBarLayout.kt | 4 ++--
.../oxycblt/auxio/ui/EdgeCoordinatorLayout.kt | 3 ++-
.../org/oxycblt/auxio/ui/EdgeRecyclerView.kt | 3 ++-
.../drawable/ui_large_unbounded_ripple.xml | 2 +-
.../main/res/drawable/ui_unbounded_ripple.xml | 2 +-
.../res/layout-land/fragment_playback.xml | 4 ----
.../layout-sw600dp-land/fragment_playback.xml | 4 ----
.../res/layout-sw600dp/fragment_playback.xml | 4 ----
.../res/layout-sw640dp/view_playback_bar.xml | 3 ---
.../res/layout-w600dp/view_playback_bar.xml | 7 ++-----
app/src/main/res/layout/fragment_about.xml | 8 +-------
app/src/main/res/layout/item_excluded_dir.xml | 4 +++-
app/src/main/res/layout/view_playback_bar.xml | 1 -
app/src/main/res/layout/view_seek_bar.xml | 1 +
app/src/main/res/values-zh-rCN/strings.xml | 2 --
app/src/main/res/values/dimens.xml | 4 +---
.../res/values/{settings.xml => integers.xml} | 2 ++
app/src/main/res/values/styles_ui.xml | 4 ----
.../metadata/android/en-US/changelogs/1.txt | 2 +-
info/ADDITIONS.md | 4 ++--
info/ARCHITECTURE.md | 6 +++---
31 files changed, 50 insertions(+), 72 deletions(-)
rename app/src/main/res/values/{settings.xml => integers.xml} (96%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a3c820ea..2a43bb930 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -204,7 +204,7 @@ to when using gesture navigation
- Fixed issue where the scroll thumb would briefly display on the Songs UI
- Fixed issue where fast scrolling could be triggered outside the bounds of the indicators
- Fixed issue where the wrong playing item would be highlighted if the names were identical
-- Fixed a crash when the thumb was moved above the fast scroller [Backported to 1.3.3, included in this release officially]
+- Fixed a crash when the thumb was moved above the fast scroller [Back-ported to 1.3.3, included in this release officially]
#### Dev/Meta
- Migrated fully to material design
diff --git a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
index 97205cb44..e496f8193 100644
--- a/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
+++ b/app/src/main/java/org/oxycblt/auxio/accent/Accent.kt
@@ -101,7 +101,7 @@ private val ACCENT_PRIMARY_COLORS = arrayOf(
/**
* The data object for an accent. In the UI this is known as a "Color Scheme."
* This can be nominally used to gleam some attributes about a given color scheme, but this
- * is not recommended. Attributes are usually the better option in nearly all cases.
+ * is not recommended. Attributes are the better option in nearly all cases.
*
* @property name The name of this accent
* @property theme The theme resource for this accent
diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
index 6a073aff6..954215565 100644
--- a/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailAppBarLayout.kt
@@ -5,7 +5,7 @@ import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
-import androidx.annotation.StyleRes
+import androidx.annotation.AttrRes
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
@@ -28,7 +28,7 @@ import java.lang.Exception
class DetailAppBarLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- @StyleRes defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : EdgeAppBarLayout(context, attrs, defStyleAttr) {
private var mTitleView: AppCompatTextView? = null
private var mRecycler: RecyclerView? = null
@@ -109,7 +109,7 @@ class DetailAppBarLayout @JvmOverloads constructor(
titleView?.alpha = it.animatedValue as Float
}
- duration = resources.getInteger(R.integer.app_bar_elevation_anim_duration).toLong()
+ duration = resources.getInteger(R.integer.detail_app_bar_title_anim_duration).toLong()
start()
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt b/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt
index 863306dc9..9f38464dd 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt
@@ -22,6 +22,7 @@ import android.content.Context
import android.util.AttributeSet
import android.view.WindowInsets
import android.widget.FrameLayout
+import androidx.annotation.AttrRes
import androidx.core.view.updatePadding
import org.oxycblt.auxio.util.systemBarInsetsCompat
@@ -32,7 +33,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
class FloatingActionButtonContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
init {
clipToPadding = false
diff --git a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt
index 77a1ec5e2..bd08de937 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/FastScrollRecyclerView.kt
@@ -32,6 +32,7 @@ import android.view.ViewGroup
import android.view.WindowInsets
import android.widget.FrameLayout
import android.widget.TextView
+import androidx.annotation.AttrRes
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.math.MathUtils
import androidx.core.view.isInvisible
@@ -77,7 +78,7 @@ import kotlin.math.abs
class FastScrollRecyclerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
/** Callback to provide a string to be shown on the popup when an item is passed */
var popupProvider: ((Int) -> String)? = null
diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
index c42b7a6c7..1e3f80386 100644
--- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt
+++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt
@@ -28,7 +28,7 @@ import androidx.annotation.StringRes
// --- MUSIC MODELS ---
/**
- * The template for all items in Auxio.
+ * The base for all items in Auxio.
*/
sealed class Item {
/** A unique ID for this item. ***THIS IS NOT A MEDIASTORE ID!** */
@@ -36,7 +36,7 @@ sealed class Item {
}
/**
- * A [Item] variant that represents a music item.
+ * [Item] variant that represents a music item.
* @property name
*/
sealed class Music : Item() {
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarView.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarView.kt
index 695170e97..caf863c05 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarView.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackBarView.kt
@@ -41,7 +41,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
class PlaybackBarView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val binding = ViewPlaybackBarBinding.inflate(context.inflater, this, true)
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
index 523f2a2b6..b05777b81 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackButton.kt
@@ -5,6 +5,7 @@ import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.RectF
import android.util.AttributeSet
+import androidx.annotation.AttrRes
import androidx.appcompat.widget.AppCompatImageButton
import org.oxycblt.auxio.R
import org.oxycblt.auxio.util.getDimenSizeSafe
@@ -25,7 +26,7 @@ import org.oxycblt.auxio.util.getDrawableSafe
class PlaybackButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : AppCompatImageButton(context, attrs, defStyleAttr) {
private val iconSize = context.getDimenSizeSafe(R.dimen.size_playback_icon)
private val centerMatrix = Matrix()
@@ -33,7 +34,7 @@ class PlaybackButton @JvmOverloads constructor(
private val matrixDst = RectF()
private val indicatorDrawable = context.getDrawableSafe(R.drawable.ui_indicator)
- var hasIndicator = false
+ private var hasIndicator = false
set(value) {
field = value
invalidate()
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
index 274995005..4fae1f8e0 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSeekBar.kt
@@ -42,7 +42,7 @@ import org.oxycblt.auxio.util.stateList
class PlaybackSeekBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleRes: Int = -1
+ defStyleRes: Int = 0
) : ConstraintLayout(context, attrs, defStyleRes), Slider.OnChangeListener, Slider.OnSliderTouchListener {
private val binding = ViewSeekBarBinding.inflate(context.inflater, this, true)
private val isSeeking: Boolean get() = binding.playbackDurationCurrent.isActivated
diff --git a/app/src/main/java/org/oxycblt/auxio/settings/pref/IntListPreference.kt b/app/src/main/java/org/oxycblt/auxio/settings/pref/IntListPreference.kt
index 6b163b8c2..78bdc08b4 100644
--- a/app/src/main/java/org/oxycblt/auxio/settings/pref/IntListPreference.kt
+++ b/app/src/main/java/org/oxycblt/auxio/settings/pref/IntListPreference.kt
@@ -18,25 +18,28 @@
package org.oxycblt.auxio.settings.pref
-import android.annotation.SuppressLint
import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import androidx.preference.DialogPreference
+import androidx.preference.Preference
import org.oxycblt.auxio.R
-import androidx.preference.R as prefR
class IntListPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = prefR.attr.dialogPreferenceStyle,
+ defStyleAttr: Int = androidx.preference.R.attr.dialogPreferenceStyle,
defStyleRes: Int = 0
) : DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
+ // Reflect into Preference to get the (normally inaccessible) default value.
+ private val defValueField = Preference::class.java.getDeclaredField("mDefaultValue").apply {
+ isAccessible = true
+ }
+
val entries: Array
val values: IntArray
-
private var currentValue: Int? = null
- private val defValue: Int
+ private val defValue: Int get() = defValueField.get(this) as Int
init {
val prefAttrs = context.obtainStyledAttributes(
@@ -49,8 +52,6 @@ class IntListPreference @JvmOverloads constructor(
prefAttrs.getResourceId(R.styleable.IntListPreference_entryValues, -1)
)
- defValue = prefAttrs.getInt(prefR.styleable.Preference_defaultValue, Int.MIN_VALUE)
-
prefAttrs.recycle()
summaryProvider = IntListSummaryProvider()
@@ -96,7 +97,6 @@ class IntListPreference @JvmOverloads constructor(
}
}
- @SuppressLint("PrivateResource")
private inner class IntListSummaryProvider : SummaryProvider {
override fun provideSummary(preference: IntListPreference): CharSequence {
val index = getValueIndex()
@@ -105,7 +105,8 @@ class IntListPreference @JvmOverloads constructor(
return entries[index]
}
- return context.getString(prefR.string.not_set)
+ // Usually an invalid state, don't bother translating
+ return ""
}
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
index 8ecaf814a..f30302ca0 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/EdgeAppBarLayout.kt
@@ -24,7 +24,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.WindowInsets
-import androidx.annotation.StyleRes
+import androidx.annotation.AttrRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updatePadding
@@ -41,7 +41,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
open class EdgeAppBarLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- @StyleRes defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : AppBarLayout(context, attrs, defStyleAttr) {
private var scrollingChild: View? = null
private val tConsumed = IntArray(2)
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/EdgeCoordinatorLayout.kt b/app/src/main/java/org/oxycblt/auxio/ui/EdgeCoordinatorLayout.kt
index 7e41af3fe..84341f7be 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/EdgeCoordinatorLayout.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/EdgeCoordinatorLayout.kt
@@ -21,6 +21,7 @@ package org.oxycblt.auxio.ui
import android.content.Context
import android.util.AttributeSet
import android.view.WindowInsets
+import androidx.annotation.AttrRes
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.children
@@ -33,7 +34,7 @@ import androidx.core.view.children
class EdgeCoordinatorLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : CoordinatorLayout(context, attrs, defStyleAttr) {
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
for (child in children) {
diff --git a/app/src/main/java/org/oxycblt/auxio/ui/EdgeRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/ui/EdgeRecyclerView.kt
index 8f8fc4b1d..3a273a55b 100644
--- a/app/src/main/java/org/oxycblt/auxio/ui/EdgeRecyclerView.kt
+++ b/app/src/main/java/org/oxycblt/auxio/ui/EdgeRecyclerView.kt
@@ -21,6 +21,7 @@ package org.oxycblt.auxio.ui
import android.content.Context
import android.util.AttributeSet
import android.view.WindowInsets
+import androidx.annotation.AttrRes
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.util.systemBarInsetsCompat
@@ -31,7 +32,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
class EdgeRecyclerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
- defStyleAttr: Int = -1
+ @AttrRes defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
diff --git a/app/src/main/res/drawable/ui_large_unbounded_ripple.xml b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
index 3c999556f..8daf5be74 100644
--- a/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_large_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="24dp" />
diff --git a/app/src/main/res/drawable/ui_unbounded_ripple.xml b/app/src/main/res/drawable/ui_unbounded_ripple.xml
index 3118661f4..0b98cc5db 100644
--- a/app/src/main/res/drawable/ui_unbounded_ripple.xml
+++ b/app/src/main/res/drawable/ui_unbounded_ripple.xml
@@ -1,4 +1,4 @@
+ android:radius="20dp" />
diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml
index 6a5d9cee3..eee455511 100644
--- a/app/src/main/res/layout-land/fragment_playback.xml
+++ b/app/src/main/res/layout-land/fragment_playback.xml
@@ -122,7 +122,6 @@
-
-
+ android:layout_margin="@dimen/spacing_medium">
已加载 %d 首曲目
- - %d 首歌曲
- "%d 首歌曲"
- - %d 张专辑
- "%d 张专辑"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 8b2794cad..f29dcde96 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,6 +1,7 @@
+ 4dp
8dp
16dp
24dp
@@ -16,9 +17,6 @@
192dp
256dp
- 20dp
- 24dp
-
32dp
32dp
diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/integers.xml
similarity index 96%
rename from app/src/main/res/values/settings.xml
rename to app/src/main/res/values/integers.xml
index 691e1e77a..ef9360ddf 100644
--- a/app/src/main/res/values/settings.xml
+++ b/app/src/main/res/values/integers.xml
@@ -1,5 +1,7 @@
+ 150
+
- @string/set_theme_auto
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index 9f41449fb..95c53941b 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -151,10 +151,6 @@
-
-
diff --git a/fastlane/metadata/android/en-US/changelogs/1.txt b/fastlane/metadata/android/en-US/changelogs/1.txt
index a443dae23..79589920f 100644
--- a/fastlane/metadata/android/en-US/changelogs/1.txt
+++ b/fastlane/metadata/android/en-US/changelogs/1.txt
@@ -1,5 +1,5 @@
- Redesigned the detail UIs
-- Navigation is much more fluid and straightfoward
+- Navigation is much more fluid and straightforward
- Search has been moved to a dedicated tab
- Added search filtering
- Fixed issue where audio focus would resume playback when it shouldn't
diff --git a/info/ADDITIONS.md b/info/ADDITIONS.md
index 3681bdbcf..68b127a22 100644
--- a/info/ADDITIONS.md
+++ b/info/ADDITIONS.md
@@ -7,7 +7,7 @@ These will likely be accepted as long as they do not cause too much harm to the
## New Customizations/Options
While I do like adding new behavior/UI customizations, these will be looked at more closely as certain additions can cause harm to the apps UI/UX while not providing alot of benefit. These tend to be accepted however.
-## Feature Addtions and UI Changes
+## Feature Additions and UI Changes
These arent as likely to be accepted. As I said, I do not want Auxio to become overly bloated with features that are rarely used, therefore I only tend to accept features that:
- Benefit **my own** usage
@@ -22,6 +22,6 @@ Feel free to fork Auxio to add your own feature set however.
- Recently added list [#18] (Out of scope)
- Lyrics [#19] (Out of scope)
- Tag editing [#33] (Out of scope)
-- Gapless Playback [#35] (Technical issues)
+- Gapless Playback [#35] (Technical issues, may change in the future)
- Reduce leading instrument [#45] (Technical issues, Out of scope)
- Opening music through a provider [#30] (Out of scope)
diff --git a/info/ARCHITECTURE.md b/info/ARCHITECTURE.md
index 5ef8a9bb6..16248789f 100644
--- a/info/ARCHITECTURE.md
+++ b/info/ARCHITECTURE.md
@@ -52,13 +52,13 @@ is separated into three phases:
- Set up the UI
- Set up ViewModel instances and LiveData observers
-`findViewById` is to **only** be used when interfacing with non-Auxio views. Otherwise, viewbinding should be
-used in all cases. If one needs to keep track of a viewbinding outside of `onCreateView`, then one can declare
+`findViewById` is to **only** be used when interfacing with non-Auxio views. Otherwise, view-binding should be
+used in all cases. If one needs to keep track of a view-binding outside of `onCreateView`, then one can declare
a binding `by memberBinding(BindingClass::inflate)` in order to have a binding that properly disposes itself
on lifecycle events.
At times it may be more appropriate to use a `View` instead of a full blown fragment. This is okay as long as
-viewbinding is still used.
+view-binding is still used.
When creating a ViewHolder for a `RecyclerView`, one should use `BaseViewHolder` to standardize the binding process
and automate some code shared across all ViewHolders. The only exceptions to this case are for ViewHolders that
From 280b582efae96a3f58c28f7ecbbcab90972d6366 Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Wed, 23 Feb 2022 16:13:59 -0700
Subject: [PATCH 21/38] home: rework fab adaptiveness
Rework how the fab's adaptive functionality works.
Handle fab adaptiveness by using a style instead of a fully custom
view. This helps eliminate a usage of private resources.
---
.../home/AdaptiveFloatingActionButton.kt | 40 -------------------
...ButtonContainer.kt => EdgeFabContainer.kt} | 3 +-
.../org/oxycblt/auxio/home/HomeFragment.kt | 14 +++----
app/src/main/res/layout/fragment_home.xml | 11 +++--
.../main/res/layout/view_shuffle_button.xml | 19 +++++++++
app/src/main/res/values-sw640dp/styles_ui.xml | 6 +++
app/src/main/res/values/styles_ui.xml | 7 +++-
build.gradle | 2 +-
8 files changed, 44 insertions(+), 58 deletions(-)
delete mode 100644 app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
rename app/src/main/java/org/oxycblt/auxio/home/{FloatingActionButtonContainer.kt => EdgeFabContainer.kt} (96%)
create mode 100644 app/src/main/res/layout/view_shuffle_button.xml
create mode 100644 app/src/main/res/values-sw640dp/styles_ui.xml
diff --git a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt b/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
deleted file mode 100644
index f6b405332..000000000
--- a/app/src/main/java/org/oxycblt/auxio/home/AdaptiveFloatingActionButton.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.oxycblt.auxio.home
-
-import android.content.Context
-import android.util.AttributeSet
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import org.oxycblt.auxio.util.getDimenSizeSafe
-import org.oxycblt.auxio.util.logD
-import com.google.android.material.R as MaterialR
-
-/**
- * A FloatingActionButton that automatically switches to a normal or large FAB depending on the
- * screen size.
- */
-@Suppress("PrivateResource")
-class AdaptiveFloatingActionButton @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttr: Int = MaterialR.style.Widget_Material3_FloatingActionButton_Primary
-) : FloatingActionButton(context, attrs, defStyleAttr) {
-
- init {
- size = SIZE_NORMAL
-
- // Use a large FAB on large screens, as it makes it easier to touch.
- if (resources.configuration.smallestScreenWidthDp >= 640) {
- logD("Using large FAB configuration")
-
- val largeFabSize = context.getDimenSizeSafe(
- MaterialR.dimen.m3_large_fab_size
- )
-
- val largeImageSize = context.getDimenSizeSafe(
- MaterialR.dimen.m3_large_fab_max_image_size
- )
-
- customSize = largeFabSize
- setMaxImageSize(largeImageSize)
- }
- }
-}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt b/app/src/main/java/org/oxycblt/auxio/home/EdgeFabContainer.kt
similarity index 96%
rename from app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt
rename to app/src/main/java/org/oxycblt/auxio/home/EdgeFabContainer.kt
index 9f38464dd..8828c62f6 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/FloatingActionButtonContainer.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/EdgeFabContainer.kt
@@ -30,7 +30,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
* A container for a FloatingActionButton that enables edge-to-edge support.
* @author OxygenCobalt
*/
-class FloatingActionButtonContainer @JvmOverloads constructor(
+class EdgeFabContainer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = 0
@@ -45,7 +45,6 @@ class FloatingActionButtonContainer @JvmOverloads constructor(
override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {
updatePadding(bottom = insets.systemBarInsetsCompat.bottom)
-
return insets
}
}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
index b5b447ad4..f5fe3f216 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
@@ -159,11 +159,9 @@ class HomeFragment : Fragment() {
).attach()
}
- binding.homeFab.setOnClickListener {
- playbackModel.shuffleAll()
- }
+ binding.homeShuffleFab.setup(playbackModel)
- // --- VIEWMODEL SETUP ---
+// --- VIEWMODEL SETUP ---
// There is no way a fast scrolling event can continue across a re-create. Reset it.
homeModel.updateFastScrolling(false)
@@ -171,12 +169,12 @@ class HomeFragment : Fragment() {
musicModel.loaderResponse.observe(viewLifecycleOwner) { response ->
// Handle the loader response.
when (response) {
- is MusicStore.Response.Ok -> binding.homeFab.show()
+ is MusicStore.Response.Ok -> binding.homeShuffleFab.show()
// While loading or during an error, make sure we keep the shuffle fab hidden so
// that any kind of playback is impossible. PlaybackStateManager also relies on this
// invariant, so please don't change it.
- else -> binding.homeFab.hide()
+ else -> binding.homeShuffleFab.hide()
}
}
@@ -188,9 +186,9 @@ class HomeFragment : Fragment() {
}
if (scrolling) {
- binding.homeFab.hide()
+ binding.homeShuffleFab.hide()
} else {
- binding.homeFab.show()
+ binding.homeShuffleFab.show()
}
}
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 4bb06bb87..2bab4386f 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -40,24 +40,23 @@
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
tools:layout="@layout/fragment_home_list" />
-
-
+ android:src="@drawable/ic_shuffle" />
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_shuffle_button.xml b/app/src/main/res/layout/view_shuffle_button.xml
new file mode 100644
index 000000000..2065d961c
--- /dev/null
+++ b/app/src/main/res/layout/view_shuffle_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-sw640dp/styles_ui.xml b/app/src/main/res/values-sw640dp/styles_ui.xml
new file mode 100644
index 000000000..f72e10d34
--- /dev/null
+++ b/app/src/main/res/values-sw640dp/styles_ui.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index 95c53941b..ae84084c5 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -167,9 +167,14 @@
compensate.
2. For some reason elevation behaves strangely in the playback panel, so we disable it.
-->
- - @dimen/size_playback_icon
+ - normal
- @dimen/size_btn_large
+ - @dimen/size_playback_icon
- 0dp
- 0dp
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7436375ca..773a522aa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.1.1'
+ classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
From 61dbfe31857a7d83688e36e1f3d27af4792e966b Mon Sep 17 00:00:00 2001
From: OxygenCobalt
Date: Wed, 23 Feb 2022 17:36:17 -0700
Subject: [PATCH 22/38] coil: make rounded images more nuanced
Create a custom view for rounded images, making them more nuanced in
the process.
The previous method for applying rounded images in-app was generally
clunky and fragile. Introduce a new custom view that actually takes a
cornerRadius attribute from the ImageView itself that then applies it
whenever the user enables the setting. This also allows rounded images
to be more nuanced, as typical 8dp elevation can be used for small
views and a more fitting 16dp radius can be used for large views.
---
CHANGELOG.md | 1 +
.../java/org/oxycblt/auxio/MainFragment.kt | 4 --
.../java/org/oxycblt/auxio/coil/CoilUtils.kt | 16 --------
.../oxycblt/auxio/coil/RoundableImageView.kt | 41 +++++++++++++++++++
.../org/oxycblt/auxio/home/HomeFragment.kt | 12 +++---
.../main/res/drawable/ui_rounded_cutout.xml | 5 ---
.../main/res/layout-h600dp/item_detail.xml | 2 +-
.../res/layout-land/fragment_playback.xml | 2 +-
app/src/main/res/layout-land/item_detail.xml | 2 +-
.../layout-sw600dp-land/fragment_playback.xml | 2 +-
.../res/layout-sw600dp/fragment_playback.xml | 2 +-
.../main/res/layout-sw600dp/item_detail.xml | 2 +-
.../res/layout-sw640dp/view_playback_bar.xml | 2 +-
.../main/res/layout-sw840dp/item_detail.xml | 2 +-
.../layout-w600dp-land/fragment_playback.xml | 2 +-
.../res/layout-w600dp/view_playback_bar.xml | 2 +-
app/src/main/res/layout/fragment_playback.xml | 2 +-
app/src/main/res/layout/item_album.xml | 2 +-
app/src/main/res/layout/item_album_song.xml | 1 -
app/src/main/res/layout/item_artist.xml | 2 +-
app/src/main/res/layout/item_artist_album.xml | 2 +-
app/src/main/res/layout/item_artist_song.xml | 2 +-
app/src/main/res/layout/item_detail.xml | 2 +-
app/src/main/res/layout/item_genre.xml | 2 +-
app/src/main/res/layout/item_genre_song.xml | 2 +-
app/src/main/res/layout/item_queue_song.xml | 2 +-
app/src/main/res/layout/item_song.xml | 2 +-
app/src/main/res/layout/view_playback_bar.xml | 2 +-
app/src/main/res/values/attrs.xml | 4 ++
app/src/main/res/values/dimens.xml | 4 +-
app/src/main/res/values/styles_ui.xml | 6 +++
31 files changed, 83 insertions(+), 53 deletions(-)
create mode 100644 app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt
delete mode 100644 app/src/main/res/drawable/ui_rounded_cutout.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a43bb930..7e5b20bdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## dev [v2.2.2 or 2.3.0]
#### What's Improved
+- Rounded images are more nuanced
- Shuffle and Repeat mode buttons now have more contrast when they are turned on
#### What's Changed
diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
index a19d45c0a..b61c315c8 100644
--- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt
@@ -79,10 +79,6 @@ class MainFragment : Fragment() {
// but for some insane reason google decided to cripple the window APIs one could use
// to limit it's size. So, we just have our own special layout that is shown whenever
// the screen is too small because of course we have to.
- // Another fun fact: smallestScreenWidthDp is completely bugged and uses the total
- // screen size, even when the window is smaller. This basically borks split screen
- // even more than it already does. Fun!
-
if (requireActivity().isInMultiWindowMode) {
val config = resources.configuration
diff --git a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt
index 85464857d..c65aff867 100644
--- a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt
+++ b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt
@@ -35,7 +35,6 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.settings.SettingsManager
// --- BINDING ADAPTERS ---
@@ -66,21 +65,6 @@ fun ImageView.bindGenreImage(genre: Genre?) = load(genre, R.drawable.ic_genre)
fun ImageView.load(music: T?, @DrawableRes error: Int) {
dispose()
- // We don't round album covers by default as it desecrates album artwork, but we do provide
- // an option if one wants it.
- // As for why we use clipToOutline instead of coils RoundedCornersTransformation, the radii
- // of an image's corners is dependent on the actual dimensions of the image, which would force
- // us to resize all images to a fixed size. clipToOutline is pretty much always cheaper as long
- // as we have a perfectly-square image.
- val settingsManager = SettingsManager.getInstance()
- if (settingsManager.roundCovers && background == null) {
- setBackgroundResource(R.drawable.ui_rounded_cutout)
- clipToOutline = true
- } else if (!settingsManager.roundCovers && background != null) {
- background = null
- clipToOutline = false
- }
-
load(music) {
error(error)
transformations(SquareFrameTransform())
diff --git a/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt b/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt
new file mode 100644
index 000000000..61ff16ac3
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/coil/RoundableImageView.kt
@@ -0,0 +1,41 @@
+package org.oxycblt.auxio.coil
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.annotation.AttrRes
+import androidx.appcompat.widget.AppCompatImageView
+import com.google.android.material.shape.MaterialShapeDrawable
+import org.oxycblt.auxio.R
+import org.oxycblt.auxio.settings.SettingsManager
+import org.oxycblt.auxio.util.getColorSafe
+import org.oxycblt.auxio.util.stateList
+
+class RoundableImageView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ @AttrRes defStyleAttr: Int = 0
+) : AppCompatImageView(context, attrs, defStyleAttr) {
+ init {
+ val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.RoundableImageView)
+ val cornerRadius = styledAttrs.getDimension(R.styleable.RoundableImageView_cornerRadius, 0.0f)
+ styledAttrs.recycle()
+
+ background = MaterialShapeDrawable().apply {
+ setCornerSize(cornerRadius)
+ fillColor = context.getColorSafe(android.R.color.transparent).stateList
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+
+ // We don't round album covers by default as it desecrates album artwork, but we do
+ // provide an option if one wants it.
+ // As for why we use clipToOutline instead of coils RoundedCornersTransformation, the radii
+ // of an image's corners is dependent on the actual dimensions of the image, which would
+ // force us to resize all images to a fixed size. clipToOutline is pretty much always
+ // cheaper as long as we have a perfectly-square image.
+ val settingsManager = SettingsManager.getInstance()
+ clipToOutline = settingsManager.roundCovers
+ }
+}
diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
index f5fe3f216..c89ccb972 100644
--- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
@@ -159,7 +159,9 @@ class HomeFragment : Fragment() {
).attach()
}
- binding.homeShuffleFab.setup(playbackModel)
+ binding.homeFab.setOnClickListener {
+ playbackModel.shuffleAll()
+ }
// --- VIEWMODEL SETUP ---
@@ -169,12 +171,12 @@ class HomeFragment : Fragment() {
musicModel.loaderResponse.observe(viewLifecycleOwner) { response ->
// Handle the loader response.
when (response) {
- is MusicStore.Response.Ok -> binding.homeShuffleFab.show()
+ is MusicStore.Response.Ok -> binding.homeFab.show()
// While loading or during an error, make sure we keep the shuffle fab hidden so
// that any kind of playback is impossible. PlaybackStateManager also relies on this
// invariant, so please don't change it.
- else -> binding.homeShuffleFab.hide()
+ else -> binding.homeFab.hide()
}
}
@@ -186,9 +188,9 @@ class HomeFragment : Fragment() {
}
if (scrolling) {
- binding.homeShuffleFab.hide()
+ binding.homeFab.hide()
} else {
- binding.homeShuffleFab.show()
+ binding.homeFab.show()
}
}
diff --git a/app/src/main/res/drawable/ui_rounded_cutout.xml b/app/src/main/res/drawable/ui_rounded_cutout.xml
deleted file mode 100644
index 00d969fd3..000000000
--- a/app/src/main/res/drawable/ui_rounded_cutout.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout-h600dp/item_detail.xml b/app/src/main/res/layout-h600dp/item_detail.xml
index 21e2f021a..02a1eff41 100644
--- a/app/src/main/res/layout-h600dp/item_detail.xml
+++ b/app/src/main/res/layout-h600dp/item_detail.xml
@@ -10,7 +10,7 @@
android:layout_height="match_parent"
android:padding="@dimen/spacing_medium">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index f29dcde96..270cf530d 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,7 +1,6 @@
- 4dp
8dp
16dp
24dp
@@ -17,6 +16,9 @@
192dp
256dp
+ 8dp
+ 16dp
+
32dp
32dp
diff --git a/app/src/main/res/values/styles_ui.xml b/app/src/main/res/values/styles_ui.xml
index ae84084c5..413acece4 100644
--- a/app/src/main/res/values/styles_ui.xml
+++ b/app/src/main/res/values/styles_ui.xml
@@ -26,32 +26,38 @@