music: re-add search browsing
This commit is contained in:
parent
b1e871c6e1
commit
924e3d1801
4 changed files with 79 additions and 35 deletions
|
@ -105,7 +105,7 @@ class AuxioService : MediaBrowserServiceCompat(), ForegroundListener, MusicServi
|
|||
extras: Bundle?,
|
||||
result: Result<MutableList<MediaItem>>
|
||||
) {
|
||||
super.onSearch(query, extras, result)
|
||||
musicFragment.search(query, result)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
|
|
|
@ -307,21 +307,6 @@ constructor(
|
|||
// results
|
||||
// }
|
||||
|
||||
private fun List<MediaItem>.paginate(page: Int, pageSize: Int): List<MediaItem>? {
|
||||
if (page == Int.MAX_VALUE) {
|
||||
// I think if someone requests this page it more or less implies that I should
|
||||
// return all of the pages.
|
||||
return this
|
||||
}
|
||||
val start = page * pageSize
|
||||
val end = min((page + 1) * pageSize, size) // Tolerate partial page queries
|
||||
if (pageSize == 0 || start !in indices) {
|
||||
// These pages are probably invalid. Hopefully this won't backfire.
|
||||
return null
|
||||
}
|
||||
return subList(start, end).toMutableList()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
// TODO: Rely on detail item gen logic?
|
||||
val ARTIST_ALBUMS_SORT = Sort(Sort.Mode.ByDate, Sort.Direction.DESCENDING)
|
||||
|
|
|
@ -19,43 +19,41 @@
|
|||
package org.oxycblt.auxio.music.service
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import androidx.media.MediaBrowserServiceCompat.BrowserRoot
|
||||
import androidx.media.MediaBrowserServiceCompat
|
||||
import androidx.media.utils.MediaConstants
|
||||
import android.support.v4.media.MediaBrowserCompat.MediaItem
|
||||
import coil.ImageLoader
|
||||
import androidx.media.MediaBrowserServiceCompat
|
||||
import androidx.media.MediaBrowserServiceCompat.BrowserRoot
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oxycblt.auxio.ForegroundListener
|
||||
import org.oxycblt.auxio.ForegroundServiceNotification
|
||||
import org.oxycblt.auxio.music.IndexingState
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.MusicSettings
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.search.SearchEngine
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
import javax.inject.Inject
|
||||
|
||||
class MusicServiceFragment
|
||||
@Inject
|
||||
constructor(
|
||||
@ApplicationContext context: Context,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val indexer: Indexer,
|
||||
private val browser: MusicBrowser,
|
||||
private val musicBrowser: MusicBrowser,
|
||||
private val musicRepository: MusicRepository,
|
||||
private val musicSettings: MusicSettings,
|
||||
private val searchEngine: SearchEngine,
|
||||
private val contentObserver: SystemContentObserver,
|
||||
) : MusicBrowser.Invalidator, MusicSettings.Listener {
|
||||
private val indexingNotification = IndexingNotification(context)
|
||||
private val observingNotification = ObservingNotification(context)
|
||||
private var invalidator: Invalidator? = null
|
||||
private var foregroundListener: ForegroundListener? = null
|
||||
private val dispatchJob = Job()
|
||||
private val dispatchScope = CoroutineScope(dispatchJob + Dispatchers.Default)
|
||||
|
||||
interface Invalidator {
|
||||
fun invalidateMusic(mediaId: String)
|
||||
|
@ -64,7 +62,7 @@ constructor(
|
|||
fun attach(foregroundListener: ForegroundListener, invalidator: Invalidator) {
|
||||
this.invalidator = invalidator
|
||||
indexer.attach(foregroundListener)
|
||||
browser.attach(this)
|
||||
musicBrowser.attach(this)
|
||||
contentObserver.attach()
|
||||
musicSettings.registerListener(this)
|
||||
}
|
||||
|
@ -72,7 +70,8 @@ constructor(
|
|||
fun release() {
|
||||
musicSettings.unregisterListener(this)
|
||||
contentObserver.release()
|
||||
browser.release()
|
||||
dispatchJob.cancel()
|
||||
musicBrowser.release()
|
||||
indexer.release()
|
||||
invalidator = null
|
||||
}
|
||||
|
@ -127,10 +126,53 @@ constructor(
|
|||
fun getRoot() = BrowserRoot(Category.ROOT.id, null)
|
||||
|
||||
fun getItem(mediaId: String, result: MediaBrowserServiceCompat.Result<MediaItem>) =
|
||||
result.dispatch { browser.getItem(mediaId) }
|
||||
result.dispatch { musicBrowser.getItem(mediaId) }
|
||||
|
||||
fun getChildren(mediaId: String, result: MediaBrowserServiceCompat.Result<MutableList<MediaItem>>) =
|
||||
result.dispatch { browser.getChildren(mediaId)?.toMutableList() }
|
||||
fun getChildren(
|
||||
mediaId: String,
|
||||
result: MediaBrowserServiceCompat.Result<MutableList<MediaItem>>
|
||||
) =
|
||||
result.dispatch { musicBrowser.getChildren(mediaId)?.toMutableList() }
|
||||
|
||||
fun search(query: String, result: MediaBrowserServiceCompat.Result<MutableList<MediaItem>>) =
|
||||
result.dispatchAsync {
|
||||
if (query.isEmpty()) {
|
||||
return@dispatchAsync mutableListOf()
|
||||
}
|
||||
val deviceLibrary =
|
||||
musicRepository.deviceLibrary ?: return@dispatchAsync mutableListOf()
|
||||
val userLibrary = musicRepository.userLibrary ?: return@dispatchAsync mutableListOf()
|
||||
val items =
|
||||
SearchEngine.Items(
|
||||
deviceLibrary.songs,
|
||||
deviceLibrary.albums,
|
||||
deviceLibrary.artists,
|
||||
deviceLibrary.genres,
|
||||
userLibrary.playlists
|
||||
)
|
||||
searchEngine.search(items, query).concat()
|
||||
}
|
||||
|
||||
|
||||
private fun SearchEngine.Items.concat(): MutableList<MediaItem> {
|
||||
val music = mutableListOf<MediaItem>()
|
||||
if (songs != null) {
|
||||
music.addAll(songs.map { it.toMediaItem(context, null) })
|
||||
}
|
||||
if (albums != null) {
|
||||
music.addAll(albums.map { it.toMediaItem(context) })
|
||||
}
|
||||
if (artists != null) {
|
||||
music.addAll(artists.map { it.toMediaItem(context) })
|
||||
}
|
||||
if (genres != null) {
|
||||
music.addAll(genres.map { it.toMediaItem(context) })
|
||||
}
|
||||
if (playlists != null) {
|
||||
music.addAll(playlists.map { it.toMediaItem(context) })
|
||||
}
|
||||
return music
|
||||
}
|
||||
|
||||
private fun <T> MediaBrowserServiceCompat.Result<T>.dispatch(body: () -> T?) {
|
||||
try {
|
||||
|
@ -144,4 +186,19 @@ constructor(
|
|||
sendResult(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> MediaBrowserServiceCompat.Result<T>.dispatchAsync(body: suspend () -> T?) {
|
||||
dispatchScope.launch {
|
||||
try {
|
||||
val result = body()
|
||||
if (result == null) {
|
||||
logW("Result is null")
|
||||
}
|
||||
sendResult(result)
|
||||
} catch (e: Exception) {
|
||||
logD("Error while dispatching: $e")
|
||||
sendResult(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ interface SearchEngine {
|
|||
val artists: Collection<Artist>? = null,
|
||||
val genres: Collection<Genre>? = null,
|
||||
val playlists: Collection<Playlist>? = null
|
||||
)
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class SearchEngineImpl @Inject constructor(@ApplicationContext private val context: Context) :
|
||||
|
|
Loading…
Reference in a new issue