Skip to content

Commit

Permalink
home: re-add sorting
Browse files Browse the repository at this point in the history
Re-add sorting to HomeFragment, except heavily improved. The major
improvement here is the addition of song sorting, which was a heavily
requested feature judging by #16. The setting does not save yet and
is not present in the detail fragments, but it is still a major
milestone for the new home ui.
  • Loading branch information
OxygenCobalt committed Sep 5, 2021
1 parent 0fc8f1c commit dae334b
Show file tree
Hide file tree
Showing 39 changed files with 371 additions and 143 deletions.
6 changes: 6 additions & 0 deletions app/src/main/java/org/oxycblt/auxio/home/HomeAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import org.oxycblt.auxio.ui.ArtistViewHolder
import org.oxycblt.auxio.ui.GenreViewHolder
import org.oxycblt.auxio.ui.SongViewHolder

/**
* A universal adapter for displaying data in [HomeFragment].
*/
class HomeAdapter(
private val doOnClick: (data: BaseModel) -> Unit,
private val doOnLongClick: (view: View, data: BaseModel) -> Unit
Expand Down Expand Up @@ -89,6 +92,9 @@ class HomeAdapter(
fun updateData(newData: List<BaseModel>) {
data = newData

// I would use ListAdapter instead of this inefficient invalidate call, but they still
// haven't fixed the issue where ListAdapter's calculations will cause wild scrolling
// for basically no reason.
notifyDataSetChanged()
}
}
107 changes: 79 additions & 28 deletions app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ package org.oxycblt.auxio.home

import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.view.iterator
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
Expand All @@ -48,13 +50,9 @@ import org.oxycblt.auxio.util.makeScrollingViewFade
/**
* The main "Launching Point" fragment of Auxio, allowing navigation to the detail
* views for each respective fragment.
* TODO: Re-add sorting (but new and improved)
* It will require a new SortMode to be made simply for compat. Migrate the old SortMode
* eventually.
* @author OxygenCobalt
*/
class HomeFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val homeModel: HomeViewModel by activityViewModels()

Expand All @@ -64,6 +62,7 @@ class HomeFragment : Fragment() {
savedInstanceState: Bundle?
): View {
val binding = FragmentHomeBinding.inflate(inflater)
val sortItem: MenuItem

// --- UI SETUP ---

Expand All @@ -75,20 +74,35 @@ class HomeFragment : Fragment() {

binding.homeAppbar.makeScrollingViewFade(binding.homeToolbar)

binding.homeToolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_settings -> {
parentFragment?.parentFragment?.findNavController()?.navigate(
MainFragmentDirections.actionShowSettings()
)
}
binding.homeToolbar.apply {
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_settings -> {
parentFragment?.parentFragment?.findNavController()?.navigate(
MainFragmentDirections.actionShowSettings()
)
}

R.id.action_search -> {
findNavController().navigate(HomeFragmentDirections.actionShowSearch())
}

R.id.action_search -> {
findNavController().navigate(HomeFragmentDirections.actionShowSearch())
R.id.submenu_sorting -> { }

// Sorting option was selected, check then and update the mode
else -> {
item.isChecked = true

homeModel.updateCurrentSort(
requireNotNull(LibSortMode.fromId(item.itemId))
)
}
}

true
}

true
sortItem = menu.findItem(R.id.submenu_sorting)
}

binding.homePager.apply {
Expand Down Expand Up @@ -121,24 +135,13 @@ class HomeFragment : Fragment() {
// page transitions.
offscreenPageLimit = homeModel.tabs.value!!.size

// ViewPager2 tends to garble any scrolling view events that occur within it's
// fragments, so we fix that by instructing our AppBarLayout to follow the specific
// view we have just selected.
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
binding.homeAppbar.liftOnScrollTargetViewId =
when (homeModel.tabs.value!![position]) {
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
}
}
override fun onPageSelected(position: Int) = homeModel.updateCurrentTab(position)
})
}

