music: add artist count to genres
Add an artist count to genres. This should make genres more unique from future playlists. A list of artists will be added later on.
This commit is contained in:
parent
7c913708f2
commit
42aba9e556
10 changed files with 33 additions and 19 deletions
|
@ -70,8 +70,8 @@ dependencies {
|
||||||
// 1.4.0 is used in order to avoid a ripple bug in material components
|
// 1.4.0 is used in order to avoid a ripple bug in material components
|
||||||
implementation "androidx.appcompat:appcompat:1.4.0"
|
implementation "androidx.appcompat:appcompat:1.4.0"
|
||||||
implementation "androidx.core:core-ktx:1.9.0"
|
implementation "androidx.core:core-ktx:1.9.0"
|
||||||
implementation "androidx.activity:activity-ktx:1.6.0"
|
implementation "androidx.activity:activity-ktx:1.6.1"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.5.3"
|
implementation "androidx.fragment:fragment-ktx:1.5.4"
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||||
|
|
|
@ -76,9 +76,6 @@ object IntegerTable {
|
||||||
/** RepeatMode.TRACK */
|
/** RepeatMode.TRACK */
|
||||||
const val REPEAT_MODE_TRACK = 0xA102
|
const val REPEAT_MODE_TRACK = 0xA102
|
||||||
|
|
||||||
/** PlaybackMode.IN_GENRE */
|
|
||||||
const val PLAYBACK_MODE_IN_GENRE = 0xA103
|
|
||||||
|
|
||||||
/** PlaybackMode.IN_ARTIST */
|
/** PlaybackMode.IN_ARTIST */
|
||||||
const val PLAYBACK_MODE_IN_ARTIST = 0xA104
|
const val PLAYBACK_MODE_IN_ARTIST = 0xA104
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,8 @@ private class GenreDetailViewHolder private constructor(private val binding: Ite
|
||||||
binding.detailSubhead.isVisible = false
|
binding.detailSubhead.isVisible = false
|
||||||
binding.detailInfo.text = binding.context.getString(
|
binding.detailInfo.text = binding.context.getString(
|
||||||
R.string.fmt_two,
|
R.string.fmt_two,
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size),
|
binding.context.getPlural(R.plurals.fmt_artist_count, item.artists.size),
|
||||||
item.durationMs.formatDurationMs(false)
|
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.detailPlayButton.setOnClickListener { listener.onPlayParent() }
|
binding.detailPlayButton.setOnClickListener { listener.onPlayParent() }
|
||||||
|
|
|
@ -110,7 +110,6 @@ private constructor(
|
||||||
) : BaseFetcher() {
|
) : BaseFetcher() {
|
||||||
override suspend fun fetch(): FetchResult? {
|
override suspend fun fetch(): FetchResult? {
|
||||||
val results = genre.albums.mapAtMost(4) { fetchCover(context, it) }
|
val results = genre.albums.mapAtMost(4) { fetchCover(context, it) }
|
||||||
|
|
||||||
return createMosaic(context, results, size)
|
return createMosaic(context, results, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -698,13 +698,18 @@ class Genre constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
||||||
/** The albums of this genre. */
|
/** The albums of this genre. */
|
||||||
val albums: List<Album>
|
val albums: List<Album>
|
||||||
|
|
||||||
|
/** The artists of this genre. */
|
||||||
|
val artists: List<Artist>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
var totalDuration = 0L
|
var totalDuration = 0L
|
||||||
val distinctAlbums = mutableSetOf<Album>()
|
val distinctAlbums = mutableSetOf<Album>()
|
||||||
|
val distinctArtists = mutableSetOf<Artist>()
|
||||||
|
|
||||||
for (song in songs) {
|
for (song in songs) {
|
||||||
song._link(this)
|
song._link(this)
|
||||||
distinctAlbums.add(song.album)
|
distinctAlbums.add(song.album)
|
||||||
|
distinctArtists.addAll(song.artists)
|
||||||
totalDuration += song.durationMs
|
totalDuration += song.durationMs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,6 +719,8 @@ class Genre constructor(raw: Raw, override val songs: List<Song>) : MusicParent(
|
||||||
.sortedByDescending { album ->
|
.sortedByDescending { album ->
|
||||||
album.songs.count { it.genres.contains(this) }
|
album.songs.count { it.genres.contains(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
artists = Sort(Sort.Mode.ByName, true).artists(distinctArtists)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun _finalize() {
|
override fun _finalize() {
|
||||||
|
|
|
@ -41,7 +41,7 @@ import kotlin.math.min
|
||||||
*
|
*
|
||||||
* Unlike a typical Date within the standard library, this class just represents the ID3v2/Vorbis
|
* Unlike a typical Date within the standard library, this class just represents the ID3v2/Vorbis
|
||||||
* date format, which is largely assumed to be a subset of ISO-8601. No validation outside of format
|
* date format, which is largely assumed to be a subset of ISO-8601. No validation outside of format
|
||||||
* validation is done.
|
* validation is done, and any date calculation is (fallibly) performed when displayed in the UI.
|
||||||
*
|
*
|
||||||
* The reasoning behind Date is that Auxio cannot trust any kind of metadata date to actually make
|
* The reasoning behind Date is that Auxio cannot trust any kind of metadata date to actually make
|
||||||
* sense in a calendar, due to bad tagging, locale-specific issues, or simply from the limited
|
* sense in a calendar, due to bad tagging, locale-specific issues, or simply from the limited
|
||||||
|
@ -52,9 +52,6 @@ import kotlin.math.min
|
||||||
* The string representation of a Date is RFC 3339, with granular position depending on the presence
|
* The string representation of a Date is RFC 3339, with granular position depending on the presence
|
||||||
* of particular tokens.
|
* of particular tokens.
|
||||||
*
|
*
|
||||||
* Please, **Do not use this for anything important related to time.** I cannot stress this enough.
|
|
||||||
* This code will blow up if you try to do that.
|
|
||||||
*
|
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class Date private constructor(private val tokens: List<Int>) : Comparable<Date> {
|
class Date private constructor(private val tokens: List<Int>) : Comparable<Date> {
|
||||||
|
@ -83,10 +80,15 @@ class Date private constructor(private val tokens: List<Int>) : Comparable<Date>
|
||||||
|
|
||||||
private val second = tokens.getOrNull(5)
|
private val second = tokens.getOrNull(5)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve this date into a string. This could result in a year string formatted
|
||||||
|
* as "YYYY", or a month and year string formatted as "MMM YYYY" depending on the
|
||||||
|
* situation.
|
||||||
|
*/
|
||||||
fun resolveDate(context: Context): String {
|
fun resolveDate(context: Context): String {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
return try {
|
return try {
|
||||||
resolveFullDate(context).also { logD(it) }
|
resolveFullDate(context)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logE("Failed to format a full date")
|
logE("Failed to format a full date")
|
||||||
logE(e.stackTraceToString())
|
logE(e.stackTraceToString())
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.oxycblt.auxio.util.systemGestureInsetsCompat
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a reasonable enough skeleton around BottomSheetBehavior (Excluding auxio extensions in
|
* Implements a reasonable enough skeleton around BottomSheetBehavior (Excluding auxio extensions in
|
||||||
* the vendored code because I course I have to) for normal use without absurd bugs.
|
* the vendored code because of course I have to) for normal use without absurd bugs.
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
|
abstract class AuxioSheetBehavior<V : View>(context: Context, attributeSet: AttributeSet?) :
|
||||||
|
|
|
@ -169,7 +169,11 @@ class GenreViewHolder private constructor(private val binding: ItemParentBinding
|
||||||
binding.parentImage.bind(item)
|
binding.parentImage.bind(item)
|
||||||
binding.parentName.text = item.resolveName(binding.context)
|
binding.parentName.text = item.resolveName(binding.context)
|
||||||
binding.parentInfo.text =
|
binding.parentInfo.text =
|
||||||
|
binding.context.getString(
|
||||||
|
R.string.fmt_two,
|
||||||
|
binding.context.getPlural(R.plurals.fmt_artist_count, item.artists.size),
|
||||||
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
binding.context.getPlural(R.plurals.fmt_song_count, item.songs.size)
|
||||||
|
)
|
||||||
// binding.parentMenu.setOnClickListener { listener.onOpenMenu(item, it) }
|
// binding.parentMenu.setOnClickListener { listener.onOpenMenu(item, it) }
|
||||||
binding.root.setOnLongClickListener {
|
binding.root.setOnLongClickListener {
|
||||||
listener.onOpenMenu(item, it)
|
listener.onOpenMenu(item, it)
|
||||||
|
|
|
@ -361,4 +361,9 @@
|
||||||
<item quantity="one">%d album</item>
|
<item quantity="one">%d album</item>
|
||||||
<item quantity="other">%d albums</item>
|
<item quantity="other">%d albums</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="fmt_artist_count">
|
||||||
|
<item quantity="one">%d artist</item>
|
||||||
|
<item quantity="other">%d artists</item>
|
||||||
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.7.20'
|
ext.kotlin_version = '1.7.20'
|
||||||
ext.navigation_version = "2.5.2"
|
ext.navigation_version = "2.5.3"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|
Loading…
Reference in a new issue