music: update search results as well
This commit is contained in:
parent
583e984c70
commit
02b7acd1c5
4 changed files with 56 additions and 40 deletions
|
|
@ -20,6 +20,7 @@ package org.oxycblt.auxio.music.service
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.session.MediaSession.ControllerInfo
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
@ -51,11 +52,14 @@ constructor(
|
||||||
) : MusicRepository.UpdateListener {
|
) : MusicRepository.UpdateListener {
|
||||||
private val browserJob = Job()
|
private val browserJob = Job()
|
||||||
private val searchScope = CoroutineScope(browserJob + Dispatchers.Default)
|
private val searchScope = CoroutineScope(browserJob + Dispatchers.Default)
|
||||||
|
private val searchSubscribers = mutableMapOf<ControllerInfo, String>()
|
||||||
private val searchResults = mutableMapOf<String, Deferred<SearchEngine.Items>>()
|
private val searchResults = mutableMapOf<String, Deferred<SearchEngine.Items>>()
|
||||||
private var invalidator: Invalidator? = null
|
private var invalidator: Invalidator? = null
|
||||||
|
|
||||||
interface Invalidator {
|
interface Invalidator {
|
||||||
fun invalidate(ids: List<String>)
|
fun invalidate(ids: List<String>)
|
||||||
|
|
||||||
|
fun invalidate(controller: ControllerInfo, query: String, itemCount: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attach(invalidator: Invalidator) {
|
fun attach(invalidator: Invalidator) {
|
||||||
|
|
@ -93,9 +97,16 @@ constructor(
|
||||||
|
|
||||||
if (invalidateSearch) {
|
if (invalidateSearch) {
|
||||||
for (entry in searchResults.entries) {
|
for (entry in searchResults.entries) {
|
||||||
entry.value.cancel()
|
searchResults[entry.key]?.cancel()
|
||||||
}
|
}
|
||||||
searchResults.clear()
|
searchResults.clear()
|
||||||
|
|
||||||
|
for (entry in searchSubscribers.entries) {
|
||||||
|
if (searchResults[entry.value] != null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
searchResults[entry.value] = searchTo(entry.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,7 +201,8 @@ constructor(
|
||||||
is Genre -> {
|
is Genre -> {
|
||||||
val artists = GENRE_ARTISTS_SORT.artists(item.artists)
|
val artists = GENRE_ARTISTS_SORT.artists(item.artists)
|
||||||
val songs = listSettings.genreSongSort.songs(item.songs)
|
val songs = listSettings.genreSongSort.songs(item.songs)
|
||||||
artists.map { it.toMediaItem(context) } + songs.map { it.toMediaItem(context, null) }
|
artists.map { it.toMediaItem(context) } +
|
||||||
|
songs.map { it.toMediaItem(context, null) }
|
||||||
}
|
}
|
||||||
is Playlist -> {
|
is Playlist -> {
|
||||||
item.songs.map { it.toMediaItem(context, item) }
|
item.songs.map { it.toMediaItem(context, item) }
|
||||||
|
|
@ -200,20 +212,17 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun prepareSearch(query: String): Int {
|
suspend fun prepareSearch(query: String, controller: ControllerInfo) {
|
||||||
val deviceLibrary = musicRepository.deviceLibrary
|
searchSubscribers[controller] = query
|
||||||
val userLibrary = musicRepository.userLibrary
|
val existing = searchResults[query]
|
||||||
if (deviceLibrary == null || userLibrary == null) {
|
if (existing == null) {
|
||||||
return 0
|
val new = searchTo(query)
|
||||||
|
searchResults[query] = new
|
||||||
|
new.await()
|
||||||
|
} else {
|
||||||
|
val items = existing.await()
|
||||||
|
invalidator?.invalidate(controller, query, items.count())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.isEmpty()) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
val deferred = searchTo(query, deviceLibrary, userLibrary)
|
|
||||||
searchResults[query] = deferred
|
|
||||||
return deferred.await().count()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSearchResult(
|
suspend fun getSearchResult(
|
||||||
|
|
@ -221,22 +230,8 @@ constructor(
|
||||||
page: Int,
|
page: Int,
|
||||||
pageSize: Int,
|
pageSize: Int,
|
||||||
): List<MediaItem>? {
|
): List<MediaItem>? {
|
||||||
val deviceLibrary = musicRepository.deviceLibrary
|
val deferred = searchResults[query] ?: searchTo(query).also { searchResults[query] = it }
|
||||||
val userLibrary = musicRepository.userLibrary
|
return deferred.await().concat().paginate(page, pageSize)
|
||||||
if (deviceLibrary == null || userLibrary == null) {
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.isEmpty()) {
|
|
||||||
return listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
val existing = searchResults[query]
|
|
||||||
if (existing != null) {
|
|
||||||
return existing.await().concat().paginate(page, pageSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchTo(query, deviceLibrary, userLibrary).await().concat().paginate(page, pageSize)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SearchEngine.Items.concat(): MutableList<MediaItem> {
|
private fun SearchEngine.Items.concat(): MutableList<MediaItem> {
|
||||||
|
|
@ -279,8 +274,13 @@ constructor(
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchTo(query: String, deviceLibrary: DeviceLibrary, userLibrary: UserLibrary) =
|
private fun searchTo(query: String) =
|
||||||
searchScope.async {
|
searchScope.async {
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
return@async SearchEngine.Items()
|
||||||
|
}
|
||||||
|
val deviceLibrary = musicRepository.deviceLibrary ?: return@async SearchEngine.Items()
|
||||||
|
val userLibrary = musicRepository.userLibrary ?: return@async SearchEngine.Items()
|
||||||
val items =
|
val items =
|
||||||
SearchEngine.Items(
|
SearchEngine.Items(
|
||||||
deviceLibrary.songs,
|
deviceLibrary.songs,
|
||||||
|
|
@ -288,7 +288,13 @@ constructor(
|
||||||
deviceLibrary.artists,
|
deviceLibrary.artists,
|
||||||
deviceLibrary.genres,
|
deviceLibrary.genres,
|
||||||
userLibrary.playlists)
|
userLibrary.playlists)
|
||||||
searchEngine.search(items, query)
|
val results = searchEngine.search(items, query)
|
||||||
|
for (entry in searchSubscribers.entries) {
|
||||||
|
if (entry.value == query) {
|
||||||
|
invalidator?.invalidate(entry.key, query, results.count())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<MediaItem>.paginate(page: Int, pageSize: Int): List<MediaItem>? {
|
private fun List<MediaItem>.paginate(page: Int, pageSize: Int): List<MediaItem>? {
|
||||||
|
|
|
||||||
|
|
@ -224,8 +224,8 @@ constructor(
|
||||||
): ListenableFuture<LibraryResult<Void>> =
|
): ListenableFuture<LibraryResult<Void>> =
|
||||||
waitScope
|
waitScope
|
||||||
.async {
|
.async {
|
||||||
val count = mediaItemBrowser.prepareSearch(query)
|
mediaItemBrowser.prepareSearch(query, browser)
|
||||||
session.notifySearchResultChanged(browser, query, count, params)
|
// Invalidator will send the notify result
|
||||||
LibraryResult.ofVoid()
|
LibraryResult.ofVoid()
|
||||||
}
|
}
|
||||||
.asListenableFuture()
|
.asListenableFuture()
|
||||||
|
|
@ -260,4 +260,12 @@ constructor(
|
||||||
mediaSession.notifyChildrenChanged(id, Int.MAX_VALUE, null)
|
mediaSession.notifyChildrenChanged(id, Int.MAX_VALUE, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate(
|
||||||
|
controller: MediaSession.ControllerInfo,
|
||||||
|
query: String,
|
||||||
|
itemCount: Int
|
||||||
|
) {
|
||||||
|
mediaSession.notifySearchResultChanged(controller, query, itemCount, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,11 @@ interface SearchEngine {
|
||||||
* @param playlists A list of [Playlist], null if empty.
|
* @param playlists A list of [Playlist], null if empty.
|
||||||
*/
|
*/
|
||||||
data class Items(
|
data class Items(
|
||||||
val songs: Collection<Song>?,
|
val songs: Collection<Song>? = null,
|
||||||
val albums: Collection<Album>?,
|
val albums: Collection<Album>? = null,
|
||||||
val artists: Collection<Artist>?,
|
val artists: Collection<Artist>? = null,
|
||||||
val genres: Collection<Genre>?,
|
val genres: Collection<Genre>? = null,
|
||||||
val playlists: Collection<Playlist>?
|
val playlists: Collection<Playlist>? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
app/src/main/java/org/oxycblt/auxio/tasker/Tasker.kt
Normal file
2
app/src/main/java/org/oxycblt/auxio/tasker/Tasker.kt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
package org.oxycblt.auxio.tasker
|
||||||
|
|
||||||
Loading…
Reference in a new issue