Improve semantics
Remove some useless TODOs, update naming in places.
This commit is contained in:
parent
00667151dc
commit
2811c83543
20 changed files with 89 additions and 76 deletions
|
@ -89,7 +89,6 @@ class MainFragment : Fragment() {
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
// TODO: Add a slide animation to this
|
||||
// Change CompactPlaybackFragment's visibility here so that an animation occurs.
|
||||
playbackModel.currentSong.observe(viewLifecycleOwner) {
|
||||
if (it == null) {
|
||||
|
|
|
@ -86,7 +86,6 @@ class AlbumDetailFragment : Fragment() {
|
|||
|
||||
// Observe playback model to update the play button
|
||||
// TODO: Make these icons animated
|
||||
// TODO: Shuffle button/option, unsure of which one
|
||||
playbackModel.currentMode.observe(viewLifecycleOwner) {
|
||||
updatePlayButton(it, binding)
|
||||
}
|
||||
|
|
|
@ -18,9 +18,6 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
|
|||
import org.oxycblt.auxio.theme.applyDivider
|
||||
import org.oxycblt.auxio.theme.disable
|
||||
|
||||
// TODO: Add media controls to DetailFragments [Play, Shuffle]
|
||||
// TODO: Implement a system where if the main info [Name, Parent, Counts] scrolls off of
|
||||
// the screen, then update the toolbar to show that info w/the media controls
|
||||
class ArtistDetailFragment : Fragment() {
|
||||
|
||||
private val args: ArtistDetailFragmentArgs by navArgs()
|
||||
|
|
|
@ -8,7 +8,6 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
|
||||
// TODO: Add ability to highlight currently playing songs
|
||||
class DetailAlbumAdapter(
|
||||
private val doOnClick: (Album) -> Unit
|
||||
) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) {
|
||||
|
@ -25,7 +24,7 @@ class DetailAlbumAdapter(
|
|||
|
||||
// Generic ViewHolder for a detail album
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemArtistAlbumBinding
|
||||
private val binding: ItemArtistAlbumBinding,
|
||||
) : BaseViewHolder<Album>(binding, doOnClick) {
|
||||
|
||||
override fun onBind(model: Album) {
|
||||
|
|
|
@ -2,18 +2,24 @@ package org.oxycblt.auxio.detail.adapters
|
|||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.theme.accent
|
||||
import org.oxycblt.auxio.theme.toColor
|
||||
|
||||
class DetailSongAdapter(
|
||||
private val doOnClick: (Song) -> Unit
|
||||
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) {
|
||||
private var currentSong: Song? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context))
|
||||
ItemAlbumSongBinding.inflate(LayoutInflater.from(parent.context)),
|
||||
accent.first.toColor(parent.context)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -23,11 +29,17 @@ class DetailSongAdapter(
|
|||
|
||||
// Generic ViewHolder for a song
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemAlbumSongBinding
|
||||
private val binding: ItemAlbumSongBinding,
|
||||
@ColorInt private val resolvedAccent: Int
|
||||
) : BaseViewHolder<Song>(binding, doOnClick) {
|
||||
|
||||
override fun onBind(model: Song) {
|
||||
binding.song = model
|
||||
|
||||
if (model == currentSong) {
|
||||
binding.songName.setTextColor(resolvedAccent)
|
||||
}
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,6 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
|
||||
// --- UI SETUP ---
|
||||
|
||||
// TODO: Add exit functionality
|
||||
binding.libraryToolbar.apply {
|
||||
overflowIcon = ContextCompat.getDrawable(
|
||||
requireContext(), R.drawable.ic_sort_none
|
||||
|
@ -78,7 +77,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
true
|
||||
}
|
||||
|
||||
// TODO: Add icons to overflow menu items
|
||||
// TODO: Add icons to overflow menu items?
|
||||
menu.apply {
|
||||
val item = findItem(R.id.action_search)
|
||||
val searchView = item.actionView as SearchView
|
||||
|
@ -118,7 +117,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Change LibraryAdapter to a ListAdapter
|
||||
// FIXME: Change LibraryAdapter to a ListAdapter
|
||||
// [If there's a way to preserve scroll position properly]
|
||||
binding.libraryRecycler.apply {
|
||||
adapter = libraryAdapter
|
||||
|
|
|
@ -4,10 +4,9 @@ import android.net.Uri
|
|||
|
||||
// --- MUSIC MODELS ---
|
||||
// TODO: Remove parent/child references so that they can be parcelable [Would require genre rework]
|
||||
// TODO: Don't determine artist/album/song counts on the fly [If possible]
|
||||
|
||||
// The base model for all music
|
||||
// This is used in a lot of general functions in order to cut down on code
|
||||
// This is used in a lot of general functions in order to have generic utilities
|
||||
sealed class BaseModel {
|
||||
abstract val id: Long
|
||||
abstract val name: String
|
||||
|
@ -39,13 +38,12 @@ data class Album(
|
|||
|
||||
val songs = mutableListOf<Song>()
|
||||
val numSongs: Int get() = songs.size
|
||||
val totalDuration: String
|
||||
get() {
|
||||
val totalDuration: String by lazy {
|
||||
var seconds: Long = 0
|
||||
songs.forEach {
|
||||
seconds += it.seconds
|
||||
}
|
||||
return seconds.toDuration()
|
||||
seconds.toDuration()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,21 +56,19 @@ data class Artist(
|
|||
val genres = mutableListOf<Genre>()
|
||||
|
||||
val numAlbums: Int get() = albums.size
|
||||
val numSongs: Int
|
||||
get() {
|
||||
val numSongs: Int by lazy {
|
||||
var num = 0
|
||||
albums.forEach {
|
||||
num += it.numSongs
|
||||
}
|
||||
return num
|
||||
num
|
||||
}
|
||||
val songs: MutableList<Song>
|
||||
get() {
|
||||
val songs: MutableList<Song> by lazy {
|
||||
val songs = mutableListOf<Song>()
|
||||
albums.forEach {
|
||||
songs.addAll(it.songs)
|
||||
}
|
||||
return songs
|
||||
songs
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,21 +80,19 @@ data class Genre(
|
|||
val artists = mutableListOf<Artist>()
|
||||
|
||||
val numArtists: Int get() = artists.size
|
||||
val numAlbums: Int
|
||||
get() {
|
||||
val numAlbums: Int by lazy {
|
||||
var num = 0
|
||||
artists.forEach {
|
||||
num += it.numAlbums
|
||||
}
|
||||
return num
|
||||
num
|
||||
}
|
||||
val numSongs: Int
|
||||
get() {
|
||||
val numSongs: Int by lazy {
|
||||
var num = 0
|
||||
artists.forEach {
|
||||
num += it.numSongs
|
||||
}
|
||||
return num
|
||||
num
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ private val ID3_GENRES = arrayOf(
|
|||
"Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
|
||||
"Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
|
||||
|
||||
// Winamp extensions
|
||||
"Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
|
||||
"Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
|
||||
"Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour",
|
||||
|
@ -154,8 +153,9 @@ fun TextView.bindAlbumInfo(album: Album) {
|
|||
)
|
||||
}
|
||||
|
||||
// Bind the album year
|
||||
@BindingAdapter("albumYear")
|
||||
fun TextView.bindAlbumDate(album: Album) {
|
||||
fun TextView.bindAlbumYear(album: Album) {
|
||||
text = album.year.toYear(context)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@ import coil.size.Size
|
|||
import okio.buffer
|
||||
import okio.source
|
||||
import java.io.InputStream
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
const val MOSAIC_BITMAP_SIZE = 512
|
||||
const val MOSAIC_BITMAP_INCREMENT = 256
|
||||
|
||||
// A Fetcher that takes multiple cover uris and turns them into a 2x2 mosaic image.
|
||||
// TODO: Add 4x4 mosaics?
|
||||
class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
|
||||
override suspend fun fetch(
|
||||
pool: BitmapPool,
|
||||
|
@ -46,13 +46,20 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
|
|||
if (streams.size < 4) {
|
||||
streams.forEach { it.close() }
|
||||
|
||||
// FIXME: What if all the streams fail?
|
||||
|
||||
return SourceResult(
|
||||
return if (streams.isNotEmpty()) {
|
||||
SourceResult(
|
||||
source = streams[0].source().buffer(),
|
||||
mimeType = context.contentResolver.getType(data[0]),
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
} else {
|
||||
// If ALL the streams failed, then don't even bother with that.
|
||||
DrawableResult(
|
||||
drawable = R.drawable.ic_song.toDrawable(),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the mosaic, code adapted from Phonograph.
|
||||
|
|
|
@ -35,7 +35,7 @@ class CompactPlaybackFragment : Fragment() {
|
|||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
|
||||
// Put a placeholder song in the binding & hide the playback fragment initially,
|
||||
// as for some reason the attach event doesn't register anymore w/LiveData
|
||||
|
@ -59,6 +59,7 @@ class CompactPlaybackFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Fix the thing where the icons will animate on startup
|
||||
playbackModel.isPlaying.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
// Animate the icon transition when the playing status switches
|
||||
|
@ -78,4 +79,10 @@ class CompactPlaybackFragment : Fragment() {
|
|||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
playbackModel.updateStaticIconStatus(true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ import org.oxycblt.auxio.theme.disable
|
|||
import org.oxycblt.auxio.theme.enable
|
||||
import org.oxycblt.auxio.theme.toColor
|
||||
|
||||
// TODO: Possibly add some swipe-to-next-track function, could require a ViewPager.
|
||||
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
||||
// TODO: Implement media controls
|
||||
// TODO: Implement nav to artists/albums
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.oxycblt.auxio.playback
|
|||
|
||||
// Enum for instruction how the queue should function.
|
||||
// ALL SONGS -> Play from all songs
|
||||
// IN_GENRE -> Play from the genre
|
||||
// IN_ARTIST -> Play from the songs of the artist
|
||||
// IN_ALBUM -> Play from the songs of the album
|
||||
enum class PlaybackMode {
|
||||
|
|
|
@ -14,10 +14,10 @@ import org.oxycblt.auxio.music.toDuration
|
|||
import kotlin.random.Random
|
||||
import kotlin.random.Random.Default.nextLong
|
||||
|
||||
// TODO: Implement media controls
|
||||
// TODO: Implement persistence
|
||||
// TODO: Queue
|
||||
// TODO: Add the playback service itself
|
||||
// TODO: Possibly add some swipe-to-next-track function, could require a ViewPager.
|
||||
// TODO: Add loop control [From playback]
|
||||
// TODO: Implement persistence through Bundles and sanity checks [I want to keep my shuffles, okay?]
|
||||
// A ViewModel that acts as an intermediary between PlaybackService and the Playback Fragments.
|
||||
class PlaybackViewModel : ViewModel() {
|
||||
private val mCurrentSong = MutableLiveData<Song>()
|
||||
|
|
|
@ -27,7 +27,7 @@ class SongsFragment : Fragment() {
|
|||
val musicStore = MusicStore.getInstance()
|
||||
|
||||
// TODO: Add option to search songs if LibraryFragment isn't enabled
|
||||
// TODO: Maybe add fast scrolling or sorting
|
||||
// TODO: Fast scrolling?
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/control_color">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="@color/control_color">
|
|
@ -131,7 +131,7 @@
|
|||
android:backgroundTint="?android:attr/colorPrimary"
|
||||
android:contentDescription="@string/description_play"
|
||||
android:onClick="@{() -> playbackModel.play(album, true)}"
|
||||
android:src="@drawable/ic_shuffle_small"
|
||||
android:src="@drawable/ic_shuffle"
|
||||
android:tint="@color/background"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/album_details"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
app:layout_constraintTop_toBottomOf="@+id/artist_genre"
|
||||
tools:text="2 Albums, 20 Songs" />
|
||||
|
||||
<!-- TODO: Improve these two buttons so that they don't look as cluttered -->
|
||||
<ImageButton
|
||||
android:id="@+id/artist_play"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
|
@ -112,11 +113,11 @@
|
|||
android:onClick="@{() -> playbackModel.play(artist, false)}"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist_counts"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toStartOf="@+id/album_shuffle"
|
||||
app:layout_constraintEnd_toStartOf="@+id/artist_shuffle"
|
||||
app:layout_constraintTop_toTopOf="@+id/artist_genre" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/album_shuffle"
|
||||
android:id="@+id/artist_shuffle"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
|
@ -126,7 +127,7 @@
|
|||
android:backgroundTint="?android:attr/colorPrimary"
|
||||
android:contentDescription="@string/description_play"
|
||||
android:onClick="@{() -> playbackModel.play(artist, true)}"
|
||||
android:src="@drawable/ic_shuffle_small"
|
||||
android:src="@drawable/ic_shuffle"
|
||||
android:tint="@color/background"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist_counts"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
|
|
|
@ -191,7 +191,7 @@
|
|||
android:layout_width="@dimen/size_play_pause_compact"
|
||||
android:layout_height="@dimen/size_play_pause_compact"
|
||||
android:layout_marginEnd="@dimen/margin_mid_large"
|
||||
android:src="@drawable/ic_shuffle"
|
||||
android:src="@drawable/ic_shuffle_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
|
||||
android:contentDescription="@{playbackModel.isShuffling() ? @string/description_shuffle_off : @string/description_shuffle_on"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_shuffle"
|
||||
android:icon="@drawable/ic_shuffle_small"
|
||||
android:icon="@drawable/ic_shuffle"
|
||||
android:title="@string/label_shuffle"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
Loading…
Reference in a new issue