diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index ca91f635c..d152ed4c1 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -47,6 +47,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.DisplayMode import org.oxycblt.auxio.ui.SortMode import org.oxycblt.auxio.util.applyEdge @@ -59,6 +60,7 @@ import org.oxycblt.auxio.util.logE * @author OxygenCobalt */ class HomeFragment : Fragment() { + private val playbackModel: PlaybackViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels() private val homeModel: HomeViewModel by activityViewModels() @@ -68,6 +70,7 @@ class HomeFragment : Fragment() { savedInstanceState: Bundle? ): View { val binding = FragmentHomeBinding.inflate(inflater) + var bottomPadding = 0 val sortItem: MenuItem // --- UI SETUP --- @@ -75,6 +78,8 @@ class HomeFragment : Fragment() { binding.lifecycleOwner = viewLifecycleOwner binding.applyEdge { bars -> + bottomPadding = bars.bottom + updateFabPadding(binding, bottomPadding) binding.homeAppbar.updatePadding(top = bars.top) } @@ -190,6 +195,10 @@ class HomeFragment : Fragment() { }) } + binding.homeFab.setOnClickListener { + playbackModel.shuffleAll() + } + TabLayoutMediator(binding.homeTabs, binding.homePager) { tab, pos -> tab.setText(homeModel.tabs[pos].string) }.attach() @@ -206,38 +215,28 @@ class HomeFragment : Fragment() { } } - homeModel.curTab.observe(viewLifecycleOwner) { tab -> + homeModel.curTab.observe(viewLifecycleOwner) { t -> + val tab = requireNotNull(t) + // Make sure that we update the scrolling view and allowed menu items before whenever // the tab changes. - val targetView = when (requireNotNull(tab)) { - DisplayMode.SHOW_SONGS -> { - updateSortMenu(sortItem, tab) - R.id.home_song_list + when (tab) { + DisplayMode.SHOW_SONGS -> updateSortMenu(sortItem, tab) + + DisplayMode.SHOW_ALBUMS -> updateSortMenu(sortItem, tab) { id -> + id != R.id.option_sort_album } - DisplayMode.SHOW_ALBUMS -> { - updateSortMenu(sortItem, tab) { id -> id != R.id.option_sort_album } - R.id.home_album_list + DisplayMode.SHOW_ARTISTS -> updateSortMenu(sortItem, tab) { id -> + id == R.id.option_sort_asc || id == R.id.option_sort_dsc } - DisplayMode.SHOW_ARTISTS -> { - updateSortMenu(sortItem, tab) { id -> - id == R.id.option_sort_asc || id == R.id.option_sort_dsc - } - - R.id.home_artist_list - } - - DisplayMode.SHOW_GENRES -> { - updateSortMenu(sortItem, tab) { id -> - id == R.id.option_sort_asc || id == R.id.option_sort_dsc - } - - R.id.home_genre_list + DisplayMode.SHOW_GENRES -> updateSortMenu(sortItem, tab) { id -> + id == R.id.option_sort_asc || id == R.id.option_sort_dsc } } - binding.homeAppbar.liftOnScrollTargetViewId = targetView + binding.homeAppbar.liftOnScrollTargetViewId = tab.viewId } detailModel.navToItem.observe(viewLifecycleOwner) { item -> @@ -267,6 +266,10 @@ class HomeFragment : Fragment() { } } + playbackModel.song.observe(viewLifecycleOwner) { + updateFabPadding(binding, bottomPadding) + } + logD("Fragment Created.") return binding.root @@ -288,6 +291,30 @@ class HomeFragment : Fragment() { } } + private fun updateFabPadding( + binding: FragmentHomeBinding, + bottomPadding: Int + ) { + // To get our FAB to work with edge-to-edge, we need keep track of the bar view and update + // the padding based off of that. However, we can't use the shared method here since FABs + // don't respect padding, so we duplicate the code here except with the margins instead. + val fabParams = binding.homeFab.layoutParams as CoordinatorLayout.LayoutParams + val baseSpacing = resources.getDimensionPixelSize(R.dimen.spacing_medium) + + if (playbackModel.song.value == null) { + fabParams.bottomMargin = baseSpacing + bottomPadding + } else { + fabParams.bottomMargin = baseSpacing + } + } + + private val DisplayMode.viewId: Int get() = when (this) { + DisplayMode.SHOW_SONGS -> R.id.home_song_list + DisplayMode.SHOW_ALBUMS -> R.id.home_album_list + DisplayMode.SHOW_ARTISTS -> R.id.home_artist_list + DisplayMode.SHOW_GENRES -> R.id.home_genre_list + } + private inner class HomePagerAdapter : FragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle) { diff --git a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/Md2PopupBackground.kt b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/Md2PopupBackground.kt index 4bded0af5..829489ccf 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/fastscroll/Md2PopupBackground.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/fastscroll/Md2PopupBackground.kt @@ -38,7 +38,7 @@ import kotlin.math.sqrt * The custom drawable used as FastScrollRecyclerView's popup background. * This is an adaptation from AndroidFastScroll's MD2 theme. * - * Attributions as per the Apache 2.0 license: + * Attributions as per the Apache 2.0 license: * ORIGINAL AUTHOR: Zhanghai [https://github.com/zhanghai] * PROJECT: Android Fast Scroll [https://github.com/zhanghai/AndroidFastScroll] * MODIFIER: OxygenCobalt [https://github.com/] diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt index 4121f9cf9..792c34d94 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/SongListFragment.kt @@ -22,9 +22,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.R -import org.oxycblt.auxio.databinding.ItemPlayShuffleBinding import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.ui.DisplayMode import org.oxycblt.auxio.ui.SongViewHolder @@ -32,7 +30,6 @@ import org.oxycblt.auxio.ui.SortMode import org.oxycblt.auxio.ui.newMenu import org.oxycblt.auxio.ui.sliceArticle import org.oxycblt.auxio.util.applySpans -import org.oxycblt.auxio.util.inflater class SongListFragment : HomeListFragment() { override fun onCreateView( @@ -57,95 +54,34 @@ class SongListFragment : HomeListFragment() { override val popupProvider: (Int) -> String get() = { idx -> - if (idx != 0) { - val song = homeModel.songs.value!![idx] + val song = homeModel.songs.value!![idx] - when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS)) { - SortMode.ASCENDING, SortMode.DESCENDING -> song.name.sliceArticle() - .first().uppercase() + when (homeModel.getSortForDisplay(DisplayMode.SHOW_SONGS)) { + SortMode.ASCENDING, SortMode.DESCENDING -> song.name.sliceArticle() + .first().uppercase() - SortMode.ARTIST -> song.album.artist.name.sliceArticle() - .first().uppercase() + SortMode.ARTIST -> song.album.artist.name.sliceArticle() + .first().uppercase() - SortMode.ALBUM -> song.album.name.sliceArticle() - .first().uppercase() + SortMode.ALBUM -> song.album.name.sliceArticle() + .first().uppercase() - SortMode.YEAR -> song.album.year.toString() - } - } else { - "" + SortMode.YEAR -> song.album.year.toString() } } inner class SongsAdapter( private val doOnClick: (data: Song) -> Unit, private val doOnLongClick: (view: View, data: Song) -> Unit, - ) : HomeAdapter() { - override fun getItemCount(): Int { - return if (data.isNotEmpty()) { - data.size + 1 // Make space for the play/shuffle header - } else { - data.size - } + ) : HomeAdapter() { + override fun getItemCount(): Int = data.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder { + return SongViewHolder.from(parent.context, doOnClick, doOnLongClick) } - override fun getItemViewType(position: Int): Int { - return if (position == 0) { - PLAY_ITEM_TYPE - } else { - SongViewHolder.ITEM_TYPE - } + override fun onBindViewHolder(holder: SongViewHolder, position: Int) { + holder.bind(data[position]) } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return when (viewType) { - PLAY_ITEM_TYPE -> PlayViewHolder( - ItemPlayShuffleBinding.inflate(parent.context.inflater) - ) - - SongViewHolder.ITEM_TYPE -> SongViewHolder.from( - parent.context, doOnClick, doOnLongClick - ) - - else -> error("Invalid viewholder item type.") - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is SongViewHolder) { - holder.bind(data[position - 1]) - } - } - } - - /** - * The viewholder for the play/shuffle header on the song header. - * Using a FAB would have been more conventional here, but it's so difficult to get a FAB - * to play along with edge-to-edge and nested RecyclerView instances to the point where I - * may as well not bother. - */ - private inner class PlayViewHolder( - binding: ItemPlayShuffleBinding - ) : RecyclerView.ViewHolder(binding.root) { - init { - // Force the layout to *actually* be the screen width. - // We can't inherit BaseViewHolder here since this ViewHolder isn't really connected - // to an item. - binding.root.layoutParams = RecyclerView.LayoutParams( - RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT - ) - - binding.playButton.setOnClickListener { - playbackModel.playAll() - } - - binding.shuffleButton.setOnClickListener { - playbackModel.shuffleAll() - } - } - } - - companion object { - const val PLAY_ITEM_TYPE = 0xA00E } } diff --git a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt index 298db1efd..0f08aff94 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/ViewUtil.kt @@ -196,9 +196,10 @@ fun View.applyEdge(onApply: (Rect) -> Unit) { * Auxio things should be better. * TODO: Get rid of this get rid of this get rid of this */ -fun RecyclerView.applyEdgeRespectingBar( +fun View.applyEdgeRespectingBar( playbackModel: PlaybackViewModel, - viewLifecycleOwner: LifecycleOwner + viewLifecycleOwner: LifecycleOwner, + initialPadding: Int = 0 ) { var bottomPadding = 0 @@ -208,7 +209,7 @@ fun RecyclerView.applyEdgeRespectingBar( if (playbackModel.song.value == null) { updatePadding(bottom = bottomPadding) } else { - updatePadding(bottom = 0) + updatePadding(bottom = initialPadding) } } @@ -216,7 +217,7 @@ fun RecyclerView.applyEdgeRespectingBar( if (song == null) { updatePadding(bottom = bottomPadding) } else { - updatePadding(bottom = 0) + updatePadding(bottom = initialPadding) } } } diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 540a89941..0bb4592eb 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -48,5 +48,16 @@ app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" tools:layout="@layout/fragment_home_list" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_play_shuffle.xml b/app/src/main/res/layout/item_play_shuffle.xml deleted file mode 100644 index ce010b3f4..000000000 --- a/app/src/main/res/layout/item_play_shuffle.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3359c3586..fdb3ceaf3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -119,6 +119,7 @@ Skip to last song Change repeat mode Turn shuffle on or off + Shuffle all songs Clear queue Remove this queue item