Update Music Loading
Slightly tweak the music models, use plural strings when formatting args, and properly sort the music items after everything else is sorted.
This commit is contained in:
parent
565d1efa96
commit
016d664e51
16 changed files with 68 additions and 79 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
.gradle
|
.gradle
|
||||||
local.properties
|
local.properties
|
||||||
build/
|
build/
|
||||||
|
release/
|
||||||
|
|
||||||
# Studio
|
# Studio
|
||||||
.idea/
|
.idea/
|
||||||
|
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
|
@ -18,4 +18,6 @@
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
-dontobfuscate
|
|
@ -11,6 +11,8 @@ import org.oxycblt.auxio.theme.accent
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
// TODO: Collapse LoadingFragment/MainFragment into MainActivity.
|
||||||
|
|
||||||
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
|
||||||
// Debugging placeholder
|
// Debugging placeholder
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||||
|
|
|
@ -21,8 +21,6 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||||
|
|
||||||
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
class LoadingFragment : Fragment(R.layout.fragment_loading) {
|
||||||
|
|
||||||
// TODO: Phase out LoadingFragment
|
|
||||||
|
|
||||||
private val loadingModel: LoadingViewModel by lazy {
|
private val loadingModel: LoadingViewModel by lazy {
|
||||||
ViewModelProvider(
|
ViewModelProvider(
|
||||||
this,
|
this,
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.oxycblt.auxio.music
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.databinding.BindingAdapter
|
import androidx.databinding.BindingAdapter
|
||||||
|
@ -65,57 +66,40 @@ fun Long.toAlbumArtURI(): Uri {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Int.toSongCount(): Int {
|
|
||||||
return if (this < 2) {
|
|
||||||
R.string.label_single_song
|
|
||||||
} else {
|
|
||||||
R.string.format_multi_song_count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Int.toAlbumCount(): Int {
|
|
||||||
return if (this < 2) {
|
|
||||||
R.string.label_single_album
|
|
||||||
} else {
|
|
||||||
R.string.format_album_count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the amount of songs in an album
|
// Format the amount of songs in an album
|
||||||
@BindingAdapter("songCount")
|
@BindingAdapter("songCount")
|
||||||
fun TextView.getAlbumSongs(album: Album) {
|
fun TextView.getAlbumSongs(album: Album) {
|
||||||
text = context.getString(album.numSongs.toSongCount(), album.numSongs)
|
text = context.resources.getQuantityString(R.plurals.format_song_count, album.numSongs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@BindingAdapter("albumSongCount")
|
@BindingAdapter("artistCounts")
|
||||||
fun TextView.getSongAlbumCount(artist: Artist) {
|
fun TextView.getArtistCounts(artist: Artist) {
|
||||||
val albums = context.getString(artist.numAlbums.toAlbumCount(), artist.numAlbums)
|
// Get the quantity string for both albums & artists, and then stitch them together.
|
||||||
val songs = context.getString(artist.numSongs.toSongCount(), artist.numSongs)
|
val albums = context.resources.getQuantityString(
|
||||||
|
R.plurals.format_albums, artist.numAlbums, artist.numAlbums
|
||||||
|
)
|
||||||
|
val songs = context.resources.getQuantityString(
|
||||||
|
R.plurals.format_song_count, artist.numSongs, artist.numSongs
|
||||||
|
)
|
||||||
|
|
||||||
text = context.getString(R.string.format_combined_song_album, albums, songs)
|
Log.d("getArtistCounts", albums)
|
||||||
|
|
||||||
|
text = context.getString(R.string.format_double_counts, albums, songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the cover art
|
// Get the cover art
|
||||||
@BindingAdapter("coverArt")
|
@BindingAdapter("coverArt")
|
||||||
fun ImageView.getCoverArt(any: Any) {
|
fun ImageView.getCoverArt(song: Song) {
|
||||||
val uri = when (any) {
|
load(song.album.coverUri) {
|
||||||
is Song -> any.album.coverUri
|
|
||||||
is Album -> any.coverUri
|
|
||||||
|
|
||||||
else -> Uri.EMPTY
|
|
||||||
}
|
|
||||||
|
|
||||||
load(uri) {
|
|
||||||
crossfade(true)
|
crossfade(true)
|
||||||
placeholder(android.R.color.transparent)
|
placeholder(android.R.color.transparent)
|
||||||
error(R.drawable.ic_music)
|
error(R.drawable.ic_music)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the artist image.
|
@BindingAdapter("coverArt")
|
||||||
@BindingAdapter("artistImage")
|
fun ImageView.getCoverArt(album: Album) {
|
||||||
fun ImageView.getArtistImage(artist: Artist) {
|
load(album.coverUri) {
|
||||||
load(artist.albums[0].coverUri) {
|
|
||||||
crossfade(true)
|
crossfade(true)
|
||||||
placeholder(android.R.color.transparent)
|
placeholder(android.R.color.transparent)
|
||||||
error(R.drawable.ic_music)
|
error(R.drawable.ic_music)
|
||||||
|
|
|
@ -5,15 +5,15 @@ import android.net.Uri
|
||||||
// Abstraction for Song
|
// Abstraction for Song
|
||||||
data class Album(
|
data class Album(
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
var title: String = "",
|
var name: String = "",
|
||||||
val artistName: String = "", // Only used for sorting. Use artist for everything else.
|
val artistName: String = "", // only used for sorting. Use artist.name instead.
|
||||||
val coverUri: Uri = Uri.EMPTY,
|
val coverUri: Uri = Uri.EMPTY,
|
||||||
val year: Int = 0,
|
val year: Int = 0
|
||||||
var numSongs: Int = 0
|
|
||||||
) {
|
) {
|
||||||
lateinit var artist: Artist
|
lateinit var artist: Artist
|
||||||
|
|
||||||
val songs = mutableListOf<Song>()
|
val songs = mutableListOf<Song>()
|
||||||
|
val numSongs: Int get() = songs.size
|
||||||
|
|
||||||
fun finalize() {
|
fun finalize() {
|
||||||
songs.sortBy { it.track }
|
songs.sortBy { it.track }
|
||||||
|
|
|
@ -7,13 +7,13 @@ data class Artist(
|
||||||
val genres: MutableList<Genre> = mutableListOf(Genre())
|
val genres: MutableList<Genre> = mutableListOf(Genre())
|
||||||
) {
|
) {
|
||||||
val albums = mutableListOf<Album>()
|
val albums = mutableListOf<Album>()
|
||||||
var numAlbums = 0
|
|
||||||
|
val numAlbums: Int get() = albums.size
|
||||||
var numSongs = 0
|
var numSongs = 0
|
||||||
|
|
||||||
fun finalize() {
|
fun finalize() {
|
||||||
albums.sortByDescending { it.year }
|
albums.sortByDescending { it.year }
|
||||||
|
|
||||||
numAlbums = albums.size
|
|
||||||
albums.forEach { album ->
|
albums.forEach { album ->
|
||||||
numSongs += album.numSongs
|
numSongs += album.numSongs
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,8 @@ data class Genre(
|
||||||
var name: String = "",
|
var name: String = "",
|
||||||
) {
|
) {
|
||||||
val artists = mutableListOf<Artist>()
|
val artists = mutableListOf<Artist>()
|
||||||
var numArtists = 0
|
|
||||||
|
|
||||||
fun finalize() {
|
fun finalize() {
|
||||||
artists.sortByDescending { it.name }
|
artists.sortByDescending { it.name }
|
||||||
numArtists = artists.size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import android.text.format.DateUtils
|
||||||
// Class containing all relevant values for a song.
|
// Class containing all relevant values for a song.
|
||||||
data class Song(
|
data class Song(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
var title: String,
|
var name: String,
|
||||||
val albumName: String, // Only used for sorting. Use artist for everything else.
|
val albumName: String, // Only used for sorting. Use album.title for everything else.
|
||||||
val track: Int,
|
val track: Int,
|
||||||
val duration: Long
|
val duration: Long
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -113,8 +113,8 @@ class MusicLoader(private val resolver: ContentResolver) {
|
||||||
artistCursor = resolver.query(
|
artistCursor = resolver.query(
|
||||||
Genres.Members.getContentUri("external", genre.id),
|
Genres.Members.getContentUri("external", genre.id),
|
||||||
arrayOf(
|
arrayOf(
|
||||||
Artists._ID,
|
Artists._ID, // 0
|
||||||
Artists.ARTIST
|
Artists.ARTIST // 1
|
||||||
),
|
),
|
||||||
null, null,
|
null, null,
|
||||||
Artists.DEFAULT_SORT_ORDER
|
Artists.DEFAULT_SORT_ORDER
|
||||||
|
@ -171,7 +171,6 @@ class MusicLoader(private val resolver: ContentResolver) {
|
||||||
Albums.ARTIST, // 2
|
Albums.ARTIST, // 2
|
||||||
|
|
||||||
Albums.FIRST_YEAR, // 3
|
Albums.FIRST_YEAR, // 3
|
||||||
Albums.NUMBER_OF_SONGS // 4
|
|
||||||
),
|
),
|
||||||
null, null,
|
null, null,
|
||||||
Albums.DEFAULT_SORT_ORDER
|
Albums.DEFAULT_SORT_ORDER
|
||||||
|
@ -182,21 +181,19 @@ class MusicLoader(private val resolver: ContentResolver) {
|
||||||
val nameIndex = cursor.getColumnIndexOrThrow(Albums.ALBUM)
|
val nameIndex = cursor.getColumnIndexOrThrow(Albums.ALBUM)
|
||||||
val artistIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST)
|
val artistIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST)
|
||||||
val yearIndex = cursor.getColumnIndexOrThrow(Albums.FIRST_YEAR)
|
val yearIndex = cursor.getColumnIndexOrThrow(Albums.FIRST_YEAR)
|
||||||
val numIndex = cursor.getColumnIndexOrThrow(Albums.NUMBER_OF_SONGS)
|
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
val id = cursor.getLong(idIndex)
|
val id = cursor.getLong(idIndex)
|
||||||
val name = cursor.getString(nameIndex) ?: ""
|
val name = cursor.getString(nameIndex) ?: ""
|
||||||
val artist = cursor.getString(artistIndex) ?: ""
|
val artist = cursor.getString(artistIndex) ?: ""
|
||||||
val year = cursor.getInt(yearIndex)
|
val year = cursor.getInt(yearIndex)
|
||||||
val numSongs = cursor.getInt(numIndex)
|
|
||||||
|
|
||||||
val coverUri = id.toAlbumArtURI()
|
val coverUri = id.toAlbumArtURI()
|
||||||
|
|
||||||
albums.add(
|
albums.add(
|
||||||
Album(
|
Album(
|
||||||
id, name, artist,
|
id, name, artist,
|
||||||
coverUri, year, numSongs
|
coverUri, year
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +203,7 @@ class MusicLoader(private val resolver: ContentResolver) {
|
||||||
|
|
||||||
// Remove dupes
|
// Remove dupes
|
||||||
albums = albums.distinctBy {
|
albums = albums.distinctBy {
|
||||||
it.title to it.artistName to it.year to it.numSongs
|
it.name to it.artistName to it.year to it.numSongs
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -260,7 +257,7 @@ class MusicLoader(private val resolver: ContentResolver) {
|
||||||
|
|
||||||
// Remove dupes
|
// Remove dupes
|
||||||
songs = songs.distinctBy {
|
songs = songs.distinctBy {
|
||||||
it.title to it.albumName to it.track to it.duration
|
it.name to it.albumName to it.track to it.duration
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
|
|
||||||
Log.d(
|
Log.d(
|
||||||
|
|
|
@ -22,6 +22,7 @@ class MusicSorter(
|
||||||
sortArtistsIntoGenres()
|
sortArtistsIntoGenres()
|
||||||
|
|
||||||
addPlaceholders()
|
addPlaceholders()
|
||||||
|
finalizeMusic()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortSongsIntoAlbums() {
|
private fun sortSongsIntoAlbums() {
|
||||||
|
@ -31,7 +32,7 @@ class MusicSorter(
|
||||||
|
|
||||||
for (album in albums) {
|
for (album in albums) {
|
||||||
// Find all songs that match the current album title
|
// Find all songs that match the current album title
|
||||||
val albumSongs = songs.filter { it.albumName == album.title }
|
val albumSongs = songs.filter { it.albumName == album.name }
|
||||||
|
|
||||||
// Then add them to the album
|
// Then add them to the album
|
||||||
for (song in albumSongs) {
|
for (song in albumSongs) {
|
||||||
|
@ -46,14 +47,13 @@ class MusicSorter(
|
||||||
if (unknownSongs.size > 0) {
|
if (unknownSongs.size > 0) {
|
||||||
|
|
||||||
// Reuse an existing unknown album if one is found
|
// Reuse an existing unknown album if one is found
|
||||||
val unknownAlbum = albums.find { it.title == "" } ?: Album()
|
val unknownAlbum = albums.find { it.name == "" } ?: Album()
|
||||||
|
|
||||||
for (song in unknownSongs) {
|
for (song in unknownSongs) {
|
||||||
song.album = unknownAlbum
|
song.album = unknownAlbum
|
||||||
unknownAlbum.songs.add(song)
|
unknownAlbum.songs.add(song)
|
||||||
}
|
}
|
||||||
|
|
||||||
unknownAlbum.numSongs = unknownAlbum.songs.size
|
|
||||||
unknownAlbum.finalize()
|
unknownAlbum.finalize()
|
||||||
|
|
||||||
albums.add(unknownAlbum)
|
albums.add(unknownAlbum)
|
||||||
|
@ -63,8 +63,6 @@ class MusicSorter(
|
||||||
"${unknownSongs.size} songs were placed into an unknown album."
|
"${unknownSongs.size} songs were placed into an unknown album."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
albums.sortByDescending { it.title }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortAlbumsIntoArtists() {
|
private fun sortAlbumsIntoArtists() {
|
||||||
|
@ -107,8 +105,6 @@ class MusicSorter(
|
||||||
"${unknownAlbums.size} albums were placed into an unknown artist."
|
"${unknownAlbums.size} albums were placed into an unknown artist."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
artists.sortByDescending { it.name }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortArtistsIntoGenres() {
|
private fun sortArtistsIntoGenres() {
|
||||||
|
@ -126,7 +122,7 @@ class MusicSorter(
|
||||||
|
|
||||||
// Then add them to the genre, along with refreshing the amount of artists
|
// Then add them to the genre, along with refreshing the amount of artists
|
||||||
genre.artists.addAll(genreArtists)
|
genre.artists.addAll(genreArtists)
|
||||||
genre.numArtists = artists.size
|
genre.finalize()
|
||||||
|
|
||||||
unknownArtists.removeAll(genreArtists)
|
unknownArtists.removeAll(genreArtists)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +136,7 @@ class MusicSorter(
|
||||||
unknownGenre.artists.add(artist)
|
unknownGenre.artists.add(artist)
|
||||||
}
|
}
|
||||||
|
|
||||||
unknownGenre.numArtists = artists.size
|
unknownGenre.finalize()
|
||||||
|
|
||||||
genres.add(unknownGenre)
|
genres.add(unknownGenre)
|
||||||
|
|
||||||
|
@ -149,14 +145,19 @@ class MusicSorter(
|
||||||
"${unknownArtists.size} albums were placed into an unknown genre."
|
"${unknownArtists.size} albums were placed into an unknown genre."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
genres.sortByDescending { it.name }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct any empty names [""] with the proper placeholders [Unknown Album]
|
// Correct any empty names [""] with the proper placeholders [Unknown Album]
|
||||||
private fun addPlaceholders() {
|
private fun addPlaceholders() {
|
||||||
genres.forEach { if (it.name == "") it.name = genrePlaceholder }
|
genres.forEach { if (it.name == "") it.name = genrePlaceholder }
|
||||||
artists.forEach { if (it.name == "") it.name = artistPlaceholder }
|
artists.forEach { if (it.name == "") it.name = artistPlaceholder }
|
||||||
albums.forEach { if (it.title == "") it.title = albumPlaceholder }
|
albums.forEach { if (it.name == "") it.name = albumPlaceholder }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort all music into
|
||||||
|
private fun finalizeMusic() {
|
||||||
|
genres.sortBy { it.name }
|
||||||
|
artists.sortBy { it.name }
|
||||||
|
albums.sortBy { it.name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class SongsFragment : Fragment() {
|
||||||
binding.songRecycler.adapter = SongAdapter(
|
binding.songRecycler.adapter = SongAdapter(
|
||||||
songsModel.songs.value!!,
|
songsModel.songs.value!!,
|
||||||
ClickListener { song ->
|
ClickListener { song ->
|
||||||
Log.d(this::class.simpleName, song.title)
|
Log.d(this::class.simpleName, song.name)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
binding.songRecycler.applyDivider()
|
binding.songRecycler.applyDivider()
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/margin_medium"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
android:text="@{album.title}"
|
android:text="@{album.name}"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
app:albumSongCount="@{artist}"
|
app:artistCounts="@{artist}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/artist_name"
|
app:layout_constraintTop_toBottomOf="@+id/artist_name"
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
android:layout_marginStart="@dimen/margin_medium"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:text="@{song.title}"
|
android:text="@{song.name}"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/song_info"
|
app:layout_constraintBottom_toTopOf="@+id/song_info"
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:text="@{@string/format_song_info(song.album.artist.name, song.album.title)}"
|
android:text="@{@string/format_song_info(song.album.artist.name, song.album.name)}"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/duration"
|
app:layout_constraintEnd_toStartOf="@+id/duration"
|
||||||
app:layout_constraintStart_toEndOf="@+id/cover"
|
app:layout_constraintStart_toEndOf="@+id/cover"
|
||||||
|
|
|
@ -11,15 +11,21 @@
|
||||||
|
|
||||||
<string name="label_retry">Retry</string>
|
<string name="label_retry">Retry</string>
|
||||||
<string name="label_grant">Grant</string>
|
<string name="label_grant">Grant</string>
|
||||||
<string name="label_single_song">1 Song</string>
|
|
||||||
<string name="label_single_album">1 Album</string>
|
|
||||||
|
|
||||||
<string name="placeholder_unknown_genre">Unknown Genre</string>
|
<string name="placeholder_unknown_genre">Unknown Genre</string>
|
||||||
<string name="placeholder_unknown_artist">Unknown Artist</string>
|
<string name="placeholder_unknown_artist">Unknown Artist</string>
|
||||||
<string name="placeholder_unknown_album">Unknown Album</string>
|
<string name="placeholder_unknown_album">Unknown Album</string>
|
||||||
|
|
||||||
<string name="format_multi_song_count">%s Songs</string>
|
<string name="format_song_info">%1$s / %2$s</string>
|
||||||
<string name="format_song_info">%s / %s</string>
|
<string name="format_double_counts">%1$s, %2$s</string>
|
||||||
<string name="format_album_count">%s Albums</string>
|
|
||||||
<string name="format_combined_song_album">%s, %s</string>
|
<plurals name="format_song_count">
|
||||||
|
<item quantity="one">%s Song</item>
|
||||||
|
<item quantity="other">%s Songs</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="format_albums">
|
||||||
|
<item quantity="one">%s Album</item>
|
||||||
|
<item quantity="other">%s Albums</item>
|
||||||
|
</plurals>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue