Android Auto: Added comments to code

This commit is contained in:
ISO-B
2024-11-18 20:20:00 +02:00
parent 8caa08843e
commit 1766111e1d
3 changed files with 81 additions and 33 deletions
@@ -439,6 +439,7 @@ data class LibraryItemSearchResultType(
var authors:List<LibraryAuthorItem>? var authors:List<LibraryAuthorItem>?
) )
// For personalized shelves
@JsonTypeInfo( @JsonTypeInfo(
use=JsonTypeInfo.Id.NAME, use=JsonTypeInfo.Id.NAME,
property = "type", property = "type",
@@ -27,7 +27,7 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
private var cachedLibrarySeries : MutableMap<String, List<LibrarySeriesItem>> = hashMapOf() private var cachedLibrarySeries : MutableMap<String, List<LibrarySeriesItem>> = hashMapOf()
private var cachedLibrarySeriesItem : MutableMap<String, MutableMap<String, List<LibraryItem>>> = hashMapOf() private var cachedLibrarySeriesItem : MutableMap<String, MutableMap<String, List<LibraryItem>>> = hashMapOf()
private var cachedLibraryCollections : MutableMap<String, MutableMap<String, LibraryCollection>> = hashMapOf() private var cachedLibraryCollections : MutableMap<String, MutableMap<String, LibraryCollection>> = hashMapOf()
private var cachedLibraryRecentShelfs : MutableMap<String, MutableList<LibraryShelfType>> = hashMapOf() private var cachedLibraryRecentShelves : MutableMap<String, MutableList<LibraryShelfType>> = hashMapOf()
private var cachedLibraryDiscovery : MutableMap<String, MutableList<LibraryItem>> = hashMapOf() private var cachedLibraryDiscovery : MutableMap<String, MutableList<LibraryItem>> = hashMapOf()
private var cachedLibraryPodcasts : MutableMap<String, MutableMap<String, LibraryItem>> = hashMapOf() private var cachedLibraryPodcasts : MutableMap<String, MutableMap<String, LibraryItem>> = hashMapOf()
private var isLibraryPodcastsCached : MutableMap<String, Boolean> = hashMapOf() private var isLibraryPodcastsCached : MutableMap<String, Boolean> = hashMapOf()
@@ -49,6 +49,11 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
return serverLibraries.find { it.id == id } != null return serverLibraries.find { it.id == id } != null
} }
/**
* Check if there is discovery shelf for [libraryId]
* If personalized shelves are not yet populated for library then populate
*
*/
fun getHasDiscovery(libraryId: String) : Boolean { fun getHasDiscovery(libraryId: String) : Boolean {
if (cachedLibraryDiscovery.containsKey(libraryId)) { if (cachedLibraryDiscovery.containsKey(libraryId)) {
if (cachedLibraryDiscovery[libraryId]!!.isNotEmpty()) { if (cachedLibraryDiscovery[libraryId]!!.isNotEmpty()) {
@@ -64,6 +69,9 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
return serverLibraries.find { it.id == id } return serverLibraries.find { it.id == id }
} }
/**
* Add [libraryItem] to [serverLibraryItems] if it is not already added
*/
private fun addServerLibrary(libraryItem: LibraryItem) { private fun addServerLibrary(libraryItem: LibraryItem) {
if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) { if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) {
serverLibraryItems.add(libraryItem) serverLibraryItems.add(libraryItem)
@@ -138,7 +146,7 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cachedLibrarySeries = hashMapOf() cachedLibrarySeries = hashMapOf()
cachedLibrarySeriesItem = hashMapOf() cachedLibrarySeriesItem = hashMapOf()
cachedLibraryCollections = hashMapOf() cachedLibraryCollections = hashMapOf()
cachedLibraryRecentShelfs = hashMapOf() cachedLibraryRecentShelves = hashMapOf()
cachedLibraryDiscovery = hashMapOf() cachedLibraryDiscovery = hashMapOf()
cachedLibraryPodcasts = hashMapOf() cachedLibraryPodcasts = hashMapOf()
isLibraryPodcastsCached = hashMapOf() isLibraryPodcastsCached = hashMapOf()
@@ -161,6 +169,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} }
/**
* Load personalized shelves from server for all libraries.
* [cb] resolves when all libraries are processed
*/
fun populatePersonalizedDataForAllLibraries(cb: () -> Unit ) { fun populatePersonalizedDataForAllLibraries(cb: () -> Unit ) {
serverLibraries.forEach { serverLibraries.forEach {
libraryPersonalizationsDone++ libraryPersonalizationsDone++
@@ -175,21 +187,25 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cb() cb()
} }
/**
* Get personalized shelves from server for selected [libraryId].
* Populates [cachedLibraryRecentShelves] and [cachedLibraryDiscovery].
*/
private fun populatePersonalizedDataForLibrary(libraryId: String, cb: () -> Unit) { private fun populatePersonalizedDataForLibrary(libraryId: String, cb: () -> Unit) {
apiHandler.getLibraryPersonalized(libraryId) { shelfs -> apiHandler.getLibraryPersonalized(libraryId) { shelves ->
Log.d(tag, "populatePersonalizedDataForLibrary $libraryId") Log.d(tag, "populatePersonalizedDataForLibrary $libraryId")
if (shelfs === null) return@getLibraryPersonalized if (shelves === null) return@getLibraryPersonalized
shelfs.map { shelf -> shelves.map { shelf ->
Log.d(tag, "$shelf") Log.d(tag, "$shelf")
if (shelf.type == "book") { if (shelf.type == "book") {
if (shelf.id == "continue-listening") return@map if (shelf.id == "continue-listening") return@map
else if (shelf.id == "listen-again") return@map else if (shelf.id == "listen-again") return@map
else if (shelf.id == "recently-added") { else if (shelf.id == "recently-added") {
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cachedLibraryRecentShelfs[libraryId] = mutableListOf() cachedLibraryRecentShelves[libraryId] = mutableListOf()
} }
if (cachedLibraryRecentShelfs[libraryId]?.find { it.id == shelf.id } == null) { if (cachedLibraryRecentShelves[libraryId]?.find { it.id == shelf.id } == null) {
cachedLibraryRecentShelfs[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
} }
else if (shelf.id == "discover") { else if (shelf.id == "discover") {
@@ -205,22 +221,22 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
shelf as LibraryShelfBookEntity shelf as LibraryShelfBookEntity
} else if (shelf.type == "series") { } else if (shelf.type == "series") {
if (shelf.id == "recent-series") { if (shelf.id == "recent-series") {
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cachedLibraryRecentShelfs[libraryId] = mutableListOf() cachedLibraryRecentShelves[libraryId] = mutableListOf()
} }
if (cachedLibraryRecentShelfs[libraryId]?.find { it.id == shelf.id } == null) { if (cachedLibraryRecentShelves[libraryId]?.find { it.id == shelf.id } == null) {
cachedLibraryRecentShelfs[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
} }
} else if (shelf.type == "episode") { } else if (shelf.type == "episode") {
if (shelf.id == "continue-listening") return@map if (shelf.id == "continue-listening") return@map
else if (shelf.id == "listen-again") return@map else if (shelf.id == "listen-again") return@map
else if (shelf.id == "newest-episodes") { else if (shelf.id == "newest-episodes") {
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cachedLibraryRecentShelfs[libraryId] = mutableListOf() cachedLibraryRecentShelves[libraryId] = mutableListOf()
} }
if (cachedLibraryRecentShelfs[libraryId]?.find { it.id == shelf.id } == null) { if (cachedLibraryRecentShelves[libraryId]?.find { it.id == shelf.id } == null) {
cachedLibraryRecentShelfs[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
(shelf as LibraryShelfEpisodeEntity).entities?.forEach { libraryItem -> (shelf as LibraryShelfEpisodeEntity).entities?.forEach { libraryItem ->
@@ -229,11 +245,11 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} else if (shelf.type == "podcast") { } else if (shelf.type == "podcast") {
if (shelf.id == "recently-added"){ if (shelf.id == "recently-added"){
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cachedLibraryRecentShelfs[libraryId] = mutableListOf() cachedLibraryRecentShelves[libraryId] = mutableListOf()
} }
if (cachedLibraryRecentShelfs[libraryId]?.find { it.id == shelf.id } == null) { if (cachedLibraryRecentShelves[libraryId]?.find { it.id == shelf.id } == null) {
cachedLibraryRecentShelfs[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
} }
else if (shelf.id == "discover"){ else if (shelf.id == "discover"){
@@ -241,11 +257,11 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} else if (shelf.type =="authors") { } else if (shelf.type =="authors") {
if (shelf.id == "newest-authors") { if (shelf.id == "newest-authors") {
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cachedLibraryRecentShelfs[libraryId] = mutableListOf() cachedLibraryRecentShelves[libraryId] = mutableListOf()
} }
if (cachedLibraryRecentShelfs[libraryId]?.find { it.id == shelf.id } == null) { if (cachedLibraryRecentShelves[libraryId]?.find { it.id == shelf.id } == null) {
cachedLibraryRecentShelfs[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
} }
} }
@@ -256,6 +272,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} }
/**
* Returns podcasts for selected library.
* If data is not found from local cache it is loaded from server
*/
fun loadLibraryPodcasts(libraryId:String, cb: (List<LibraryItem>?) -> Unit) { fun loadLibraryPodcasts(libraryId:String, cb: (List<LibraryItem>?) -> Unit) {
// Without this there is possibility that only recent podcasts get loaded // Without this there is possibility that only recent podcasts get loaded
// Loading recent podcasts will also create cachedLibraryPodcasts entry for library // Loading recent podcasts will also create cachedLibraryPodcasts entry for library
@@ -322,7 +342,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cb(seriesWithBooks) cb(seriesWithBooks)
} }
fun sortSeriesBooks(seriesBooks: List<LibraryItem>) : List<LibraryItem> { /**
* Sorts books in series. Assumes that sequence is main.minor
*/
private fun sortSeriesBooks(seriesBooks: List<LibraryItem>) : List<LibraryItem> {
val sortingLogic = compareBy<LibraryItem> { it.seriesSequenceParts[0].length } val sortingLogic = compareBy<LibraryItem> { it.seriesSequenceParts[0].length }
.thenBy { it.seriesSequenceParts[0].ifEmpty { "" } } .thenBy { it.seriesSequenceParts[0].ifEmpty { "" } }
.thenBy { it.seriesSequenceParts.getOrElse(1) { "" }.length } .thenBy { it.seriesSequenceParts.getOrElse(1) { "" }.length }
@@ -519,6 +542,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} }
/**
* Returns audiobooks from discovery shelf for [libraryId]
* If data is not found from local cache then it will be fetched from server
*/
fun loadLibraryDiscoveryBooksWithAudio(libraryId: String, cb: (List<LibraryItem>) -> Unit) { fun loadLibraryDiscoveryBooksWithAudio(libraryId: String, cb: (List<LibraryItem>) -> Unit) {
if (!cachedLibraryDiscovery.containsKey(libraryId)) { if (!cachedLibraryDiscovery.containsKey(libraryId)) {
cb(listOf()) cb(listOf())
@@ -528,22 +555,30 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cb(libraryItemsWithAudio as List<LibraryItem>) cb(libraryItemsWithAudio as List<LibraryItem>)
} }
/**
* Returns recent shelves for [libraryId]
* If data is not shelves are found returns empty list
*/
fun getLibraryRecentShelfs(libraryId: String, cb: (List<LibraryShelfType>) -> Unit) { fun getLibraryRecentShelfs(libraryId: String, cb: (List<LibraryShelfType>) -> Unit) {
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
Log.d(tag, "getLibraryRecentShelfs: No shelves $libraryId") Log.d(tag, "getLibraryRecentShelfs: No shelves $libraryId")
cb(listOf()) cb(listOf())
return return
} }
cb(cachedLibraryRecentShelfs[libraryId] as List<LibraryShelfType>) cb(cachedLibraryRecentShelves[libraryId] as List<LibraryShelfType>)
} }
/**
* Returns recent shelf by [type] for [libraryId]
* If shelf is not found returns null
*/
fun getLibraryRecentShelfByType(libraryId: String, type:String, cb: (LibraryShelfType?) -> Unit) { fun getLibraryRecentShelfByType(libraryId: String, type:String, cb: (LibraryShelfType?) -> Unit) {
Log.d(tag, "getLibraryRecentShelfByType: $libraryId | $type") Log.d(tag, "getLibraryRecentShelfByType: $libraryId | $type")
if (!cachedLibraryRecentShelfs.containsKey(libraryId)) { if (!cachedLibraryRecentShelves.containsKey(libraryId)) {
cb(null) cb(null)
return return
} }
for (shelf in cachedLibraryRecentShelfs[libraryId]!!) { for (shelf in cachedLibraryRecentShelves[libraryId]!!) {
if (shelf.type == type.lowercase()) { if (shelf.type == type.lowercase()) {
cb(shelf) cb(shelf)
return return
@@ -552,6 +587,9 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cb(null) cb(null)
} }
/**
* Loads podcasts for newest episodes shelf
*/
private fun loadPodcastItem(libraryId: String, libraryItemId: String, cb: (LibraryItem?) -> Unit) { private fun loadPodcastItem(libraryId: String, libraryItemId: String, cb: (LibraryItem?) -> Unit) {
// Ensure that there is map for library // Ensure that there is map for library
if (!cachedLibraryPodcasts.containsKey(libraryId)) { if (!cachedLibraryPodcasts.containsKey(libraryId)) {
@@ -643,6 +681,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} }
/**
* Loads libraries for selected server.
* After loading libraries fetches stats for each library.
*/
private fun loadLibraries(cb: (List<Library>) -> Unit) { private fun loadLibraries(cb: (List<Library>) -> Unit) {
if (serverLibraries.isNotEmpty()) { if (serverLibraries.isNotEmpty()) {
cb(serverLibraries) cb(serverLibraries)
@@ -812,6 +854,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
} }
/**
* Handles search requests.
* Searches from books, series and authors
*/
suspend fun doSearch(libraryId: String, queryString: String) : Map<String, List<MediaBrowserCompat.MediaItem>> { suspend fun doSearch(libraryId: String, queryString: String) : Map<String, List<MediaBrowserCompat.MediaItem>> {
return suspendCoroutine { return suspendCoroutine {
apiHandler.getSearchResults(libraryId, queryString) { searchResult -> apiHandler.getSearchResults(libraryId, queryString) { searchResult ->
@@ -822,7 +868,7 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
return@getSearchResults return@getSearchResults
} }
var foundItems: MutableMap<String, List<MediaBrowserCompat.MediaItem>> = mutableMapOf() val foundItems: MutableMap<String, List<MediaBrowserCompat.MediaItem>> = mutableMapOf()
val serverLibrary = serverLibraries.find { sl -> sl.id == libraryId } val serverLibrary = serverLibraries.find { sl -> sl.id == libraryId }
@@ -126,7 +126,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private var forceReloadingAndroidAuto:Boolean = false private var forceReloadingAndroidAuto:Boolean = false
private var firstLoadDone:Boolean = false private var firstLoadDone:Boolean = false
fun isBrowsetreeInitialized() : Boolean { fun isBrowseTreeInitialized() : Boolean {
return this::browseTree.isInitialized return this::browseTree.isInitialized
} }
@@ -1152,6 +1152,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
result.sendResult(null) result.sendResult(null)
return return
} }
// Wait until top-menu is initialized
while (!this::browseTree.isInitialized) {} while (!this::browseTree.isInitialized) {}
val children = browseTree[parentMediaId]?.map { item -> val children = browseTree[parentMediaId]?.map { item ->
Log.d(tag, "[MENU: $parentMediaId] Showing list item ${item.description.title}") Log.d(tag, "[MENU: $parentMediaId] Showing list item ${item.description.title}")
@@ -1668,7 +1669,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (hasNetworkConnectivity) { if (hasNetworkConnectivity) {
// Force android auto loading if libraries are empty. // Force android auto loading if libraries are empty.
// Lack of network connectivity is most likely reason for libraries being empty // Lack of network connectivity is most likely reason for libraries being empty
if (isBrowsetreeInitialized() && firstLoadDone && mediaManager.serverLibraries.isEmpty()) { if (isBrowseTreeInitialized() && firstLoadDone && mediaManager.serverLibraries.isEmpty()) {
forceReloadingAndroidAuto = true forceReloadingAndroidAuto = true
notifyChildrenChanged("/") notifyChildrenChanged("/")
} }