From 69c508caec42ce77598799aaf5e27f39318ae26a Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Sat, 3 Oct 2020 09:14:49 -0600 Subject: [PATCH] Add search to LibraryFragment Add searching to LibraryFragment. --- AuxioTODO | 19 ++--- app/src/main/AndroidManifest.xml | 4 +- .../auxio/detail/ArtistDetailFragment.kt | 1 - .../auxio/detail/GenreDetailFragment.kt | 1 - .../oxycblt/auxio/library/LibraryAdapter.kt | 2 +- .../oxycblt/auxio/library/LibraryFragment.kt | 79 ++++++++++++++++--- .../oxycblt/auxio/library/LibraryViewModel.kt | 15 +++- .../org/oxycblt/auxio/recycler/SortMode.kt | 26 +++++- app/src/main/res/drawable/cursor.xml | 10 +++ app/src/main/res/drawable/ic_search.xml | 11 +++ app/src/main/res/drawable/ic_sort_none.xml | 2 +- .../main/res/layout/fragment_album_detail.xml | 2 +- .../res/layout/fragment_artist_detail.xml | 2 +- .../main/res/layout/fragment_genre_detail.xml | 2 +- app/src/main/res/layout/fragment_library.xml | 8 +- app/src/main/res/layout/fragment_songs.xml | 2 +- app/src/main/res/menu/menu_library.xml | 41 ++++++---- app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/styles.xml | 12 ++- 19 files changed, 184 insertions(+), 59 deletions(-) create mode 100644 app/src/main/res/drawable/cursor.xml create mode 100644 app/src/main/res/drawable/ic_search.xml diff --git a/AuxioTODO b/AuxioTODO index d1b4d5d43..00c8e55e5 100644 --- a/AuxioTODO +++ b/AuxioTODO @@ -1,19 +1,19 @@ TODO: -? - Could do with some more research. -! - Tried to do, but is completely broken. +TODOs surrounded with ?s are things I want to do, but need to do more research on and may be a nightmare to implement. +TODOs surrounded with !s are things I tried to do, but failed for reasons included in the TODO. I still want to add these, but theres no way for me to do them at the moment. /detail/ - ? Implement Toolbar update functionality ? -- ! Implement shared element transitions ! +- ! Implement shared element transitions [Return transitions are broken] ! /music/ - Add option to show all genres -- ! Move genres to songs ! -- ! Remove lists from music models ! -- ! Dont determine track/album/artist counts on the fly ! +- ! Move genres to songs [Wont find songs w/o genres]! +- ! Remove lists from music models [Current genre loading system makes it impossible] ! +- ! Dont determine track/album/artist counts on the fly [Audio columns for those values are broken] ! /songs/ @@ -23,12 +23,13 @@ TODO: /library/ -- Search - Exit functionality +- ? Remove gap from where I removed the overflow menu ? +- ? Add icons to overflow menu items ? - ? Show Artists, Albums, and Songs in search ? +- ? Implement filtering for above ^^^ [Will resolve gap issue] ? - ? Move into ViewPager ? -- ! Move Adapter functionality to ListAdapter! - +- ! Move Adapter functionality to ListAdapter [RecyclerView scrolls to middle/bottom when data is re-sorted] ! To be added: /prefs/ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f82214a58..8c9262ea8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Base"> - + + 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 e256c843b..923603d96 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -13,7 +13,6 @@ import androidx.navigation.fragment.navArgs import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter -import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.theme.applyDivider diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index fe682e5a5..ca0ae60ff 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -13,7 +13,6 @@ import androidx.navigation.fragment.navArgs import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter -import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.theme.applyDivider diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryAdapter.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryAdapter.kt index 01be48292..f2932f9c4 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryAdapter.kt @@ -22,7 +22,7 @@ class LibraryAdapter( private val listener: ClickListener ) : RecyclerView.Adapter() { - private var data: List + var data: List init { // Assign the data on startup depending on the type 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 77864b37a..c393a9c23 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -5,11 +5,14 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.view.forEach import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController +import androidx.transition.Fade +import androidx.transition.TransitionManager import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentLibraryBinding @@ -26,7 +29,7 @@ import org.oxycblt.auxio.theme.applyColor import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.resolveAttr -class LibraryFragment : Fragment() { +class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { private val musicModel: MusicViewModel by activityViewModels() private val libraryModel: LibraryViewModel by activityViewModels() @@ -38,12 +41,52 @@ class LibraryFragment : Fragment() { ): View? { val binding = FragmentLibraryBinding.inflate(inflater) - val artistAdapter = LibraryAdapter( + // Toolbar setup + binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable( + requireContext(), R.drawable.ic_sort_none + ) + + binding.libraryToolbar.menu.apply { + val item = findItem(R.id.action_search) + val searchView = item.actionView as SearchView + + searchView.queryHint = getString(R.string.hint_search_library) + searchView.setOnQueryTextListener(this@LibraryFragment) + searchView.setOnQueryTextFocusChangeListener { _, hasFocus -> + this.setGroupVisible(R.id.group_sorting, !hasFocus) + + // Make sure the search item will still be visible, and then do an animation + item.isVisible = !hasFocus + TransitionManager.beginDelayedTransition( + binding.libraryToolbar, Fade() + ) + item.collapseActionView() + } + } + + binding.libraryToolbar.setOnMenuItemClickListener { + if (it.itemId != R.id.action_search) { + libraryModel.updateSortMode(it) + } else { + // Do whatever this is in order to make the SearchView focusable. + (it.actionView as SearchView).isIconified = false + + // Then also do a basic animation + TransitionManager.beginDelayedTransition( + binding.libraryToolbar, Fade() + ) + it.expandActionView() + } + true + } + + val libraryAdapter = LibraryAdapter( libraryModel.showMode.value!!, ClickListener { navToItem(it) } ) - binding.libraryRecycler.adapter = artistAdapter + // RecyclerView setup + binding.libraryRecycler.adapter = libraryAdapter binding.libraryRecycler.applyDivider() binding.libraryRecycler.setHasFixedSize(true) @@ -51,7 +94,7 @@ class LibraryFragment : Fragment() { Log.d(this::class.simpleName, "Updating sort mode to $mode") // Update the adapter with the new data - artistAdapter.updateData( + libraryAdapter.updateData( when (libraryModel.showMode.value) { SHOW_GENRES -> mode.getSortedGenreList(musicModel.genres.value!!) SHOW_ARTISTS -> mode.getSortedArtistList(musicModel.artists.value!!) @@ -61,12 +104,7 @@ class LibraryFragment : Fragment() { } ) - // Then update the shown icon & the currently highlighted sort icon to reflect - // the new sorting mode. - binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable( - requireContext(), mode.iconRes - ) - + // Then update the menu item in the toolbar to reflect the new mode binding.libraryToolbar.menu.forEach { if (it.itemId == libraryModel.sortMode.value!!.toMenuId()) { it.applyColor(resolveAttr(requireContext(), R.attr.colorPrimary)) @@ -76,10 +114,17 @@ class LibraryFragment : Fragment() { } } - binding.libraryToolbar.setOnMenuItemClickListener { - libraryModel.updateSortMode(it) + libraryModel.searchQuery.observe(viewLifecycleOwner) { query -> + // Update the adapter with the new data + libraryAdapter.updateData( + when (libraryModel.showMode.value) { + SHOW_GENRES -> musicModel.genres.value!! + SHOW_ARTISTS -> musicModel.artists.value!! + SHOW_ALBUMS -> musicModel.albums.value!! - true + else -> musicModel.artists.value!! + }.filter { it.name.contains(query, true) } + ) } Log.d(this::class.simpleName, "Fragment created.") @@ -87,6 +132,14 @@ class LibraryFragment : Fragment() { return binding.root } + override fun onQueryTextSubmit(query: String): Boolean = false + + override fun onQueryTextChange(query: String): Boolean { + libraryModel.updateSearchQuery(query) + + return false + } + private fun navToItem(baseModel: BaseModel) { Log.d(this::class.simpleName, "Navigating to the detail fragment for ${baseModel.name}") diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt index 354cfa4fb..b4201b3d7 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryViewModel.kt @@ -8,7 +8,7 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.theme.SHOW_ARTISTS -class LibraryViewModel : ViewModel() { +class LibraryViewModel() : ViewModel() { // TODO: Move these to prefs when they're added private val mShowMode = MutableLiveData(SHOW_ARTISTS) val showMode: LiveData get() = mShowMode @@ -16,11 +16,14 @@ class LibraryViewModel : ViewModel() { private val mSortMode = MutableLiveData(SortMode.ALPHA_DOWN) val sortMode: LiveData get() = mSortMode + private val mSearchQuery = MutableLiveData("") + val searchQuery: LiveData get() = mSearchQuery + fun updateSortMode(item: MenuItem) { val mode = when (item.itemId) { - R.id.sort_none -> SortMode.NONE - R.id.sort_alpha_down -> SortMode.ALPHA_DOWN - R.id.sort_alpha_up -> SortMode.ALPHA_UP + R.id.option_sort_none -> SortMode.NONE + R.id.option_sort_alpha_down -> SortMode.ALPHA_DOWN + R.id.option_sort_alpha_up -> SortMode.ALPHA_UP else -> SortMode.NONE } @@ -29,4 +32,8 @@ class LibraryViewModel : ViewModel() { mSortMode.value = mode } } + + fun updateSearchQuery(query: String) { + mSearchQuery.value = query + } } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index c001ad809..1415fcf83 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -3,6 +3,7 @@ package org.oxycblt.auxio.recycler import org.oxycblt.auxio.R import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist +import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song @@ -77,13 +78,30 @@ enum class SortMode(val iconRes: Int) { } } + fun getSortedBaseModelList(list: List): List { + return when (this) { + ALPHA_UP -> list.sortedWith( + compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name } + ) + + ALPHA_DOWN -> list.sortedWith( + compareBy(String.CASE_INSENSITIVE_ORDER) { it.name } + ) + + NUMERIC_UP -> list.sortedWith(compareByDescending { it.id }) + NUMERIC_DOWN -> list.sortedWith(compareBy { it.id }) + + else -> list + } + } + fun toMenuId(): Int { return when (this) { - NONE -> R.id.sort_none - ALPHA_UP -> R.id.sort_alpha_up - ALPHA_DOWN -> R.id.sort_alpha_down + NONE -> R.id.option_sort_none + ALPHA_UP -> R.id.option_sort_alpha_up + ALPHA_DOWN -> R.id.option_sort_alpha_down - else -> R.id.sort_none + else -> R.id.option_sort_none } } } diff --git a/app/src/main/res/drawable/cursor.xml b/app/src/main/res/drawable/cursor.xml new file mode 100644 index 000000000..0af5ef6c8 --- /dev/null +++ b/app/src/main/res/drawable/cursor.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 000000000..55dcccbc7 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_sort_none.xml b/app/src/main/res/drawable/ic_sort_none.xml index 47d54f7fb..9e4362c1a 100644 --- a/app/src/main/res/drawable/ic_sort_none.xml +++ b/app/src/main/res/drawable/ic_sort_none.xml @@ -4,7 +4,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" - android:tint="?attr/colorPrimary"> + android:tint="?android:attr/colorControlNormal"> diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index bf77add7b..6100344b5 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -25,7 +25,7 @@ android:layout_height="?android:attr/actionBarSize" android:background="?android:attr/windowBackground" android:elevation="@dimen/elevation_normal" - app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold" + app:titleTextAppearance="@style/TextAppearance.Toolbar.Header" app:navigationIcon="@drawable/ic_back" app:title="@string/title_library_fragment" /> diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index fa05a4afa..7a51b7994 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -25,7 +25,7 @@ android:layout_height="?android:attr/actionBarSize" android:background="?android:attr/windowBackground" android:elevation="@dimen/elevation_normal" - app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold" + app:titleTextAppearance="@style/TextAppearance.Toolbar.Header" app:navigationIcon="@drawable/ic_back" app:title="@string/title_library_fragment" /> diff --git a/app/src/main/res/layout/fragment_genre_detail.xml b/app/src/main/res/layout/fragment_genre_detail.xml index ec2644fe2..e41ad259e 100644 --- a/app/src/main/res/layout/fragment_genre_detail.xml +++ b/app/src/main/res/layout/fragment_genre_detail.xml @@ -25,7 +25,7 @@ android:layout_height="?android:attr/actionBarSize" android:background="?android:attr/windowBackground" android:elevation="@dimen/elevation_normal" - app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold" + app:titleTextAppearance="@style/TextAppearance.Toolbar.Header" app:navigationIcon="@drawable/ic_back" app:title="@string/title_library_fragment" /> diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 24dbff0d8..cf9541635 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -7,8 +7,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:animateLayoutChanges="true" - android:descendantFocusability="blocksDescendants"> + android:animateLayoutChanges="true"> + app:titleTextAppearance="@style/TextAppearance.Toolbar.Header" + app:title="@string/title_library_fragment"/> - + + - - + android:id="@+id/action_search" + android:icon="@drawable/ic_search" + android:title="@string/label_search" + app:actionViewClass="androidx.appcompat.widget.SearchView" + app:showAsAction="collapseActionView|always" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 061a88131..136b4cf09 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,10 +17,14 @@ Artists Albums Songs + Search Default A-Z Z-A + + Search Library… + Album Cover for %s Artist Image for %s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 850bb1c3c..419f3c480 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,14 +5,24 @@ @color/background @android:color/black @font/inter + @drawable/cursor @style/AppThemeOverlay.Popup - + + + +