diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 33c808502..da6cd0e6d 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -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) { 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 2662cb589..5cc3ceb42 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -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) } 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 f9306a3e3..10d3e58e0 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -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() diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt index 636b56237..0e754311d 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt @@ -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(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(binding, doOnClick) { override fun onBind(model: Album) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt index e4a35a3f0..f0745df12 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt @@ -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(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(binding, doOnClick) { override fun onBind(model: Song) { binding.song = model + + if (model == currentSong) { + binding.songName.setTextColor(resolvedAccent) + } + binding.songName.requestLayout() } } diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index 336d246b5..91127661d 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -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 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 e6ab92753..6b405a518 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -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,14 +38,13 @@ data class Album( val songs = mutableListOf() val numSongs: Int get() = songs.size - val totalDuration: String - get() { - var seconds: Long = 0 - songs.forEach { - seconds += it.seconds - } - return seconds.toDuration() + val totalDuration: String by lazy { + var seconds: Long = 0 + songs.forEach { + seconds += it.seconds } + seconds.toDuration() + } } // Artist @@ -58,22 +56,20 @@ data class Artist( val genres = mutableListOf() val numAlbums: Int get() = albums.size - val numSongs: Int - get() { - var num = 0 - albums.forEach { - num += it.numSongs - } - return num + val numSongs: Int by lazy { + var num = 0 + albums.forEach { + num += it.numSongs } - val songs: MutableList - get() { - val songs = mutableListOf() - albums.forEach { - songs.addAll(it.songs) - } - return songs + num + } + val songs: MutableList by lazy { + val songs = mutableListOf() + albums.forEach { + songs.addAll(it.songs) } + songs + } } // Genre @@ -84,22 +80,20 @@ data class Genre( val artists = mutableListOf() val numArtists: Int get() = artists.size - val numAlbums: Int - get() { - var num = 0 - artists.forEach { - num += it.numAlbums - } - return num + val numAlbums: Int by lazy { + var num = 0 + artists.forEach { + num += it.numAlbums } - val numSongs: Int - get() { - var num = 0 - artists.forEach { - num += it.numSongs - } - return num + num + } + val numSongs: Int by lazy { + var num = 0 + artists.forEach { + num += it.numSongs } + num + } } // Header [Used for search, nothing else] 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 0788fe597..c256fbe2d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -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) } diff --git a/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt b/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt index 91310379e..853321439 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/coil/MosaicFetcher.kt @@ -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> { override suspend fun fetch( pool: BitmapPool, @@ -46,13 +46,20 @@ class MosaicFetcher(private val context: Context) : Fetcher> { if (streams.size < 4) { streams.forEach { it.close() } - // FIXME: What if all the streams fail? - - return SourceResult( - source = streams[0].source().buffer(), - mimeType = context.contentResolver.getType(data[0]), - dataSource = DataSource.DISK - ) + 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. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt index f86e11e95..5e4317f08 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt @@ -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) + } } 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 1bf3ca304..da2df268f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -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, diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt index 94d8e9112..8eb453c63 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackMode.kt @@ -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 { 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 cd920675e..ab7efbfb4 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -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() diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 983dc1d7f..78fb66cc9 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -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 --- diff --git a/app/src/main/res/drawable/ic_shuffle.xml b/app/src/main/res/drawable/ic_shuffle.xml index d207d1526..e68fd1c25 100644 --- a/app/src/main/res/drawable/ic_shuffle.xml +++ b/app/src/main/res/drawable/ic_shuffle.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/drawable/ic_shuffle_small.xml b/app/src/main/res/drawable/ic_shuffle_large.xml similarity index 90% rename from app/src/main/res/drawable/ic_shuffle_small.xml rename to app/src/main/res/drawable/ic_shuffle_large.xml index e68fd1c25..d207d1526 100644 --- a/app/src/main/res/drawable/ic_shuffle_small.xml +++ b/app/src/main/res/drawable/ic_shuffle_large.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index 59b23534d..85a2e1c4e 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -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" diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index a1c6a051c..9b0fcf6c6 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -98,6 +98,7 @@ app:layout_constraintTop_toBottomOf="@+id/artist_genre" tools:text="2 Albums, 20 Songs" /> + \ No newline at end of file