diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt index cbf76a81..37c877f3 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt @@ -404,3 +404,22 @@ data class LibraryItemWithEpisode( var libraryItemWrapper:LibraryItemWrapper, var episode:PodcastEpisode ) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class LibraryItemSearchResultSeriesItemType( + var series: LibrarySeriesItem, + var books: List? +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class LibraryItemSearchResultLibraryItemType( + val libraryItem: LibraryItem +) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class LibraryItemSearchResultType( + var book:List?, + var podcast:List?, + var series:List?, + var authors:List? +) diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryAuthorItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryAuthorItem.kt index 16017484..97ce5551 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryAuthorItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryAuthorItem.kt @@ -4,6 +4,7 @@ import android.content.Context import android.net.Uri import android.os.Bundle import android.support.v4.media.MediaDescriptionCompat +import androidx.media.utils.MediaConstants import com.audiobookshelf.app.BuildConfig import com.audiobookshelf.app.R import com.audiobookshelf.app.device.DeviceManager @@ -15,7 +16,6 @@ class LibraryAuthorItem( id:String, var libraryId:String, var name:String, - var lastFirst:String, var description:String?, var imagePath:String?, var addedAt:Long, @@ -40,8 +40,11 @@ class LibraryAuthorItem( } @JsonIgnore - override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat { + fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, groupTitle: String?): MediaDescriptionCompat { val extras = Bundle() + if (groupTitle !== null) { + extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle) + } val mediaId = "__LIBRARY__${libraryId}__AUTHOR__${id}" return MediaDescriptionCompat.Builder() @@ -52,4 +55,9 @@ class LibraryAuthorItem( .setExtras(extras) .build() } + + @JsonIgnore + override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat { + return getMediaDescription(progress, ctx, null) + } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt index 7afbc259..89b3fab6 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LibraryItem.kt @@ -84,7 +84,7 @@ class LibraryItem( } @JsonIgnore - fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?): MediaDescriptionCompat { + fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?, groupTitle: String?): MediaDescriptionCompat { val extras = Bundle() if (collapsedSeries == null) { @@ -124,6 +124,9 @@ class LibraryItem( ) } } + if (groupTitle !== null) { + extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle) + } val mediaId = if (localLibraryItemId != null) { localLibraryItemId @@ -153,9 +156,14 @@ class LibraryItem( .build() } + @JsonIgnore + fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?): MediaDescriptionCompat { + return getMediaDescription(progress, ctx, authorId, showSeriesNumber, null) + } + @JsonIgnore fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?): MediaDescriptionCompat { - return getMediaDescription(progress, ctx, authorId, null) + return getMediaDescription(progress, ctx, authorId, null, null) } @JsonIgnore @@ -163,6 +171,6 @@ class LibraryItem( /* This is needed so Android auto library hierarchy for author series can be implemented */ - return getMediaDescription(progress, ctx, null, null) + return getMediaDescription(progress, ctx, null, null, null) } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LibrarySeriesItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LibrarySeriesItem.kt index 8a9a798c..921a909a 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LibrarySeriesItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LibrarySeriesItem.kt @@ -3,6 +3,7 @@ package com.audiobookshelf.app.data import android.content.Context import android.os.Bundle import android.support.v4.media.MediaDescriptionCompat +import androidx.media.utils.MediaConstants import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnoreProperties @@ -29,7 +30,7 @@ class LibrarySeriesItem( } @JsonIgnore - override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat { + fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, groupTitle: String?): MediaDescriptionCompat { val extras = Bundle() if (localLibraryItemId != null) { @@ -38,6 +39,9 @@ class LibrarySeriesItem( MediaDescriptionCompat.STATUS_DOWNLOADED ) } + if (groupTitle !== null) { + extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle) + } val mediaId = "__LIBRARY__${libraryId}__SERIES__${id}" return MediaDescriptionCompat.Builder() @@ -48,4 +52,9 @@ class LibrarySeriesItem( .setExtras(extras) .build() } + + @JsonIgnore + override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat { + return getMediaDescription(progress, ctx, null) + } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt index 49dd92eb..feb755bc 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt @@ -619,6 +619,14 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) { } } + suspend fun searchLocalCache(libraryId: String, queryString: String) : LibraryItemSearchResultType? { + return suspendCoroutine { + apiHandler.getSearchResults(libraryId, queryString) { results -> + it.resume(results) + } + } + } + fun getFirstItem() : LibraryItemWrapper? { if (serverLibraryItems.isNotEmpty()) { return serverLibraryItems[0] diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 5d96c60e..73fa6530 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -47,6 +47,7 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.source.hls.HlsMediaSource import com.google.android.exoplayer2.ui.PlayerNotificationManager import com.google.android.exoplayer2.upstream.* +import kotlinx.coroutines.runBlocking import java.util.* import kotlin.concurrent.schedule @@ -1350,13 +1351,55 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { override fun onSearch(query: String, extras: Bundle?, result: Result>) { result.detach() - mediaManager.loadAndroidAutoItems { - browseTree = BrowseTree(this, mediaManager.serverItemsInProgress, mediaManager.serverLibraries) - val children = browseTree[LIBRARIES_ROOT]?.map { item -> - MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) + Log.d(tag, "Search bundle: $extras") + var foundBooks: MutableList = mutableListOf() + var foundPodcasts: MutableList = mutableListOf() + var foundSeries: MutableList = mutableListOf() + var foundAuthors: MutableList = mutableListOf() + + mediaManager.serverLibraries.forEach { serverLibrary -> + runBlocking{ + val searchResult = mediaManager.searchLocalCache(serverLibrary.id, query) + Log.d(tag, "onSearch: SearchResult: $searchResult") + if (searchResult === null) return@runBlocking + if (searchResult.book !== null && searchResult.book!!.isNotEmpty()) { + Log.d(tag, "onSearch: found ${searchResult.book!!.size} books") + val children = searchResult.book!!.map { bookResult -> + val libraryItem = bookResult.libraryItem + val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id } + val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id) + libraryItem.localLibraryItemId = localLibraryItem?.id + val description = libraryItem.getMediaDescription(progress, ctx, null, null, "Books (${serverLibrary.name})") + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) + } + foundBooks.addAll(children) + } + if (searchResult.series !== null && searchResult.series!!.isNotEmpty()) { + Log.d(tag, "onSearch: found ${searchResult.series!!.size} series") + val children = searchResult.series!!.map { seriesResult -> + val seriesItem = seriesResult.series + seriesItem.books = seriesResult.books as MutableList + val description = seriesItem.getMediaDescription(null, ctx, "Series (${serverLibrary.name})") + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) + } + foundSeries.addAll(children) + } + if (searchResult.authors !== null && searchResult.authors!!.isNotEmpty()) { + Log.d(tag, "onSearch: found ${searchResult.authors!!.size} authors") + val children = searchResult.authors!!.map { authorItem -> + val description = authorItem.getMediaDescription(null, ctx, "Authors (${serverLibrary.name})") + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) + } + foundAuthors.addAll(children) + } + Log.d(tag, "onSearch: Library ${serverLibrary.id} processed") } - result.sendResult(children as MutableList?) + Log.d(tag, "onSearch: Library ${serverLibrary.id} scanned") } + foundBooks.addAll(foundSeries) + foundBooks.addAll(foundAuthors) + result.sendResult(foundBooks as MutableList?) + Log.d(tag, "onSearch: Done") } // diff --git a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt index dc52c82c..0ba7acf6 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt @@ -286,6 +286,19 @@ class ApiHandler(var ctx:Context) { } } + fun getSearchResults(libraryId:String, queryString:String, cb: (LibraryItemSearchResultType?) -> Unit) { + Log.d(tag, "Doing search for library $libraryId") + getRequest("/api/libraries/$libraryId/search?q=$queryString", null, null) { + if (it.has("error")) { + Log.e(tag, it.getString("error") ?: "getSearchResults Failed") + cb(null) + } else { + val librarySearchResults = jacksonMapper.readValue(it.toString()) + cb(librarySearchResults) + } + } + } + fun getAllItemsInProgress(cb: (List) -> Unit) { getRequest("/api/me/items-in-progress", null, null) { val items = mutableListOf()