Add genre support to search
Add the ability for genres to show up in search, as long as the library is currently displaying them.
This commit is contained in:
parent
127f700700
commit
711d9a0b00
15 changed files with 50 additions and 27 deletions
|
@ -34,7 +34,7 @@ TODOs surrounded with !s are things I tried to do, but failed for reasons includ
|
|||
- Fix issue where fast navigations will cause the app to not display anything
|
||||
|
||||
/other/
|
||||
- Collapse show constants into ShowMode enum
|
||||
- Standardize fragment init
|
||||
|
||||
To be added:
|
||||
/prefs/
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.view.LayoutInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.forEach
|
||||
|
@ -58,8 +59,9 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
val item = findItem(R.id.action_search)
|
||||
val searchView = item.actionView as SearchView
|
||||
|
||||
// Set up the SearchView itself
|
||||
searchView.queryHint = getString(R.string.hint_search_library)
|
||||
searchView.findViewById<ImageView>(androidx.appcompat.R.id.search_go_btn)
|
||||
.setImageResource(R.drawable.ic_back)
|
||||
|
||||
searchView.setOnQueryTextListener(this@LibraryFragment)
|
||||
searchView.setOnQueryTextFocusChangeListener { _, hasFocus ->
|
||||
libraryModel.updateSearchFocusStatus(hasFocus)
|
||||
|
@ -139,7 +141,9 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
|
||||
libraryModel.searchResults.observe(viewLifecycleOwner) {
|
||||
if (libraryModel.searchHasFocus) {
|
||||
searchAdapter.submitList(it)
|
||||
searchAdapter.submitList(it) {
|
||||
binding.libraryRecycler.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ class LibraryViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
fun updateSearchQuery(query: String, musicModel: MusicViewModel) {
|
||||
// Don't bother if the query is blank.
|
||||
if (query == "") {
|
||||
resetQuery()
|
||||
|
||||
|
@ -52,18 +53,32 @@ class LibraryViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
// Search MusicViewModel for all the items [Artists, Albums, Songs] that contain
|
||||
// the query, and update the LiveData with those items. This is done on a seperate
|
||||
// thread as it can be a very intensive operation for large music libraries.
|
||||
// the query, and update the LiveData with those items. This is done on a separate
|
||||
// thread as it can be a very long operation for large music libraries.
|
||||
viewModelScope.launch {
|
||||
val combined = mutableListOf<BaseModel>()
|
||||
val children = showMode.value!!.getChildren()
|
||||
|
||||
val artists = musicModel.artists.value!!.filter { it.name.contains(query, true) }
|
||||
// If the Library ShowMode supports it, include artists / genres in the search.
|
||||
if (children.contains(ShowMode.SHOW_GENRES)) {
|
||||
val genres = musicModel.genres.value!!.filter { it.name.contains(query, true) }
|
||||
|
||||
if (artists.isNotEmpty()) {
|
||||
combined.add(Header(id = ShowMode.SHOW_ARTISTS.constant))
|
||||
combined.addAll(artists)
|
||||
if (genres.isNotEmpty()) {
|
||||
combined.add(Header(id = ShowMode.SHOW_GENRES.constant))
|
||||
combined.addAll(genres)
|
||||
}
|
||||
}
|
||||
|
||||
if (children.contains(ShowMode.SHOW_ARTISTS)) {
|
||||
val artists = musicModel.artists.value!!.filter { it.name.contains(query, true) }
|
||||
|
||||
if (artists.isNotEmpty()) {
|
||||
combined.add(Header(id = ShowMode.SHOW_ARTISTS.constant))
|
||||
combined.addAll(artists)
|
||||
}
|
||||
}
|
||||
|
||||
// Albums & Songs are always included.
|
||||
val albums = musicModel.albums.value!!.filter { it.name.contains(query, true) }
|
||||
|
||||
if (albums.isNotEmpty()) {
|
||||
|
|
|
@ -97,7 +97,7 @@ fun TextView.bindGenreCounts(genre: Genre) {
|
|||
R.plurals.format_artist_count, genre.numArtists, genre.numArtists
|
||||
)
|
||||
val albums = context.resources.getQuantityString(
|
||||
R.plurals.format_song_count, genre.numAlbums, genre.numAlbums
|
||||
R.plurals.format_album_count, genre.numAlbums, genre.numAlbums
|
||||
)
|
||||
|
||||
text = context.getString(R.string.format_double_counts, artists, albums)
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package org.oxycblt.auxio.recycler
|
||||
|
||||
enum class ShowMode(val constant: Long) {
|
||||
SHOW_GENRES(10), SHOW_ARTISTS(11), SHOW_ALBUMS(12), SHOW_SONGS(13);
|
||||
SHOW_GENRES(0), SHOW_ARTISTS(1), SHOW_ALBUMS(2), SHOW_SONGS(3);
|
||||
|
||||
// Make a slice of all the values that this ShowMode covers.
|
||||
// ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS
|
||||
fun getChildren(): List<ShowMode> {
|
||||
val vals = values()
|
||||
|
||||
return vals.slice(vals.indexOf(this) until vals.size)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ import org.oxycblt.auxio.music.Header
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.ClickListener
|
||||
|
||||
// Basic ViewHolders for each music model.
|
||||
|
||||
// Shared ViewHolders for each ViewModel, providing basic information
|
||||
// All new instances should be created with from(context, listener) instead of direct instantiation.
|
||||
class GenreViewHolder private constructor(
|
||||
listener: ClickListener<Genre>,
|
||||
private val binding: ItemGenreBinding
|
||||
|
|
|
@ -65,6 +65,7 @@ fun Int.toColor(context: Context): Int {
|
|||
}
|
||||
}
|
||||
|
||||
// Resolve an attribute into a color
|
||||
@ColorInt
|
||||
fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
|
||||
// Convert the attribute into its color
|
||||
|
@ -82,6 +83,7 @@ fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
|
|||
return color.toColor(context)
|
||||
}
|
||||
|
||||
// Apply a color to a Menu Item
|
||||
fun MenuItem.applyColor(color: Int) {
|
||||
SpannableString(title).apply {
|
||||
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
android:tint="@color/control_color">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z" />
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<color name="divider_color">#323232</color>
|
||||
<color name="selection_color">#484848</color>
|
||||
<color name="inactive_color">#404040</color>
|
||||
<color name="control_color">#ffffff</color>
|
||||
|
||||
<!--
|
||||
Base color set derived from Music Player GO.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<color name="divider_color">#cbcbcb</color>
|
||||
<color name="selection_color">#cbcbcb</color>
|
||||
<color name="inactive_color">#c4c4c4</color>
|
||||
<color name="control_color">#202020</color>
|
||||
|
||||
<!--
|
||||
Base color set derived from Music Player GO.
|
||||
|
|
|
@ -43,12 +43,11 @@
|
|||
<string name="placeholder_album">Unknown Album</string>
|
||||
<string name="placeholder_no_date">No Date</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting -->
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="format_info">%1$s / %2$s</string>
|
||||
<string name="format_double_info">%1$s / %2$s / %3$s</string>
|
||||
<string name="format_double_counts">%1$s, %2$s</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting with plurals -->
|
||||
<plurals name="format_song_count">
|
||||
<item quantity="one">%s Song</item>
|
||||
<item quantity="other">%s Songs</item>
|
||||
|
|
|
@ -18,10 +18,7 @@
|
|||
<item name="android:textColor">?android:attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Toolbar.SearchViewStyle" parent="Widget.AppCompat.SearchView">
|
||||
<item name="android:searchIcon">@drawable/ic_search</item>
|
||||
<item name="android:closeIcon">@drawable/ic_back</item>
|
||||
</style>
|
||||
<style name="Toolbar.SearchViewStyle" parent="Widget.AppCompat.SearchView"/>
|
||||
|
||||
<style name="DetailHeader">
|
||||
<item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
|
||||
|
|
|
@ -38,25 +38,21 @@
|
|||
<item name="colorSecondary">@color/light_blue</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.Cyan" parent="Theme.Base">
|
||||
<item name="colorPrimary">@color/cyan</item>
|
||||
<item name="colorSecondary">@color/cyan</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.Teal" parent="Theme.Base">
|
||||
<item name="colorPrimary">@color/teal</item>
|
||||
<item name="colorSecondary">@color/teal</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.Green" parent="Theme.Base">
|
||||
<item name="colorPrimary">@color/green</item>
|
||||
<item name="colorSecondary">@color/green</item>
|
||||
</style>
|
||||
|
||||
|
||||
<style name="Theme.LightGreen" parent="Theme.Base">
|
||||
<item name="colorPrimary">@color/light_green</item>
|
||||
<item name="colorSecondary">@color/light_green</item>
|
||||
|
|
Loading…
Reference in a new issue