Improve semantics

Remove some useless TODOs, update naming in places.
This commit is contained in:
OxygenCobalt 2020-10-18 09:06:16 -06:00
parent 00667151dc
commit 2811c83543
20 changed files with 89 additions and 76 deletions

View file

@ -89,7 +89,6 @@ class MainFragment : Fragment() {
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
// TODO: Add a slide animation to this
// Change CompactPlaybackFragment's visibility here so that an animation occurs. // Change CompactPlaybackFragment's visibility here so that an animation occurs.
playbackModel.currentSong.observe(viewLifecycleOwner) { playbackModel.currentSong.observe(viewLifecycleOwner) {
if (it == null) { if (it == null) {

View file

@ -86,7 +86,6 @@ class AlbumDetailFragment : Fragment() {
// Observe playback model to update the play button // Observe playback model to update the play button
// TODO: Make these icons animated // TODO: Make these icons animated
// TODO: Shuffle button/option, unsure of which one
playbackModel.currentMode.observe(viewLifecycleOwner) { playbackModel.currentMode.observe(viewLifecycleOwner) {
updatePlayButton(it, binding) updatePlayButton(it, binding)
} }

View file

@ -18,9 +18,6 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable 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() { class ArtistDetailFragment : Fragment() {
private val args: ArtistDetailFragmentArgs by navArgs() private val args: ArtistDetailFragmentArgs by navArgs()

View file

@ -8,7 +8,6 @@ import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
// TODO: Add ability to highlight currently playing songs
class DetailAlbumAdapter( class DetailAlbumAdapter(
private val doOnClick: (Album) -> Unit private val doOnClick: (Album) -> Unit
) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) { ) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) {
@ -25,7 +24,7 @@ class DetailAlbumAdapter(
// Generic ViewHolder for a detail album // Generic ViewHolder for a detail album
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemArtistAlbumBinding private val binding: ItemArtistAlbumBinding,
) : BaseViewHolder<Album>(binding, doOnClick) { ) : BaseViewHolder<Album>(binding, doOnClick) {
override fun onBind(model: Album) { override fun onBind(model: Album) {

View file

@ -2,18 +2,24 @@ package org.oxycblt.auxio.detail.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.ColorInt
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
import org.oxycblt.auxio.theme.accent
import org.oxycblt.auxio.theme.toColor
class DetailSongAdapter( class DetailSongAdapter(
private val doOnClick: (Song) -> Unit private val doOnClick: (Song) -> Unit
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) { ) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(DiffCallback()) {
private var currentSong: Song? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return 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 // Generic ViewHolder for a song
inner class ViewHolder( inner class ViewHolder(
private val binding: ItemAlbumSongBinding private val binding: ItemAlbumSongBinding,
@ColorInt private val resolvedAccent: Int
) : BaseViewHolder<Song>(binding, doOnClick) { ) : BaseViewHolder<Song>(binding, doOnClick) {
override fun onBind(model: Song) { override fun onBind(model: Song) {
binding.song = model binding.song = model
if (model == currentSong) {
binding.songName.setTextColor(resolvedAccent)
}
binding.songName.requestLayout() binding.songName.requestLayout()
} }
} }

View file

@ -56,7 +56,6 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
// --- UI SETUP --- // --- UI SETUP ---
// TODO: Add exit functionality
binding.libraryToolbar.apply { binding.libraryToolbar.apply {
overflowIcon = ContextCompat.getDrawable( overflowIcon = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_sort_none requireContext(), R.drawable.ic_sort_none
@ -78,7 +77,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
true true
} }
// TODO: Add icons to overflow menu items // TODO: Add icons to overflow menu items?
menu.apply { menu.apply {
val item = findItem(R.id.action_search) val item = findItem(R.id.action_search)
val searchView = item.actionView as SearchView 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] // [If there's a way to preserve scroll position properly]
binding.libraryRecycler.apply { binding.libraryRecycler.apply {
adapter = libraryAdapter adapter = libraryAdapter

View file

@ -4,10 +4,9 @@ import android.net.Uri
// --- MUSIC MODELS --- // --- MUSIC MODELS ---
// TODO: Remove parent/child references so that they can be parcelable [Would require genre rework] // 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 // 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 { sealed class BaseModel {
abstract val id: Long abstract val id: Long
abstract val name: String abstract val name: String
@ -39,14 +38,13 @@ data class Album(
val songs = mutableListOf<Song>() val songs = mutableListOf<Song>()
val numSongs: Int get() = songs.size val numSongs: Int get() = songs.size
val totalDuration: String val totalDuration: String by lazy {
get() { var seconds: Long = 0
var seconds: Long = 0 songs.forEach {
songs.forEach { seconds += it.seconds
seconds += it.seconds
}
return seconds.toDuration()
} }
seconds.toDuration()
}
} }
// Artist // Artist
@ -58,22 +56,20 @@ data class Artist(
val genres = mutableListOf<Genre>() val genres = mutableListOf<Genre>()
val numAlbums: Int get() = albums.size val numAlbums: Int get() = albums.size
val numSongs: Int val numSongs: Int by lazy {
get() { var num = 0
var num = 0 albums.forEach {
albums.forEach { num += it.numSongs
num += it.numSongs
}
return num
} }
val songs: MutableList<Song> num
get() { }
val songs = mutableListOf<Song>() val songs: MutableList<Song> by lazy {
albums.forEach { val songs = mutableListOf<Song>()
songs.addAll(it.songs) albums.forEach {
} songs.addAll(it.songs)
return songs
} }
songs
}
} }
// Genre // Genre
@ -84,22 +80,20 @@ data class Genre(
val artists = mutableListOf<Artist>() val artists = mutableListOf<Artist>()
val numArtists: Int get() = artists.size val numArtists: Int get() = artists.size
val numAlbums: Int val numAlbums: Int by lazy {
get() { var num = 0
var num = 0 artists.forEach {
artists.forEach { num += it.numAlbums
num += it.numAlbums
}
return num
} }
val numSongs: Int num
get() { }
var num = 0 val numSongs: Int by lazy {
artists.forEach { var num = 0
num += it.numSongs artists.forEach {
} num += it.numSongs
return num
} }
num
}
} }
// Header [Used for search, nothing else] // Header [Used for search, nothing else]

View file

@ -24,7 +24,6 @@ private val ID3_GENRES = arrayOf(
"Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal",
"Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock",
// Winamp extensions
"Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival",
"Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock",
"Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "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") @BindingAdapter("albumYear")
fun TextView.bindAlbumDate(album: Album) { fun TextView.bindAlbumYear(album: Album) {
text = album.year.toYear(context) text = album.year.toYear(context)
} }

View file

@ -17,12 +17,12 @@ import coil.size.Size
import okio.buffer import okio.buffer
import okio.source import okio.source
import java.io.InputStream import java.io.InputStream
import org.oxycblt.auxio.R
const val MOSAIC_BITMAP_SIZE = 512 const val MOSAIC_BITMAP_SIZE = 512
const val MOSAIC_BITMAP_INCREMENT = 256 const val MOSAIC_BITMAP_INCREMENT = 256
// A Fetcher that takes multiple cover uris and turns them into a 2x2 mosaic image. // 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>> { class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
override suspend fun fetch( override suspend fun fetch(
pool: BitmapPool, pool: BitmapPool,
@ -46,13 +46,20 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
if (streams.size < 4) { if (streams.size < 4) {
streams.forEach { it.close() } streams.forEach { it.close() }
// FIXME: What if all the streams fail? return if (streams.isNotEmpty()) {
SourceResult(
return SourceResult( source = streams[0].source().buffer(),
source = streams[0].source().buffer(), mimeType = context.contentResolver.getType(data[0]),
mimeType = context.contentResolver.getType(data[0]), dataSource = DataSource.DISK
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. // Create the mosaic, code adapted from Phonograph.

View file

@ -35,7 +35,7 @@ class CompactPlaybackFragment : Fragment() {
// --- UI SETUP --- // --- UI SETUP ---
binding.lifecycleOwner = this binding.lifecycleOwner = viewLifecycleOwner
// Put a placeholder song in the binding & hide the playback fragment initially, // 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 // 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) { playbackModel.isPlaying.observe(viewLifecycleOwner) {
if (it) { if (it) {
// Animate the icon transition when the playing status switches // Animate the icon transition when the playing status switches
@ -78,4 +79,10 @@ class CompactPlaybackFragment : Fragment() {
return binding.root return binding.root
} }
override fun onDestroy() {
super.onDestroy()
playbackModel.updateStaticIconStatus(true)
}
} }

View file

@ -19,10 +19,10 @@ import org.oxycblt.auxio.theme.disable
import org.oxycblt.auxio.theme.enable import org.oxycblt.auxio.theme.enable
import org.oxycblt.auxio.theme.toColor import org.oxycblt.auxio.theme.toColor
// TODO: Possibly add some swipe-to-next-track function, could require a ViewPager.
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
// TODO: Implement media controls
// TODO: Implement nav to artists/albums // TODO: Implement nav to artists/albums
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,

View file

@ -2,7 +2,6 @@ package org.oxycblt.auxio.playback
// Enum for instruction how the queue should function. // Enum for instruction how the queue should function.
// ALL SONGS -> Play from all songs // ALL SONGS -> Play from all songs
// IN_GENRE -> Play from the genre
// IN_ARTIST -> Play from the songs of the artist // IN_ARTIST -> Play from the songs of the artist
// IN_ALBUM -> Play from the songs of the album // IN_ALBUM -> Play from the songs of the album
enum class PlaybackMode { enum class PlaybackMode {

View file

@ -14,10 +14,10 @@ import org.oxycblt.auxio.music.toDuration
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.Random.Default.nextLong import kotlin.random.Random.Default.nextLong
// TODO: Implement media controls // TODO: Queue
// TODO: Implement persistence
// TODO: Add the playback service itself // 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. // A ViewModel that acts as an intermediary between PlaybackService and the Playback Fragments.
class PlaybackViewModel : ViewModel() { class PlaybackViewModel : ViewModel() {
private val mCurrentSong = MutableLiveData<Song>() private val mCurrentSong = MutableLiveData<Song>()

View file

@ -27,7 +27,7 @@ class SongsFragment : Fragment() {
val musicStore = MusicStore.getInstance() val musicStore = MusicStore.getInstance()
// TODO: Add option to search songs if LibraryFragment isn't enabled // TODO: Add option to search songs if LibraryFragment isn't enabled
// TODO: Maybe add fast scrolling or sorting // TODO: Fast scrolling?
// --- UI SETUP --- // --- UI SETUP ---

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp" android:width="24dp"
android:height="32dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="@color/control_color"> android:tint="@color/control_color">

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="32dp"
android:height="24dp" android:height="32dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="@color/control_color"> android:tint="@color/control_color">

View file

@ -131,7 +131,7 @@
android:backgroundTint="?android:attr/colorPrimary" android:backgroundTint="?android:attr/colorPrimary"
android:contentDescription="@string/description_play" android:contentDescription="@string/description_play"
android:onClick="@{() -> playbackModel.play(album, true)}" android:onClick="@{() -> playbackModel.play(album, true)}"
android:src="@drawable/ic_shuffle_small" android:src="@drawable/ic_shuffle"
android:tint="@color/background" android:tint="@color/background"
app:layout_constraintBottom_toBottomOf="@+id/album_details" app:layout_constraintBottom_toBottomOf="@+id/album_details"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"

View file

@ -98,6 +98,7 @@
app:layout_constraintTop_toBottomOf="@+id/artist_genre" app:layout_constraintTop_toBottomOf="@+id/artist_genre"
tools:text="2 Albums, 20 Songs" /> tools:text="2 Albums, 20 Songs" />
<!-- TODO: Improve these two buttons so that they don't look as cluttered -->
<ImageButton <ImageButton
android:id="@+id/artist_play" android:id="@+id/artist_play"
style="@style/Widget.AppCompat.Button.Borderless" style="@style/Widget.AppCompat.Button.Borderless"
@ -112,11 +113,11 @@
android:onClick="@{() -> playbackModel.play(artist, false)}" android:onClick="@{() -> playbackModel.play(artist, false)}"
app:layout_constraintBottom_toBottomOf="@+id/artist_counts" app:layout_constraintBottom_toBottomOf="@+id/artist_counts"
app:layout_constraintDimensionRatio="1:1" 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" /> app:layout_constraintTop_toTopOf="@+id/artist_genre" />
<ImageButton <ImageButton
android:id="@+id/album_shuffle" android:id="@+id/artist_shuffle"
style="@style/Widget.AppCompat.Button.Borderless" style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@ -126,7 +127,7 @@
android:backgroundTint="?android:attr/colorPrimary" android:backgroundTint="?android:attr/colorPrimary"
android:contentDescription="@string/description_play" android:contentDescription="@string/description_play"
android:onClick="@{() -> playbackModel.play(artist, true)}" android:onClick="@{() -> playbackModel.play(artist, true)}"
android:src="@drawable/ic_shuffle_small" android:src="@drawable/ic_shuffle"
android:tint="@color/background" android:tint="@color/background"
app:layout_constraintBottom_toBottomOf="@+id/artist_counts" app:layout_constraintBottom_toBottomOf="@+id/artist_counts"
app:layout_constraintDimensionRatio="1:1" app:layout_constraintDimensionRatio="1:1"

View file

@ -191,7 +191,7 @@
android:layout_width="@dimen/size_play_pause_compact" android:layout_width="@dimen/size_play_pause_compact"
android:layout_height="@dimen/size_play_pause_compact" android:layout_height="@dimen/size_play_pause_compact"
android:layout_marginEnd="@dimen/margin_mid_large" 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:background="@drawable/ui_unbounded_ripple"
android:onClick="@{() -> playbackModel.invertShuffleStatus()}" android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:contentDescription="@{playbackModel.isShuffling() ? @string/description_shuffle_off : @string/description_shuffle_on" android:contentDescription="@{playbackModel.isShuffling() ? @string/description_shuffle_off : @string/description_shuffle_on"

View file

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_shuffle" android:id="@+id/action_shuffle"
android:icon="@drawable/ic_shuffle_small" android:icon="@drawable/ic_shuffle"
android:title="@string/label_shuffle" android:title="@string/label_shuffle"
app:showAsAction="always" /> app:showAsAction="always" />
</menu> </menu>