Update library searching
Reorder how library items are displayed when searching, no longer hiding genres/artists depending on the DisplayMode.
This commit is contained in:
parent
22ab5ad255
commit
406ba212f8
13 changed files with 102 additions and 94 deletions
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.oxycblt.auxio">
|
||||
|
||||
<application
|
||||
android:label="Auxio Debug"
|
||||
tools:replace="android:label"/>
|
||||
</manifest>
|
|
@ -1,27 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M54 29.83v28.333c-1.585-0.913-3.41-1.477-5.371-1.477-5.935 0-10.743 4.807-10.743 10.743 0 5.935 4.807 10.742 10.743 10.742 5.935 0 10.743-4.807 10.743-10.742V40.573h10.742V29.831z"
|
||||
android:strokeWidth="0.890156"
|
||||
android:strokeColor="#ffffff">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="64.75"
|
||||
android:endY="28.5"
|
||||
android:startX="60"
|
||||
android:startY="61.5"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#2196f3"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#90caf9"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
4
app/src/debug/res/values/strings.xml
Normal file
4
app/src/debug/res/values/strings.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Auxio Debug</string>
|
||||
</resources>
|
|
@ -41,6 +41,8 @@ import org.oxycblt.auxio.ui.toColor
|
|||
* search functionality.
|
||||
* FIXME: Heisenleak when navving from search
|
||||
* FIXME: Heisenleak on older versions
|
||||
* TODO: Filtering & Search order upgrades
|
||||
* TODO: Show result counts?
|
||||
*/
|
||||
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
|
||||
|
||||
|
|
|
@ -61,44 +61,80 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
|||
return
|
||||
}
|
||||
|
||||
// Search MusicStore for all the items [Artists, Albums, Songs] that contain
|
||||
// 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 = mDisplayMode.getChildren()
|
||||
|
||||
// If the Library DisplayMode supports it, include artists / genres in the search.
|
||||
if (children.contains(DisplayMode.SHOW_GENRES)) {
|
||||
val genres = musicStore.genres.filter { it.name.contains(query, true) }
|
||||
|
||||
if (genres.isNotEmpty()) {
|
||||
combined.add(Header(name = context.getString(R.string.label_genres)))
|
||||
combined.addAll(genres)
|
||||
// Searching is done in a different order depending on which items are being shown
|
||||
// E.G If albums are being shown, then they will be the first items on the list.
|
||||
when (mDisplayMode) {
|
||||
DisplayMode.SHOW_GENRES -> {
|
||||
searchForGenres(combined, query, context)
|
||||
searchForArtists(combined, query, context)
|
||||
searchForAlbums(combined, query, context)
|
||||
}
|
||||
}
|
||||
|
||||
if (children.contains(DisplayMode.SHOW_ARTISTS)) {
|
||||
val artists = musicStore.artists.filter { it.name.contains(query, true) }
|
||||
|
||||
if (artists.isNotEmpty()) {
|
||||
combined.add(Header(name = context.getString(R.string.label_artists)))
|
||||
combined.addAll(artists)
|
||||
DisplayMode.SHOW_ARTISTS -> {
|
||||
searchForArtists(combined, query, context) -
|
||||
searchForAlbums(combined, query, context)
|
||||
searchForGenres(combined, query, context)
|
||||
}
|
||||
}
|
||||
|
||||
// Albums & Songs are always included.
|
||||
val albums = musicStore.albums.filter { it.name.contains(query, true) }
|
||||
|
||||
if (albums.isNotEmpty()) {
|
||||
combined.add(Header(name = context.getString(R.string.label_albums)))
|
||||
combined.addAll(albums)
|
||||
DisplayMode.SHOW_ALBUMS -> {
|
||||
searchForAlbums(combined, query, context)
|
||||
searchForArtists(combined, query, context)
|
||||
searchForGenres(combined, query, context)
|
||||
}
|
||||
}
|
||||
|
||||
mSearchResults.value = combined
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchForGenres(
|
||||
data: MutableList<BaseModel>,
|
||||
query: String,
|
||||
context: Context
|
||||
): MutableList<BaseModel> {
|
||||
val genres = musicStore.genres.filter { it.name.contains(query, true) }
|
||||
|
||||
if (genres.isNotEmpty()) {
|
||||
data.add(Header(id = 0, name = context.getString(R.string.label_genres)))
|
||||
data.addAll(genres)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
private fun searchForArtists(
|
||||
data: MutableList<BaseModel>,
|
||||
query: String,
|
||||
context: Context
|
||||
): MutableList<BaseModel> {
|
||||
val artists = musicStore.artists.filter { it.name.contains(query, true) }
|
||||
|
||||
if (artists.isNotEmpty()) {
|
||||
data.add(Header(id = 1, name = context.getString(R.string.label_artists)))
|
||||
data.addAll(artists)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
private fun searchForAlbums(
|
||||
data: MutableList<BaseModel>,
|
||||
query: String,
|
||||
context: Context
|
||||
): MutableList<BaseModel> {
|
||||
val albums = musicStore.albums.filter { it.name.contains(query, true) }
|
||||
|
||||
if (albums.isNotEmpty()) {
|
||||
data.add(Header(id = 2, name = context.getString(R.string.label_albums)))
|
||||
data.addAll(albums)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
fun resetQuery() {
|
||||
mSearchResults.value = listOf()
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.widget.TextView
|
|||
import androidx.databinding.BindingAdapter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.ui.getPlural
|
||||
|
||||
/**
|
||||
* List of ID3 genres + Winamp extensions, each index corresponds to their int value.
|
||||
|
@ -120,12 +121,8 @@ fun TextView.bindArtistGenre(artist: Artist) {
|
|||
*/
|
||||
@BindingAdapter("artistCounts")
|
||||
fun TextView.bindArtistCounts(artist: Artist) {
|
||||
val albums = context.resources.getQuantityString(
|
||||
R.plurals.format_album_count, artist.albums.size, artist.albums.size
|
||||
)
|
||||
val songs = context.resources.getQuantityString(
|
||||
R.plurals.format_song_count, artist.songs.size, artist.songs.size
|
||||
)
|
||||
val albums = context.getPlural(R.plurals.format_album_count, artist.albums.size)
|
||||
val songs = context.getPlural(R.plurals.format_song_count, artist.songs.size)
|
||||
|
||||
text = context.getString(R.string.format_double_counts, albums, songs)
|
||||
}
|
||||
|
@ -138,10 +135,7 @@ fun TextView.bindAllAlbumDetails(album: Album) {
|
|||
text = context.getString(
|
||||
R.string.format_double_info,
|
||||
album.year.toYear(context),
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.format_song_count,
|
||||
album.songs.size, album.songs.size
|
||||
),
|
||||
context.getPlural(R.plurals.format_song_count, album.songs.size),
|
||||
album.totalDuration
|
||||
)
|
||||
}
|
||||
|
@ -154,10 +148,7 @@ fun TextView.bindAlbumInfo(album: Album) {
|
|||
text = context.getString(
|
||||
R.string.format_info,
|
||||
album.artist.name,
|
||||
context.resources.getQuantityString(
|
||||
R.plurals.format_song_count,
|
||||
album.songs.size, album.songs.size
|
||||
)
|
||||
context.getPlural(R.plurals.format_song_count, album.songs.size),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,14 +20,17 @@ import org.oxycblt.auxio.settings.SettingsManager
|
|||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Master class for the playback state. This should ***not*** be used outside of the playback module.
|
||||
* Master class (and possible god object) for the playback state.
|
||||
*
|
||||
* This should ***NOT*** be used outside of the playback module.
|
||||
* - If you want to use the playback state in the UI, use [org.oxycblt.auxio.playback.PlaybackViewModel].
|
||||
* - If you want to use the playback state with the ExoPlayer instance or system-side things,
|
||||
* use [org.oxycblt.auxio.playback.PlaybackService].
|
||||
*
|
||||
* All access should be done with [PlaybackStateManager.getInstance].
|
||||
* @author OxygenCobalt
|
||||
* // TODO: Sort queues
|
||||
*
|
||||
* TODO: Sort queues
|
||||
*/
|
||||
class PlaybackStateManager private constructor() {
|
||||
// Playback
|
||||
|
|
|
@ -3,6 +3,11 @@ package org.oxycblt.auxio.recycler
|
|||
import android.content.Context
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
|
||||
/**
|
||||
* [LinearSmoothScroller] subclass that centers the item on the screen instead of snapping to the
|
||||
* top or bottom.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class CenterSmoothScroller(context: Context, target: Int) : LinearSmoothScroller(context) {
|
||||
init {
|
||||
targetPosition = target
|
||||
|
|
|
@ -126,9 +126,8 @@ class SongsFragment : Fragment(), SearchView.OnQueryTextListener {
|
|||
if (isLandscape(resources)) {
|
||||
val spans = getLandscapeSpans(resources)
|
||||
|
||||
layoutManager = GridLayoutManager(requireContext(), GridLayoutManager.VERTICAL).also {
|
||||
it.spanCount = spans
|
||||
it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans).apply {
|
||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (binding.songRecycler.adapter == searchAdapter && position == 0)
|
||||
2 else 1
|
||||
|
|
|
@ -34,17 +34,16 @@ class SongsViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
val songs = mutableListOf<BaseModel>().also {
|
||||
it.addAll(musicStore.songs.filter { it.name.contains(query, true) }.toMutableList())
|
||||
val songs = mutableListOf<BaseModel>().also { list ->
|
||||
list.addAll(
|
||||
musicStore.songs.filter {
|
||||
it.name.contains(query, true)
|
||||
}.toMutableList()
|
||||
)
|
||||
}
|
||||
|
||||
if (songs.isNotEmpty()) {
|
||||
songs.add(
|
||||
0,
|
||||
Header(
|
||||
name = context.getString(R.string.label_songs)
|
||||
)
|
||||
)
|
||||
songs.add(0, Header(id = 0, name = context.getString(R.string.label_songs)))
|
||||
}
|
||||
|
||||
mSearchResults.value = songs
|
||||
|
|
|
@ -13,6 +13,7 @@ import android.widget.TextView
|
|||
import android.widget.Toast
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
|
@ -111,3 +112,13 @@ fun Fragment.requireCompatActivity(): AppCompatActivity {
|
|||
error("Required AppCompatActivity, got ${activity::class.simpleName} instead.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a plural.
|
||||
* @param pluralsRes Resource for the plural
|
||||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
*/
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return resources.getQuantityString(pluralsRes, value, value)
|
||||
}
|
||||
|
|
|
@ -174,5 +174,4 @@
|
|||
<item quantity="one">%s Album</item>
|
||||
<item quantity="other">%s Albums</item>
|
||||
</plurals>
|
||||
|
||||
</resources>
|
Loading…
Reference in a new issue