mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-17 00:01:16 +02:00
Android auto update for downloaded media category, currently listening, and initialize when MainActivity wasnt started
This commit is contained in:
parent
ae65bed352
commit
df1ce6d91f
12 changed files with 302 additions and 96 deletions
|
@ -70,8 +70,9 @@ class MainActivity : BridgeActivity() {
|
||||||
|
|
||||||
Log.d(tag, "onCreate")
|
Log.d(tag, "onCreate")
|
||||||
|
|
||||||
// var ss = SimpleStorage(this)
|
// Grant full storage access for testing
|
||||||
// ss.requestFullStorageAccess()
|
// var ss = SimpleStorage(this)
|
||||||
|
// ss.requestFullStorageAccess()
|
||||||
|
|
||||||
var permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
var permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ data class LibraryItem(
|
||||||
var mediaType:String,
|
var mediaType:String,
|
||||||
var media:MediaType,
|
var media:MediaType,
|
||||||
var libraryFiles:MutableList<LibraryFile>?
|
var libraryFiles:MutableList<LibraryFile>?
|
||||||
) {
|
) : LibraryItemWrapper() {
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val title get() = media.metadata.title
|
val title get() = media.metadata.title
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.audiobookshelf.app.data
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
data class ServerConnectionConfig(
|
data class ServerConnectionConfig(
|
||||||
|
@ -18,7 +20,14 @@ data class DeviceData(
|
||||||
var serverConnectionConfigs:MutableList<ServerConnectionConfig>,
|
var serverConnectionConfigs:MutableList<ServerConnectionConfig>,
|
||||||
var lastServerConnectionConfigId:String?,
|
var lastServerConnectionConfigId:String?,
|
||||||
var currentLocalPlaybackSession:PlaybackSession? // Stored to open up where left off for local media
|
var currentLocalPlaybackSession:PlaybackSession? // Stored to open up where left off for local media
|
||||||
)
|
) {
|
||||||
|
@JsonIgnore
|
||||||
|
fun getLastServerConnectionConfig():ServerConnectionConfig? {
|
||||||
|
return lastServerConnectionConfigId?.let { lsccid ->
|
||||||
|
return serverConnectionConfigs.find { it.id == lsccid }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class LocalFile(
|
data class LocalFile(
|
||||||
|
@ -48,3 +57,10 @@ data class LocalFolder(
|
||||||
var storageType:String,
|
var storageType:String,
|
||||||
var mediaType:String
|
var mediaType:String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@JsonTypeInfo(use= JsonTypeInfo.Id.DEDUCTION)
|
||||||
|
@JsonSubTypes(
|
||||||
|
JsonSubTypes.Type(LibraryItem::class),
|
||||||
|
JsonSubTypes.Type(LocalLibraryItem::class)
|
||||||
|
)
|
||||||
|
open class LibraryItemWrapper()
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.audiobookshelf.app.data
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
data class LibraryCategory(
|
||||||
|
var id:String,
|
||||||
|
var label:String,
|
||||||
|
var type:String,
|
||||||
|
var entities:List<LibraryItemWrapper>,
|
||||||
|
var isLocal:Boolean
|
||||||
|
)
|
|
@ -1,6 +1,9 @@
|
||||||
package com.audiobookshelf.app.data
|
package com.audiobookshelf.app.data
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.support.v4.media.MediaMetadataCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.audiobookshelf.app.R
|
||||||
import com.audiobookshelf.app.device.DeviceManager
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
@ -25,11 +28,19 @@ data class LocalLibraryItem(
|
||||||
var serverAddress:String?,
|
var serverAddress:String?,
|
||||||
var serverUserId:String?,
|
var serverUserId:String?,
|
||||||
var libraryItemId:String?
|
var libraryItemId:String?
|
||||||
) {
|
) : LibraryItemWrapper() {
|
||||||
|
@get:JsonIgnore
|
||||||
|
val title get() = media.metadata.title
|
||||||
|
@get:JsonIgnore
|
||||||
|
val authorName get() = media.metadata.getAuthorDisplayName()
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isPodcast get() = mediaType == "podcast"
|
val isPodcast get() = mediaType == "podcast"
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getCoverUri(): Uri {
|
||||||
|
return if (coverContentUrl != null) Uri.parse(coverContentUrl) else Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getDuration():Double {
|
fun getDuration():Double {
|
||||||
var total = 0.0
|
var total = 0.0
|
||||||
|
@ -80,4 +91,18 @@ data class LocalLibraryItem(
|
||||||
fun removeLocalFile(localFileId:String) {
|
fun removeLocalFile(localFileId:String) {
|
||||||
localFiles.removeIf { it.id == localFileId }
|
localFiles.removeIf { it.id == localFileId }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getMediaMetadata(): MediaMetadataCompat {
|
||||||
|
return MediaMetadataCompat.Builder().apply {
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title)
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, authorName)
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCoverUri().toString())
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getCoverUri().toString())
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_ART_URI, getCoverUri().toString())
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, authorName)
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ import com.audiobookshelf.app.data.DeviceData
|
||||||
import com.audiobookshelf.app.data.ServerConnectionConfig
|
import com.audiobookshelf.app.data.ServerConnectionConfig
|
||||||
|
|
||||||
object DeviceManager {
|
object DeviceManager {
|
||||||
val tag = "DeviceManager"
|
const val tag = "DeviceManager"
|
||||||
|
|
||||||
val dbManager:DbManager = DbManager()
|
val dbManager:DbManager = DbManager()
|
||||||
var deviceData:DeviceData = dbManager.getDeviceData()
|
var deviceData:DeviceData = dbManager.getDeviceData()
|
||||||
var serverConnectionConfig: ServerConnectionConfig? = null
|
var serverConnectionConfig: ServerConnectionConfig? = null
|
||||||
|
@ -15,6 +16,8 @@ object DeviceManager {
|
||||||
val serverAddress get() = serverConnectionConfig?.address ?: ""
|
val serverAddress get() = serverConnectionConfig?.address ?: ""
|
||||||
val serverUserId get() = serverConnectionConfig?.userId ?: ""
|
val serverUserId get() = serverConnectionConfig?.userId ?: ""
|
||||||
val token get() = serverConnectionConfig?.token ?: ""
|
val token get() = serverConnectionConfig?.token ?: ""
|
||||||
|
val isConnectedToServer get() = serverConnectionConfig != null
|
||||||
|
val hasLastServerConnectionConfig get() = deviceData.getLastServerConnectionConfig() != null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
Log.d(tag, "Device Manager Singleton invoked")
|
Log.d(tag, "Device Manager Singleton invoked")
|
||||||
|
|
|
@ -1,44 +1,151 @@
|
||||||
package com.audiobookshelf.app.media
|
package com.audiobookshelf.app.media
|
||||||
|
|
||||||
import com.audiobookshelf.app.data.LibraryItem
|
import android.bluetooth.BluetoothClass
|
||||||
import com.audiobookshelf.app.data.PlaybackSession
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.audiobookshelf.app.data.*
|
||||||
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
|
import com.audiobookshelf.app.player.PlayerNotificationService
|
||||||
import com.audiobookshelf.app.server.ApiHandler
|
import com.audiobookshelf.app.server.ApiHandler
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import io.paperdb.Paper
|
||||||
|
|
||||||
|
class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
||||||
|
val tag = "MediaManager"
|
||||||
|
|
||||||
class MediaManager(var apiHandler: ApiHandler) {
|
|
||||||
var serverLibraryItems = listOf<LibraryItem>()
|
var serverLibraryItems = listOf<LibraryItem>()
|
||||||
|
var serverLibraryCategories = listOf<LibraryCategory>()
|
||||||
|
var serverLibraries = listOf<Library>()
|
||||||
|
|
||||||
fun loadLibraryItems(cb: (List<LibraryItem>) -> Unit) {
|
fun initializeAndroidAuto() {
|
||||||
|
Log.d(tag, "Android Auto started when MainActivity was never started - initializing Paper")
|
||||||
|
Paper.init(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLibraryCategories(libraryId:String, cb: (List<LibraryCategory>) -> Unit) {
|
||||||
|
if (serverLibraryCategories.isNotEmpty()) {
|
||||||
|
cb(serverLibraryCategories)
|
||||||
|
} else {
|
||||||
|
apiHandler.getLibraryCategories(libraryId) {
|
||||||
|
serverLibraryCategories = it
|
||||||
|
cb(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLibraryItems(libraryId:String, cb: (List<LibraryItem>) -> Unit) {
|
||||||
if (serverLibraryItems.isNotEmpty()) {
|
if (serverLibraryItems.isNotEmpty()) {
|
||||||
cb(serverLibraryItems)
|
cb(serverLibraryItems)
|
||||||
} else {
|
} else {
|
||||||
apiHandler.getLibraryItems("main") { libraryItems ->
|
apiHandler.getLibraryItems(libraryId) { libraryItems ->
|
||||||
serverLibraryItems = libraryItems
|
serverLibraryItems = libraryItems
|
||||||
cb(libraryItems)
|
cb(libraryItems)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFirstItem() : LibraryItem? {
|
fun loadLibraries(cb: (List<Library>) -> Unit) {
|
||||||
return if (serverLibraryItems.isNotEmpty()) serverLibraryItems[0] else null
|
if (serverLibraries.isNotEmpty()) {
|
||||||
|
cb(serverLibraries)
|
||||||
|
} else {
|
||||||
|
apiHandler.getLibraries {
|
||||||
|
serverLibraries = it
|
||||||
|
cb(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getById(id:String) : LibraryItem? {
|
// TODO: Load currently listening category for local items
|
||||||
|
fun loadLocalCategory():List<LibraryCategory> {
|
||||||
|
var localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
||||||
|
var localPodcasts = DeviceManager.dbManager.getLocalLibraryItems("podcast")
|
||||||
|
var cats = mutableListOf<LibraryCategory>()
|
||||||
|
if (localBooks.isNotEmpty()) {
|
||||||
|
cats.add(LibraryCategory("local-books", "Local Books", "book", localBooks, true))
|
||||||
|
}
|
||||||
|
if (localPodcasts.isNotEmpty()) {
|
||||||
|
cats.add(LibraryCategory("local-podcasts", "Local Podcasts", "podcast", localPodcasts, true))
|
||||||
|
}
|
||||||
|
return cats
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadAndroidAutoItems(libraryId:String, cb: (List<LibraryCategory>) -> Unit) {
|
||||||
|
Log.d(tag, "Load android auto items for library id $libraryId")
|
||||||
|
var cats = mutableListOf<LibraryCategory>()
|
||||||
|
|
||||||
|
var localCategories = loadLocalCategory()
|
||||||
|
cats.addAll(localCategories)
|
||||||
|
|
||||||
|
// Connected to server and has internet - load other cats
|
||||||
|
if (apiHandler.isOnline() && (DeviceManager.isConnectedToServer || DeviceManager.hasLastServerConnectionConfig)) {
|
||||||
|
if (!DeviceManager.isConnectedToServer) {
|
||||||
|
DeviceManager.serverConnectionConfig = DeviceManager.deviceData.getLastServerConnectionConfig()
|
||||||
|
Log.d(tag, "Not connected to server, set last server \"${DeviceManager.serverAddress}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLibraries { libraries ->
|
||||||
|
var library = libraries.find { it.id == libraryId } ?: libraries[0]
|
||||||
|
Log.d(tag, "Loading categories for library ${library.name} - ${library.id} - ${library.mediaType}")
|
||||||
|
|
||||||
|
loadLibraryCategories(libraryId) { libraryCategories ->
|
||||||
|
|
||||||
|
// Only using book or podcast library categories for now
|
||||||
|
libraryCategories.forEach {
|
||||||
|
Log.d(tag, "Found library category ${it.label} with type ${it.type}")
|
||||||
|
if (it.type == library.mediaType) {
|
||||||
|
Log.d(tag, "Using library category ${it.id}")
|
||||||
|
cats.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLibraryItems(libraryId) { libraryItems ->
|
||||||
|
var mainCat = LibraryCategory("library", "Library", library.mediaType, libraryItems, false)
|
||||||
|
cats.add(mainCat)
|
||||||
|
|
||||||
|
cb(cats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // Not connected/no internet sent downloaded cats only
|
||||||
|
cb(cats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFirstItem() : LibraryItemWrapper? {
|
||||||
|
if (serverLibraryItems.isNotEmpty()) {
|
||||||
|
return serverLibraryItems[0]
|
||||||
|
} else {
|
||||||
|
var localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
||||||
|
return if (localBooks.isNotEmpty()) return localBooks[0] else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getById(id:String) : LibraryItemWrapper? {
|
||||||
|
if (id.startsWith("local")) {
|
||||||
|
return DeviceManager.dbManager.getLocalLibraryItem(id)
|
||||||
|
} else {
|
||||||
return serverLibraryItems.find { it.id == id }
|
return serverLibraryItems.find { it.id == id }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getFromSearch(query:String?) : LibraryItem? {
|
fun getFromSearch(query:String?) : LibraryItemWrapper? {
|
||||||
if (query.isNullOrEmpty()) return getFirstItem()
|
if (query.isNullOrEmpty()) return getFirstItem()
|
||||||
return serverLibraryItems.find {
|
return serverLibraryItems.find {
|
||||||
it.title.lowercase(Locale.getDefault()).contains(query.lowercase(Locale.getDefault()))
|
it.title.lowercase(Locale.getDefault()).contains(query.lowercase(Locale.getDefault()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun play(libraryItem:LibraryItem, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
fun play(libraryItemWrapper:LibraryItemWrapper, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
||||||
|
if (libraryItemWrapper is LocalLibraryItem) {
|
||||||
|
var localLibraryItem = libraryItemWrapper as LocalLibraryItem
|
||||||
|
cb(localLibraryItem.getPlaybackSession(null))
|
||||||
|
} else {
|
||||||
|
var libraryItem = libraryItemWrapper as LibraryItem
|
||||||
apiHandler.playLibraryItem(libraryItem.id,"",false, mediaPlayer) {
|
apiHandler.playLibraryItem(libraryItem.id,"",false, mediaPlayer) {
|
||||||
cb(it)
|
cb(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int {
|
private fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int {
|
||||||
val lhsLength = lhs.length + 1
|
val lhsLength = lhs.length + 1
|
||||||
|
|
|
@ -7,14 +7,14 @@ import android.support.v4.media.MediaMetadataCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.AnyRes
|
import androidx.annotation.AnyRes
|
||||||
import com.audiobookshelf.app.R
|
import com.audiobookshelf.app.R
|
||||||
|
import com.audiobookshelf.app.data.LibraryCategory
|
||||||
import com.audiobookshelf.app.data.LibraryItem
|
import com.audiobookshelf.app.data.LibraryItem
|
||||||
|
import com.audiobookshelf.app.data.LocalLibraryItem
|
||||||
|
|
||||||
|
|
||||||
class BrowseTree(
|
class BrowseTree(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
itemsInProgress: List<LibraryItem>,
|
libraryCategories: List<LibraryCategory>
|
||||||
itemsMetadata: List<MediaMetadataCompat>,
|
|
||||||
downloadedMetadata: List<MediaMetadataCompat>
|
|
||||||
) {
|
) {
|
||||||
private val mediaIdToChildren = mutableMapOf<String, MutableList<MediaMetadataCompat>>()
|
private val mediaIdToChildren = mutableMapOf<String, MutableList<MediaMetadataCompat>>()
|
||||||
|
|
||||||
|
@ -37,17 +37,14 @@ class BrowseTree(
|
||||||
|
|
||||||
val continueReadingMetadata = MediaMetadataCompat.Builder().apply {
|
val continueReadingMetadata = MediaMetadataCompat.Builder().apply {
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, CONTINUE_ROOT)
|
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, CONTINUE_ROOT)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Reading")
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Listening")
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_localaudio).toString())
|
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_localaudio).toString())
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val allMetadata = MediaMetadataCompat.Builder().apply {
|
val allMetadata = MediaMetadataCompat.Builder().apply {
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, ALL_ROOT)
|
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, ALL_ROOT)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Library Items")
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Library Items")
|
||||||
|
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_books).toString())
|
||||||
var resource = getUriToDrawable(context, R.drawable.exo_icon_books).toString()
|
|
||||||
Log.d("BrowseTree", "RESOURCE $resource")
|
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, resource)
|
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val downloadsMetadata = MediaMetadataCompat.Builder().apply {
|
val downloadsMetadata = MediaMetadataCompat.Builder().apply {
|
||||||
|
@ -56,31 +53,51 @@ class BrowseTree(
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_downloaddone).toString())
|
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_downloaddone).toString())
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
if (itemsInProgress.isNotEmpty()) {
|
// Server continue Listening cat
|
||||||
|
libraryCategories.find { it.id == "continue-listening" }?.let { continueListeningCategory ->
|
||||||
|
var continueListeningMediaMetadata = continueListeningCategory.entities.map { liw ->
|
||||||
|
var libraryItem = liw as LibraryItem
|
||||||
|
libraryItem.getMediaMetadata()
|
||||||
|
}
|
||||||
|
if (continueListeningMediaMetadata.isNotEmpty()) {
|
||||||
rootList += continueReadingMetadata
|
rootList += continueReadingMetadata
|
||||||
}
|
}
|
||||||
rootList += allMetadata
|
continueListeningMediaMetadata.forEach {
|
||||||
rootList += downloadsMetadata
|
|
||||||
// rootList += localsMetadata
|
|
||||||
mediaIdToChildren[AUTO_BROWSE_ROOT] = rootList
|
|
||||||
|
|
||||||
itemsInProgress.forEach { libraryItem ->
|
|
||||||
val children = mediaIdToChildren[CONTINUE_ROOT] ?: mutableListOf()
|
val children = mediaIdToChildren[CONTINUE_ROOT] ?: mutableListOf()
|
||||||
children += libraryItem.getMediaMetadata()
|
children += it
|
||||||
mediaIdToChildren[CONTINUE_ROOT] = children
|
mediaIdToChildren[CONTINUE_ROOT] = children
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsMetadata.forEach {
|
|
||||||
val allChildren = mediaIdToChildren[ALL_ROOT] ?: mutableListOf()
|
|
||||||
allChildren += it
|
|
||||||
mediaIdToChildren[ALL_ROOT] = allChildren
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadedMetadata.forEach {
|
rootList += allMetadata
|
||||||
val allChildren = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
rootList += downloadsMetadata
|
||||||
allChildren += it
|
|
||||||
mediaIdToChildren[DOWNLOADS_ROOT] = allChildren
|
// Server library cat
|
||||||
|
libraryCategories.find { it.id == "library" }?.let { libraryCategory ->
|
||||||
|
var libraryMediaMetadata = libraryCategory.entities.map { libc ->
|
||||||
|
var libraryItem = libc as LibraryItem
|
||||||
|
libraryItem.getMediaMetadata()
|
||||||
}
|
}
|
||||||
|
libraryMediaMetadata.forEach {
|
||||||
|
val children = mediaIdToChildren[ALL_ROOT] ?: mutableListOf()
|
||||||
|
children += it
|
||||||
|
mediaIdToChildren[ALL_ROOT] = children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libraryCategories.find { it.id == "local-books" }?.let { localBooksCat ->
|
||||||
|
var localMediaMetadata = localBooksCat.entities.map { libc ->
|
||||||
|
var libraryItem = libc as LocalLibraryItem
|
||||||
|
libraryItem.getMediaMetadata()
|
||||||
|
}
|
||||||
|
localMediaMetadata.forEach {
|
||||||
|
val children = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
||||||
|
children += it
|
||||||
|
mediaIdToChildren[DOWNLOADS_ROOT] = children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaIdToChildren[AUTO_BROWSE_ROOT] = rootList
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(mediaId: String) = mediaIdToChildren[mediaId]
|
operator fun get(mediaId: String) = mediaIdToChildren[mediaId]
|
||||||
|
@ -90,4 +107,3 @@ const val AUTO_BROWSE_ROOT = "/"
|
||||||
const val ALL_ROOT = "__ALL__"
|
const val ALL_ROOT = "__ALL__"
|
||||||
const val CONTINUE_ROOT = "__CONTINUE__"
|
const val CONTINUE_ROOT = "__CONTINUE__"
|
||||||
const val DOWNLOADS_ROOT = "__DOWNLOADS__"
|
const val DOWNLOADS_ROOT = "__DOWNLOADS__"
|
||||||
//const val LOCAL_ROOT = "__LOCAL__"
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import android.support.v4.media.session.MediaSessionCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import com.audiobookshelf.app.data.LibraryItem
|
import com.audiobookshelf.app.data.LibraryItem
|
||||||
|
import com.audiobookshelf.app.data.LibraryItemWrapper
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -27,7 +28,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
||||||
Log.d(tag, "ON PREPARE MEDIA SESSION COMPAT")
|
Log.d(tag, "ON PREPARE MEDIA SESSION COMPAT")
|
||||||
playerNotificationService.mediaManager.getFirstItem()?.let { li ->
|
playerNotificationService.mediaManager.getFirstItem()?.let { li ->
|
||||||
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
||||||
Log.d(tag, "About to prepare player with li ${li.title}")
|
Log.d(tag, "About to prepare player with ${it.displayTitle}")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
playerNotificationService.preparePlayer(it,true)
|
playerNotificationService.preparePlayer(it,true)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +50,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
||||||
Log.d(tag, "ON PLAY FROM SEARCH $query")
|
Log.d(tag, "ON PLAY FROM SEARCH $query")
|
||||||
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
||||||
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
||||||
Log.d(tag, "About to prepare player with li ${li.title}")
|
Log.d(tag, "About to prepare player with ${it.displayTitle}")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
playerNotificationService.preparePlayer(it,true)
|
playerNotificationService.preparePlayer(it,true)
|
||||||
}
|
}
|
||||||
|
@ -88,16 +89,16 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
||||||
|
|
||||||
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||||
Log.d(tag, "ON PLAY FROM MEDIA ID $mediaId")
|
Log.d(tag, "ON PLAY FROM MEDIA ID $mediaId")
|
||||||
var libraryItem: LibraryItem? = null
|
var libraryItemWrapper: LibraryItemWrapper? = null
|
||||||
if (mediaId.isNullOrEmpty()) {
|
if (mediaId.isNullOrEmpty()) {
|
||||||
libraryItem = playerNotificationService.mediaManager.getFirstItem()
|
libraryItemWrapper = playerNotificationService.mediaManager.getFirstItem()
|
||||||
} else {
|
} else {
|
||||||
libraryItem = playerNotificationService.mediaManager.getById(mediaId)
|
libraryItemWrapper = playerNotificationService.mediaManager.getById(mediaId)
|
||||||
}
|
}
|
||||||
|
|
||||||
libraryItem?.let { li ->
|
libraryItemWrapper?.let { li ->
|
||||||
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
||||||
Log.d(tag, "About to prepare player with li ${li.title}")
|
Log.d(tag, "About to prepare player with ${it.displayTitle}")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
playerNotificationService.preparePlayer(it,true)
|
playerNotificationService.preparePlayer(it,true)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +111,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleCallMediaButton(intent: Intent): Boolean {
|
fun handleCallMediaButton(intent: Intent): Boolean {
|
||||||
if(Intent.ACTION_MEDIA_BUTTON == intent.getAction()) {
|
if(Intent.ACTION_MEDIA_BUTTON == intent.action) {
|
||||||
var keyEvent = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
|
var keyEvent = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
|
||||||
if (keyEvent?.getAction() == KeyEvent.ACTION_UP) {
|
if (keyEvent?.getAction() == KeyEvent.ACTION_UP) {
|
||||||
when (keyEvent?.getKeyCode()) {
|
when (keyEvent?.getKeyCode()) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.ResultReceiver
|
||||||
import android.support.v4.media.session.PlaybackStateCompat
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.audiobookshelf.app.data.LibraryItem
|
import com.audiobookshelf.app.data.LibraryItem
|
||||||
|
import com.audiobookshelf.app.data.LibraryItemWrapper
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
|
||||||
|
|
||||||
|
@ -40,10 +41,10 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
|
||||||
override fun onPrepareFromMediaId(mediaId: String, playWhenReady: Boolean, extras: Bundle?) {
|
override fun onPrepareFromMediaId(mediaId: String, playWhenReady: Boolean, extras: Bundle?) {
|
||||||
Log.d(tag, "ON PREPARE FROM MEDIA ID $mediaId $playWhenReady")
|
Log.d(tag, "ON PREPARE FROM MEDIA ID $mediaId $playWhenReady")
|
||||||
|
|
||||||
var libraryItem: LibraryItem? = playerNotificationService.mediaManager.getById(mediaId)
|
var libraryItemWrapper: LibraryItemWrapper? = playerNotificationService.mediaManager.getById(mediaId)
|
||||||
libraryItem?.let { li ->
|
libraryItemWrapper?.let { li ->
|
||||||
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
||||||
Log.d(tag, "About to prepare player with li ${li.title}")
|
Log.d(tag, "About to prepare player with ${it.displayTitle}")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
playerNotificationService.preparePlayer(it,playWhenReady)
|
playerNotificationService.preparePlayer(it,playWhenReady)
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
|
||||||
Log.d(tag, "ON PREPARE FROM SEARCH $query")
|
Log.d(tag, "ON PREPARE FROM SEARCH $query")
|
||||||
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
||||||
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
playerNotificationService.mediaManager.play(li, playerNotificationService.getMediaPlayer()) {
|
||||||
Log.d(tag, "About to prepare player with li ${li.title}")
|
Log.d(tag, "About to prepare player with ${it.displayTitle}")
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
playerNotificationService.preparePlayer(it,playWhenReady)
|
playerNotificationService.preparePlayer(it,playWhenReady)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.*
|
||||||
|
import io.paperdb.Paper
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
initSensor()
|
initSensor()
|
||||||
|
|
||||||
// Initialize media manager
|
// Initialize media manager
|
||||||
mediaManager = MediaManager(apiHandler)
|
mediaManager = MediaManager(apiHandler, ctx)
|
||||||
|
|
||||||
channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
createNotificationChannel(channelId, channelName)
|
createNotificationChannel(channelId, channelName)
|
||||||
|
@ -300,6 +301,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
var metadata = playbackSession.getMediaMetadataCompat()
|
var metadata = playbackSession.getMediaMetadataCompat()
|
||||||
mediaSession.setMetadata(metadata)
|
mediaSession.setMetadata(metadata)
|
||||||
var mediaItems = playbackSession.getMediaItems()
|
var mediaItems = playbackSession.getMediaItems()
|
||||||
|
|
||||||
|
if (mediaItems.isEmpty()) {
|
||||||
|
Log.e(tag, "Invalid playback session no media items to play")
|
||||||
|
currentPlaybackSession = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (mPlayer == currentPlayer) {
|
if (mPlayer == currentPlayer) {
|
||||||
var mediaSource:MediaSource
|
var mediaSource:MediaSource
|
||||||
|
|
||||||
|
@ -561,7 +569,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
// No further calls will be made to other media browsing methods.
|
// No further calls will be made to other media browsing methods.
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
// Flag is used to enable syncing progress natively (normally syncing is handled in webview)
|
if (!isStarted) {
|
||||||
|
Log.d(tag, "AA Not yet started")
|
||||||
|
mediaManager.initializeAndroidAuto()
|
||||||
|
isStarted = true
|
||||||
|
}
|
||||||
|
|
||||||
isAndroidAuto = true
|
isAndroidAuto = true
|
||||||
|
|
||||||
val extras = Bundle()
|
val extras = Bundle()
|
||||||
|
@ -584,9 +597,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
var flag = if (parentMediaId == AUTO_MEDIA_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
|
var flag = if (parentMediaId == AUTO_MEDIA_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
|
||||||
|
|
||||||
result.detach()
|
result.detach()
|
||||||
mediaManager.loadLibraryItems { libraryItems ->
|
|
||||||
var itemMediaMetadata:List<MediaMetadataCompat> = libraryItems.map { it.getMediaMetadata() }
|
mediaManager.loadAndroidAutoItems("main") { libraryCategories ->
|
||||||
browseTree = BrowseTree(this, mutableListOf(), itemMediaMetadata, mutableListOf())
|
browseTree = BrowseTree(this, libraryCategories)
|
||||||
val children = browseTree[parentMediaId]?.map { item ->
|
val children = browseTree[parentMediaId]?.map { item ->
|
||||||
MediaBrowserCompat.MediaItem(item.description, flag)
|
MediaBrowserCompat.MediaItem(item.description, flag)
|
||||||
}
|
}
|
||||||
|
@ -605,9 +618,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
override fun onSearch(query: String, extras: Bundle?, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
|
override fun onSearch(query: String, extras: Bundle?, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
|
||||||
result.detach()
|
result.detach()
|
||||||
mediaManager.loadLibraryItems { libraryItems ->
|
mediaManager.loadAndroidAutoItems("main") { libraryCategories ->
|
||||||
var itemMediaMetadata:List<MediaMetadataCompat> = libraryItems.map { it.getMediaMetadata() }
|
browseTree = BrowseTree(this, libraryCategories)
|
||||||
browseTree = BrowseTree(this, mutableListOf(), itemMediaMetadata, mutableListOf())
|
|
||||||
val children = browseTree[ALL_ROOT]?.map { item ->
|
val children = browseTree[ALL_ROOT]?.map { item ->
|
||||||
MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
|
MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,7 @@ import android.content.SharedPreferences
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import com.audiobookshelf.app.data.*
|
||||||
import com.audiobookshelf.app.data.Library
|
|
||||||
import com.audiobookshelf.app.data.LibraryItem
|
|
||||||
import com.audiobookshelf.app.data.LocalMediaProgress
|
|
||||||
import com.audiobookshelf.app.data.PlaybackSession
|
|
||||||
import com.audiobookshelf.app.device.DeviceManager
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
import com.audiobookshelf.app.player.MediaProgressSyncData
|
import com.audiobookshelf.app.player.MediaProgressSyncData
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
@ -21,14 +17,15 @@ import com.getcapacitor.JSObject
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import org.json.JSONObject
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
class ApiHandler(var ctx:Context) {
|
||||||
class ApiHandler {
|
|
||||||
val tag = "ApiHandler"
|
val tag = "ApiHandler"
|
||||||
|
|
||||||
private var client = OkHttpClient()
|
private var client = OkHttpClient()
|
||||||
var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||||
var ctx: Context
|
|
||||||
var storageSharedPreferences: SharedPreferences? = null
|
var storageSharedPreferences: SharedPreferences? = null
|
||||||
|
|
||||||
data class LocalMediaProgressSyncPayload(val localMediaProgress:List<LocalMediaProgress>)
|
data class LocalMediaProgressSyncPayload(val localMediaProgress:List<LocalMediaProgress>)
|
||||||
|
@ -36,10 +33,6 @@ class ApiHandler {
|
||||||
data class MediaProgressSyncResponsePayload(val numServerProgressUpdates:Int, val localProgressUpdates:List<LocalMediaProgress>)
|
data class MediaProgressSyncResponsePayload(val numServerProgressUpdates:Int, val localProgressUpdates:List<LocalMediaProgress>)
|
||||||
data class LocalMediaProgressSyncResultsPayload(var numLocalMediaProgressForServer:Int, var numServerProgressUpdates:Int, var numLocalProgressUpdates:Int)
|
data class LocalMediaProgressSyncResultsPayload(var numLocalMediaProgressForServer:Int, var numServerProgressUpdates:Int, var numLocalProgressUpdates:Int)
|
||||||
|
|
||||||
constructor(_ctx: Context) {
|
|
||||||
ctx = _ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRequest(endpoint:String, cb: (JSObject) -> Unit) {
|
fun getRequest(endpoint:String, cb: (JSObject) -> Unit) {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}")
|
.url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}")
|
||||||
|
@ -67,7 +60,6 @@ class ApiHandler {
|
||||||
|
|
||||||
fun isOnline(): Boolean {
|
fun isOnline(): Boolean {
|
||||||
val connectivityManager = ctx.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
val connectivityManager = ctx.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
if (connectivityManager != null) {
|
|
||||||
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||||
if (capabilities != null) {
|
if (capabilities != null) {
|
||||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||||
|
@ -81,7 +73,6 @@ class ApiHandler {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +142,27 @@ class ApiHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLibraryCategories(libraryId:String, cb: (List<LibraryCategory>) -> Unit) {
|
||||||
|
getRequest("/api/libraries/$libraryId/personalized") {
|
||||||
|
val items = mutableListOf<LibraryCategory>()
|
||||||
|
if (it.has("value")) {
|
||||||
|
var array = it.getJSONArray("value")
|
||||||
|
for (i in 0 until array.length()) {
|
||||||
|
var jsobj = array.get(i) as JSONObject
|
||||||
|
|
||||||
|
var type = jsobj.get("type").toString()
|
||||||
|
// Only support for podcast and book in android auto
|
||||||
|
if (type == "podcast" || type == "book") {
|
||||||
|
jsobj.put("isLocal", false)
|
||||||
|
val item = jacksonMapper.readValue<LibraryCategory>(jsobj.toString())
|
||||||
|
items.add(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun playLibraryItem(libraryItemId:String, episodeId:String?, forceTranscode:Boolean, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
fun playLibraryItem(libraryItemId:String, episodeId:String?, forceTranscode:Boolean, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
||||||
var payload = JSObject()
|
var payload = JSObject()
|
||||||
payload.put("mediaPlayer", mediaPlayer)
|
payload.put("mediaPlayer", mediaPlayer)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue