Re-add playing indicator to AlbumDetailFragment
Re-add the playing indicators to AlbumDetailFragment, while removing them from SongsFragment as I personally dont think they work there.
This commit is contained in:
parent
fef8d4146e
commit
85a8241976
8 changed files with 124 additions and 109 deletions
|
@ -16,6 +16,7 @@ import org.oxycblt.auxio.music.MusicStore
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.CenterSmoothScroller
|
||||
import org.oxycblt.auxio.recycler.Highlightable
|
||||
import org.oxycblt.auxio.ui.createToast
|
||||
import org.oxycblt.auxio.ui.setupAlbumSongActions
|
||||
|
||||
|
@ -87,7 +88,7 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
|
||||
setupRecycler(detailAdapter)
|
||||
|
||||
// -- VIEWMODEL SETUP ---
|
||||
// -- DETAILVIEWMODEL SETUP ---
|
||||
|
||||
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
logD("Updating sort mode to $mode")
|
||||
|
@ -129,6 +130,51 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
// --- PLAYBACKVIEWMODEL SETUP ---
|
||||
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
if (playbackModel.mode.value == PlaybackMode.IN_ALBUM &&
|
||||
playbackModel.parent.value!!.id == detailModel.currentAlbum.value!!.id
|
||||
) {
|
||||
detailAdapter.setCurrentSong(song)
|
||||
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
|
||||
if (song != null) {
|
||||
// Use existing data instead of having to re-sort it.
|
||||
val pos = detailAdapter.currentList.indexOfFirst {
|
||||
it.name == song.name
|
||||
}
|
||||
|
||||
// Check if the ViewHolder for this song is visible, if it is then highlight it.
|
||||
// If the ViewHolder is not visible, then the adapter should take care of it if it does become visible.
|
||||
binding.detailRecycler.layoutManager?.findViewByPosition(pos)?.let { child ->
|
||||
binding.detailRecycler.getChildViewHolder(child)?.let {
|
||||
lastHolder = it as Highlightable
|
||||
|
||||
lastHolder?.setHighlighted(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clear the viewholders if the mode isn't ALL_SONGS
|
||||
detailAdapter.setCurrentSong(null)
|
||||
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
}
|
||||
}
|
||||
|
||||
playbackModel.isInUserQueue.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
// Remove any highlighted ViewHolders if the playback is in the user queue.
|
||||
detailAdapter.setCurrentSong(null)
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
}
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
||||
return binding.root
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.Highlightable
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
import org.oxycblt.auxio.ui.memberBinding
|
||||
|
||||
|
@ -28,6 +29,7 @@ abstract class DetailFragment : Fragment() {
|
|||
protected val binding: FragmentDetailBinding by memberBinding(
|
||||
FragmentDetailBinding::inflate
|
||||
)
|
||||
protected var lastHolder: Highlightable? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
|
||||
|
@ -45,6 +47,12 @@ abstract class DetailFragment : Fragment() {
|
|||
callback.isEnabled = false
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
lastHolder = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method for doing setup of the detail toolbar.
|
||||
*/
|
||||
|
|
|
@ -13,8 +13,11 @@ import org.oxycblt.auxio.music.Album
|
|||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.Highlightable
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
|
||||
/**
|
||||
* An adapter for displaying the details and [Song]s of an [Album]
|
||||
|
@ -25,6 +28,10 @@ class AlbumDetailAdapter(
|
|||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (data: Song, view: View) -> Unit
|
||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback()) {
|
||||
|
||||
private var currentSong: Song? = null
|
||||
private var lastHolder: Highlightable? = null
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (getItem(position)) {
|
||||
is Album -> ALBUM_HEADER_ITEM_TYPE
|
||||
|
@ -51,6 +58,30 @@ class AlbumDetailAdapter(
|
|||
is Album -> (holder as AlbumHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as AlbumSongViewHolder).bind(item)
|
||||
}
|
||||
|
||||
if (currentSong != null && position > 0) {
|
||||
if (getItem(position).id == currentSong?.id) {
|
||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = (holder as Highlightable)
|
||||
holder.setHighlighted(true)
|
||||
} else {
|
||||
(holder as Highlightable).setHighlighted(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current song that this adapter should be watching for to highlight.
|
||||
* @param song The [Song] to highlight if found, null to clear any highlighted ViewHolders
|
||||
*/
|
||||
fun setCurrentSong(song: Song?) {
|
||||
// Clear out the last ViewHolder as a song update usually signifies that this current
|
||||
// ViewHolder is likely invalid.
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
|
||||
currentSong = song
|
||||
}
|
||||
|
||||
inner class AlbumHeaderViewHolder(
|
||||
|
@ -70,12 +101,25 @@ class AlbumDetailAdapter(
|
|||
|
||||
inner class AlbumSongViewHolder(
|
||||
private val binding: ItemAlbumSongBinding,
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick) {
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick), Highlightable {
|
||||
private val normalTextColor = binding.songName.currentTextColor
|
||||
private val inactiveTextColor = binding.songTrack.currentTextColor
|
||||
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
}
|
||||
|
||||
override fun setHighlighted(isHighlighted: Boolean) {
|
||||
if (isHighlighted) {
|
||||
binding.songName.setTextColorResource(accent.first)
|
||||
binding.songTrack.setTextColorResource(accent.first)
|
||||
} else {
|
||||
binding.songName.setTextColor(normalTextColor)
|
||||
binding.songTrack.setTextColor(inactiveTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -78,10 +78,8 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
if (it.itemId != R.id.action_search) {
|
||||
libraryModel.updateSortMode(it.itemId)
|
||||
} else {
|
||||
// Do whatever this is in order to make the SearchView focusable.
|
||||
(it.actionView as SearchView).isIconified = false
|
||||
|
||||
// Then also do a basic animation
|
||||
// Then also do a basic animation on the enter transition. Not done on exit
|
||||
// because that causes issues with the SearchView.
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.libraryToolbar, Fade()
|
||||
)
|
||||
|
@ -98,14 +96,12 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
searchView.queryHint = getString(R.string.hint_search_library)
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
searchView.setOnQueryTextListener(this@LibraryFragment)
|
||||
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||
item.isVisible = !hasFocus
|
||||
}
|
||||
|
||||
item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
binding.libraryRecycler.adapter = searchAdapter
|
||||
setGroupVisible(R.id.group_sorting, false)
|
||||
item.isVisible = false
|
||||
|
||||
libraryModel.resetQuery()
|
||||
|
||||
|
@ -115,6 +111,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
binding.libraryRecycler.adapter = libraryAdapter
|
||||
setGroupVisible(R.id.group_sorting, true)
|
||||
item.isVisible = true
|
||||
|
||||
libraryModel.resetQuery()
|
||||
|
||||
|
|
|
@ -47,10 +47,20 @@ data class Song(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a genre to a song.
|
||||
* @throws IllegalArgumentException When a genre is already applied.
|
||||
*/
|
||||
fun applyGenre(genre: Genre) {
|
||||
check(mGenre == null) { "Genre is already applied" }
|
||||
|
||||
mGenre = genre
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply an album to a song.
|
||||
* @throws IllegalArgumentException When a album is already applied.
|
||||
*/
|
||||
fun applyAlbum(album: Album) {
|
||||
check(mAlbum == null) { "Album is already applied" }
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ import com.google.android.exoplayer2.SimpleExoPlayer
|
|||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -345,7 +347,11 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
|
|||
)
|
||||
}
|
||||
|
||||
return SimpleExoPlayer.Builder(this, audioRenderer).build()
|
||||
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
|
||||
|
||||
return SimpleExoPlayer.Builder(this, audioRenderer)
|
||||
.setMediaSourceFactory(DefaultMediaSourceFactory(this, extractorsFactory))
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package org.oxycblt.auxio.songs
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.Highlightable
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
import org.oxycblt.auxio.recycler.viewholders.SongViewHolder
|
||||
|
||||
/**
|
||||
* The adapter for [SongsFragment], shows basic songs without durations.
|
||||
|
@ -21,61 +16,15 @@ class SongsAdapter(
|
|||
private val data: List<Song>,
|
||||
private val doOnClick: (data: Song) -> Unit,
|
||||
private val doOnLongClick: (data: Song, view: View) -> Unit
|
||||
) : RecyclerView.Adapter<SongsAdapter.SongViewHolder>() {
|
||||
|
||||
/* private var currentSong: Song? = null
|
||||
private var lastHolder: Highlightable? = null*/
|
||||
) : RecyclerView.Adapter<SongViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
|
||||
return SongViewHolder(
|
||||
ItemSongBinding.inflate(LayoutInflater.from(parent.context))
|
||||
)
|
||||
return SongViewHolder.from(parent.context, doOnClick, doOnLongClick)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
|
||||
holder.bind(data[position])
|
||||
|
||||
/* if (currentSong != null) {
|
||||
if (data[position].id == currentSong?.id) {
|
||||
// Reset the last ViewHolder before assigning the new, correct one to be highlighted
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = holder
|
||||
holder.setHighlighted(true)
|
||||
} else {
|
||||
holder.setHighlighted(false)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/* fun setCurrentSong(song: Song?) {
|
||||
// Clear out the last ViewHolder as a song update usually signifies that this current
|
||||
// ViewHolder is likely invalid.
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
|
||||
currentSong = song
|
||||
}*/
|
||||
|
||||
inner class SongViewHolder(
|
||||
private val binding: ItemSongBinding
|
||||
) : BaseViewHolder<Song>(binding, doOnClick, doOnLongClick), Highlightable {
|
||||
private val normalTextColor = binding.songName.currentTextColor
|
||||
|
||||
override fun onBind(data: Song) {
|
||||
binding.song = data
|
||||
|
||||
binding.songName.requestLayout()
|
||||
binding.songInfo.requestLayout()
|
||||
}
|
||||
|
||||
override fun setHighlighted(isHighlighted: Boolean) {
|
||||
if (isHighlighted) {
|
||||
binding.songName.setTextColorResource(accent.first)
|
||||
} else {
|
||||
binding.songName.setTextColor(normalTextColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
|||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.Highlightable
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.getLandscapeSpans
|
||||
|
@ -70,8 +69,6 @@ class SongsFragment : Fragment() {
|
|||
}
|
||||
)
|
||||
|
||||
var lastHolder: Highlightable? = null
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.songToolbar.apply {
|
||||
|
@ -106,48 +103,6 @@ class SongsFragment : Fragment() {
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
/*
|
||||
Unused, not needed for SongsFragment
|
||||
TODO: Move this code over to AlbumDetailFragment
|
||||
playbackModel.song.observe(viewLifecycleOwner) { song ->
|
||||
if (playbackModel.mode.value == PlaybackMode.ALL_SONGS) {
|
||||
songAdapter.setCurrentSong(song)
|
||||
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
|
||||
if (song != null) {
|
||||
val pos = musicStore.songs.indexOfFirst { it.id == song.id }
|
||||
|
||||
// Check if the ViewHolder for this song is visible, if it is then highlight it.
|
||||
// If it isn't, SongsAdapter will take care of it when it is visible.
|
||||
binding.songRecycler.layoutManager?.findViewByPosition(pos)?.let { child ->
|
||||
binding.songRecycler.getChildViewHolder(child)?.let {
|
||||
lastHolder = it as Highlightable
|
||||
|
||||
lastHolder?.setHighlighted(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clear the viewholders if the mode isnt ALL_SONGS
|
||||
songAdapter.setCurrentSong(null)
|
||||
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
}
|
||||
}
|
||||
|
||||
playbackModel.isInUserQueue.observe(viewLifecycleOwner) {
|
||||
if (it) {
|
||||
// Remove any highlighted ViewHolders if the playback is in the user queue.
|
||||
songAdapter.setCurrentSong(null)
|
||||
lastHolder?.setHighlighted(false)
|
||||
lastHolder = null
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
setupFastScroller(binding)
|
||||
|
||||
logD("Fragment created.")
|
||||
|
|
Loading…
Reference in a new issue