TabLayoutMediator(binding.homeTabs, binding.homePager) { tab, pos ->
val labelRes = when (requireNotNull(homeModel.tabs.value)[pos]) {
val labelRes = when (homeModel.tabs.value!![pos]) {
DisplayMode.SHOW_SONGS -> R.string.lbl_songs
DisplayMode.SHOW_ALBUMS -> R.string.lbl_albums
DisplayMode.SHOW_ARTISTS -> R.string.lbl_artists
Expand All @@ -150,6 +153,40 @@ class HomeFragment : Fragment() {

// --- VIEWMODEL SETUP ---

homeModel.curTab.observe(viewLifecycleOwner) { tab ->
binding.homeAppbar.liftOnScrollTargetViewId = when (requireNotNull(tab)) {
DisplayMode.SHOW_SONGS -> {
updateSortMenu(sortItem, homeModel.songSortMode)

R.id.home_song_list
}

DisplayMode.SHOW_ALBUMS -> {
updateSortMenu(sortItem, homeModel.albumSortMode) { id ->
id != R.id.option_sort_album
}

R.id.home_album_list
}

DisplayMode.SHOW_ARTISTS -> {
updateSortMenu(sortItem, homeModel.artistSortMode) { id ->
id == R.id.option_sort_asc || id == R.id.option_sort_dsc
}

R.id.home_artist_list
}

DisplayMode.SHOW_GENRES -> {
updateSortMenu(sortItem, homeModel.genreSortMode) { id ->
id == R.id.option_sort_asc || id == R.id.option_sort_dsc
}

R.id.home_genre_list
}
}
}

detailModel.navToItem.observe(viewLifecycleOwner) { item ->
// The AppBarLayout gets confused and collapses when we navigate too fast, wait for it
// to draw before we continue.
Expand Down Expand Up @@ -182,10 +219,24 @@ class HomeFragment : Fragment() {
return binding.root
}

private fun updateSortMenu(
item: MenuItem,
toHighlight: LibSortMode,
isVisible: (Int) -> Boolean = { true }
) {
for (option in item.subMenu) {
if (option.itemId == toHighlight.itemId) {
option.isChecked = true
}

option.isVisible = isVisible(option.itemId)
}
}

private inner class HomePagerAdapter :
FragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle) {

override fun getItemCount(): Int = requireNotNull(homeModel.tabs.value).size
override fun getItemCount(): Int = homeModel.tabs.value!!.size
override fun createFragment(position: Int): Fragment = HomeListFragment.new(position)
}
}
35 changes: 13 additions & 22 deletions app/src/main/java/org/oxycblt/auxio/home/HomeListFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.LiveData
import androidx.navigation.fragment.findNavController
import org.oxycblt.auxio.BuildConfig
Expand All @@ -38,7 +38,6 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.DisplayMode
import org.oxycblt.auxio.ui.newMenu
import org.oxycblt.auxio.ui.sliceArticle
import org.oxycblt.auxio.util.applySpans
import org.oxycblt.auxio.util.logD

Expand All @@ -47,8 +46,8 @@ import org.oxycblt.auxio.util.logD
* should be created using the [new] method with it's position in the ViewPager.
*/
class HomeListFragment : Fragment() {
private val homeModel: HomeViewModel by viewModels()
private val playbackModel: PlaybackViewModel by viewModels()
private val homeModel: HomeViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by activityViewModels()

override fun onCreateView(
inflater: LayoutInflater,
Expand Down Expand Up @@ -81,36 +80,36 @@ class HomeListFragment : Fragment() {
::newMenu
)

// --- ITEM SETUP ---

// Get some tab-specific values before we go ahead. More specifically, the data to use
// and the unique ID that HomeFragment's AppBarLayout uses to determine lift state.
val pos = requireNotNull(arguments).getInt(ARG_POS)

@IdRes val customId: Int
val toObserve: LiveData<out List<BaseModel>>
val homeData: LiveData<out List<BaseModel>>

when (requireNotNull(homeModel.tabs.value)[pos]) {
when (homeModel.tabs.value!![pos]) {
DisplayMode.SHOW_SONGS -> {
customId = R.id.home_song_list
toObserve = homeModel.songs
homeData = homeModel.songs
}
DisplayMode.SHOW_ALBUMS -> {
customId = R.id.home_album_list
toObserve = homeModel.albums
homeData = homeModel.albums
}
DisplayMode.SHOW_ARTISTS -> {
customId = R.id.home_artist_list
toObserve = homeModel.artists
homeData = homeModel.artists
}
DisplayMode.SHOW_GENRES -> {
customId = R.id.home_genre_list
toObserve = homeModel.genres
homeData = homeModel.genres
}
}

// --- UI SETUP ---

binding.lifecycleOwner = viewLifecycleOwner

binding.homeRecycler.apply {
id = customId
adapter = homeAdapter
Expand All @@ -121,16 +120,8 @@ class HomeListFragment : Fragment() {
// --- VIEWMODEL SETUP ---

// Make sure that this RecyclerView has data before startup
homeAdapter.updateData(toObserve.value!!)

toObserve.observe(viewLifecycleOwner) { data ->
homeAdapter.updateData(
data.sortedWith(
compareBy(String.CASE_INSENSITIVE_ORDER) {
it.name.sliceArticle()
}
)
)
homeData.observe(viewLifecycleOwner) { data ->
homeAdapter.updateData(data)
}

logD("Fragment created")
Expand Down
85 changes: 69 additions & 16 deletions app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,85 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.DisplayMode

/**
* The ViewModel for managing [HomeFragment]'s data and sorting modes.
*/
class HomeViewModel : ViewModel() {
private val mGenres = MutableLiveData(listOf<Genre>())
val genres: LiveData<List<Genre>> get() = mGenres

private val mArtists = MutableLiveData(listOf<Artist>())
val artists: LiveData<List<Artist>> get() = mArtists
private val mSongs = MutableLiveData(listOf<Song>())
val songs: LiveData<List<Song>> get() = mSongs

private val mAlbums = MutableLiveData(listOf<Album>())
val albums: LiveData<List<Album>> get() = mAlbums

private val mSongs = MutableLiveData(listOf<Song>())
val songs: LiveData<List<Song>> get() = mSongs
private val mArtists = MutableLiveData(listOf<Artist>())
val artists: LiveData<List<Artist>> get() = mArtists

private val mTabs = MutableLiveData(arrayOf<DisplayMode>())
private val mGenres = MutableLiveData(listOf<Genre>())
val genres: LiveData<List<Genre>> get() = mGenres

private val mTabs = MutableLiveData(
arrayOf(
DisplayMode.SHOW_SONGS, DisplayMode.SHOW_ALBUMS,
DisplayMode.SHOW_ARTISTS, DisplayMode.SHOW_GENRES
)
)
val tabs: LiveData<Array<DisplayMode>> = mTabs

private val mCurTab = MutableLiveData(mTabs.value!![0])
val curTab: LiveData<DisplayMode> = mCurTab

var genreSortMode = LibSortMode.ASCENDING
private set

var artistSortMode = LibSortMode.ASCENDING
private set

var albumSortMode = LibSortMode.ASCENDING
private set

var songSortMode = LibSortMode.ASCENDING
private set

private val musicStore = MusicStore.getInstance()

init {
mGenres.value = musicStore.genres
mArtists.value = musicStore.artists
mAlbums.value = musicStore.albums
mSongs.value = musicStore.songs
mTabs.value = arrayOf(
DisplayMode.SHOW_SONGS, DisplayMode.SHOW_ALBUMS,
DisplayMode.SHOW_ARTISTS, DisplayMode.SHOW_GENRES
)
mSongs.value = songSortMode.sortSongs(musicStore.songs)
mAlbums.value = albumSortMode.sortAlbums(musicStore.albums)
mArtists.value = artistSortMode.sortModels(musicStore.artists)
mGenres.value = genreSortMode.sortModels(musicStore.genres)
}

/**
* Update the current tab based off of the new ViewPager position.
*/
fun updateCurrentTab(pos: Int) {
val mode = mTabs.value!![pos]

mCurTab.value = mode
}

/**
* Update the currently displayed item's [LibSortMode].
*/
fun updateCurrentSort(sort: LibSortMode) {
when (mCurTab.value) {
DisplayMode.SHOW_SONGS -> {
songSortMode = sort
mSongs.value = sort.sortSongs(mSongs.value!!)
}

DisplayMode.SHOW_ALBUMS -> {
albumSortMode = sort
mAlbums.value = sort.sortAlbums(mAlbums.value!!)
}
DisplayMode.SHOW_ARTISTS -> {
artistSortMode = sort
mArtists.value = sort.sortModels(mArtists.value!!)
}
DisplayMode.SHOW_GENRES -> {
genreSortMode = sort
mGenres.value = sort.sortModels(mGenres.value!!)
}
}
}
}
Loading

0 comments on commit dae334b

Please sign in to comment.