mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-01 00:25:26 +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")
|
||||
|
||||
// var ss = SimpleStorage(this)
|
||||
// ss.requestFullStorageAccess()
|
||||
// Grant full storage access for testing
|
||||
// var ss = SimpleStorage(this)
|
||||
// ss.requestFullStorageAccess()
|
||||
|
||||
var permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
|
|
|
@ -26,7 +26,7 @@ data class LibraryItem(
|
|||
var mediaType:String,
|
||||
var media:MediaType,
|
||||
var libraryFiles:MutableList<LibraryFile>?
|
||||
) {
|
||||
) : LibraryItemWrapper() {
|
||||
@get:JsonIgnore
|
||||
val title get() = media.metadata.title
|
||||
@get:JsonIgnore
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.audiobookshelf.app.data
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||
import java.util.*
|
||||
|
||||
data class ServerConnectionConfig(
|
||||
|
@ -18,7 +20,14 @@ data class DeviceData(
|
|||
var serverConnectionConfigs:MutableList<ServerConnectionConfig>,
|
||||
var lastServerConnectionConfigId:String?,
|
||||
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)
|
||||
data class LocalFile(
|
||||
|
@ -48,3 +57,10 @@ data class LocalFolder(
|
|||
var storageType: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
|
||||
|
||||
import android.net.Uri
|
||||
import android.support.v4.media.MediaMetadataCompat
|
||||
import android.util.Log
|
||||
import com.audiobookshelf.app.R
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
@ -25,11 +28,19 @@ data class LocalLibraryItem(
|
|||
var serverAddress:String?,
|
||||
var serverUserId:String?,
|
||||
var libraryItemId:String?
|
||||
) {
|
||||
|
||||
) : LibraryItemWrapper() {
|
||||
@get:JsonIgnore
|
||||
val title get() = media.metadata.title
|
||||
@get:JsonIgnore
|
||||
val authorName get() = media.metadata.getAuthorDisplayName()
|
||||
@get:JsonIgnore
|
||||
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
|
||||
fun getDuration():Double {
|
||||
var total = 0.0
|
||||
|
@ -80,4 +91,18 @@ data class LocalLibraryItem(
|
|||
fun removeLocalFile(localFileId:String) {
|
||||
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
|
||||
|
||||
object DeviceManager {
|
||||
val tag = "DeviceManager"
|
||||
const val tag = "DeviceManager"
|
||||
|
||||
val dbManager:DbManager = DbManager()
|
||||
var deviceData:DeviceData = dbManager.getDeviceData()
|
||||
var serverConnectionConfig: ServerConnectionConfig? = null
|
||||
|
@ -15,6 +16,8 @@ object DeviceManager {
|
|||
val serverAddress get() = serverConnectionConfig?.address ?: ""
|
||||
val serverUserId get() = serverConnectionConfig?.userId ?: ""
|
||||
val token get() = serverConnectionConfig?.token ?: ""
|
||||
val isConnectedToServer get() = serverConnectionConfig != null
|
||||
val hasLastServerConnectionConfig get() = deviceData.getLastServerConnectionConfig() != null
|
||||
|
||||
init {
|
||||
Log.d(tag, "Device Manager Singleton invoked")
|
||||
|
|
|
@ -1,43 +1,150 @@
|
|||
package com.audiobookshelf.app.media
|
||||
|
||||
import com.audiobookshelf.app.data.LibraryItem
|
||||
import com.audiobookshelf.app.data.PlaybackSession
|
||||
import android.bluetooth.BluetoothClass
|
||||
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 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 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()) {
|
||||
cb(serverLibraryItems)
|
||||
} else {
|
||||
apiHandler.getLibraryItems("main") { libraryItems ->
|
||||
apiHandler.getLibraryItems(libraryId) { libraryItems ->
|
||||
serverLibraryItems = libraryItems
|
||||
cb(libraryItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFirstItem() : LibraryItem? {
|
||||
return if (serverLibraryItems.isNotEmpty()) serverLibraryItems[0] else null
|
||||
fun loadLibraries(cb: (List<Library>) -> Unit) {
|
||||
if (serverLibraries.isNotEmpty()) {
|
||||
cb(serverLibraries)
|
||||
} else {
|
||||
apiHandler.getLibraries {
|
||||
serverLibraries = it
|
||||
cb(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getById(id:String) : LibraryItem? {
|
||||
return serverLibraryItems.find { it.id == id }
|
||||
// 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 getFromSearch(query:String?) : LibraryItem? {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
fun getFromSearch(query:String?) : LibraryItemWrapper? {
|
||||
if (query.isNullOrEmpty()) return getFirstItem()
|
||||
return serverLibraryItems.find {
|
||||
it.title.lowercase(Locale.getDefault()).contains(query.lowercase(Locale.getDefault()))
|
||||
}
|
||||
}
|
||||
|
||||
fun play(libraryItem:LibraryItem, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
||||
apiHandler.playLibraryItem(libraryItem.id,"",false, mediaPlayer) {
|
||||
cb(it)
|
||||
}
|
||||
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) {
|
||||
cb(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int {
|
||||
|
|
|
@ -7,14 +7,14 @@ import android.support.v4.media.MediaMetadataCompat
|
|||
import android.util.Log
|
||||
import androidx.annotation.AnyRes
|
||||
import com.audiobookshelf.app.R
|
||||
import com.audiobookshelf.app.data.LibraryCategory
|
||||
import com.audiobookshelf.app.data.LibraryItem
|
||||
import com.audiobookshelf.app.data.LocalLibraryItem
|
||||
|
||||
|
||||
class BrowseTree(
|
||||
val context: Context,
|
||||
itemsInProgress: List<LibraryItem>,
|
||||
itemsMetadata: List<MediaMetadataCompat>,
|
||||
downloadedMetadata: List<MediaMetadataCompat>
|
||||
libraryCategories: List<LibraryCategory>
|
||||
) {
|
||||
private val mediaIdToChildren = mutableMapOf<String, MutableList<MediaMetadataCompat>>()
|
||||
|
||||
|
@ -37,17 +37,14 @@ class BrowseTree(
|
|||
|
||||
val continueReadingMetadata = MediaMetadataCompat.Builder().apply {
|
||||
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())
|
||||
}.build()
|
||||
|
||||
val allMetadata = MediaMetadataCompat.Builder().apply {
|
||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, ALL_ROOT)
|
||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Library Items")
|
||||
|
||||
var resource = getUriToDrawable(context, R.drawable.exo_icon_books).toString()
|
||||
Log.d("BrowseTree", "RESOURCE $resource")
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, resource)
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_books).toString())
|
||||
}.build()
|
||||
|
||||
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())
|
||||
}.build()
|
||||
|
||||
if (itemsInProgress.isNotEmpty()) {
|
||||
rootList += continueReadingMetadata
|
||||
// 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
|
||||
}
|
||||
continueListeningMediaMetadata.forEach {
|
||||
val children = mediaIdToChildren[CONTINUE_ROOT] ?: mutableListOf()
|
||||
children += it
|
||||
mediaIdToChildren[CONTINUE_ROOT] = children
|
||||
}
|
||||
}
|
||||
|
||||
rootList += allMetadata
|
||||
rootList += downloadsMetadata
|
||||
// rootList += localsMetadata
|
||||
|
||||
// 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
|
||||
|
||||
itemsInProgress.forEach { libraryItem ->
|
||||
val children = mediaIdToChildren[CONTINUE_ROOT] ?: mutableListOf()
|
||||
children += libraryItem.getMediaMetadata()
|
||||
mediaIdToChildren[CONTINUE_ROOT] = children
|
||||
}
|
||||
|
||||
itemsMetadata.forEach {
|
||||
val allChildren = mediaIdToChildren[ALL_ROOT] ?: mutableListOf()
|
||||
allChildren += it
|
||||
mediaIdToChildren[ALL_ROOT] = allChildren
|
||||
}
|
||||
|
||||
downloadedMetadata.forEach {
|
||||
val allChildren = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
||||
allChildren += it
|
||||
mediaIdToChildren[DOWNLOADS_ROOT] = allChildren
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(mediaId: String) = mediaIdToChildren[mediaId]
|
||||
|
@ -90,4 +107,3 @@ const val AUTO_BROWSE_ROOT = "/"
|
|||
const val ALL_ROOT = "__ALL__"
|
||||
const val CONTINUE_ROOT = "__CONTINUE__"
|
||||
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.view.KeyEvent
|
||||
import com.audiobookshelf.app.data.LibraryItem
|
||||
import com.audiobookshelf.app.data.LibraryItemWrapper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -27,7 +28,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
|||
Log.d(tag, "ON PREPARE MEDIA SESSION COMPAT")
|
||||
playerNotificationService.mediaManager.getFirstItem()?.let { li ->
|
||||
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() {
|
||||
playerNotificationService.preparePlayer(it,true)
|
||||
}
|
||||
|
@ -49,7 +50,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
|||
Log.d(tag, "ON PLAY FROM SEARCH $query")
|
||||
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
||||
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() {
|
||||
playerNotificationService.preparePlayer(it,true)
|
||||
}
|
||||
|
@ -88,16 +89,16 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
|||
|
||||
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||
Log.d(tag, "ON PLAY FROM MEDIA ID $mediaId")
|
||||
var libraryItem: LibraryItem? = null
|
||||
var libraryItemWrapper: LibraryItemWrapper? = null
|
||||
if (mediaId.isNullOrEmpty()) {
|
||||
libraryItem = playerNotificationService.mediaManager.getFirstItem()
|
||||
libraryItemWrapper = playerNotificationService.mediaManager.getFirstItem()
|
||||
} else {
|
||||
libraryItem = playerNotificationService.mediaManager.getById(mediaId)
|
||||
libraryItemWrapper = playerNotificationService.mediaManager.getById(mediaId)
|
||||
}
|
||||
|
||||
libraryItem?.let { li ->
|
||||
libraryItemWrapper?.let { li ->
|
||||
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() {
|
||||
playerNotificationService.preparePlayer(it,true)
|
||||
}
|
||||
|
@ -110,7 +111,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
|
|||
}
|
||||
|
||||
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)
|
||||
if (keyEvent?.getAction() == KeyEvent.ACTION_UP) {
|
||||
when (keyEvent?.getKeyCode()) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.os.ResultReceiver
|
|||
import android.support.v4.media.session.PlaybackStateCompat
|
||||
import android.util.Log
|
||||
import com.audiobookshelf.app.data.LibraryItem
|
||||
import com.audiobookshelf.app.data.LibraryItemWrapper
|
||||
import com.google.android.exoplayer2.Player
|
||||
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?) {
|
||||
Log.d(tag, "ON PREPARE FROM MEDIA ID $mediaId $playWhenReady")
|
||||
|
||||
var libraryItem: LibraryItem? = playerNotificationService.mediaManager.getById(mediaId)
|
||||
libraryItem?.let { li ->
|
||||
var libraryItemWrapper: LibraryItemWrapper? = playerNotificationService.mediaManager.getById(mediaId)
|
||||
libraryItemWrapper?.let { li ->
|
||||
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() {
|
||||
playerNotificationService.preparePlayer(it,playWhenReady)
|
||||
}
|
||||
|
@ -55,7 +56,7 @@ class MediaSessionPlaybackPreparer(var playerNotificationService:PlayerNotificat
|
|||
Log.d(tag, "ON PREPARE FROM SEARCH $query")
|
||||
playerNotificationService.mediaManager.getFromSearch(query)?.let { li ->
|
||||
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() {
|
||||
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.ui.PlayerNotificationManager
|
||||
import com.google.android.exoplayer2.upstream.*
|
||||
import io.paperdb.Paper
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
|
@ -196,7 +197,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
initSensor()
|
||||
|
||||
// Initialize media manager
|
||||
mediaManager = MediaManager(apiHandler)
|
||||
mediaManager = MediaManager(apiHandler, ctx)
|
||||
|
||||
channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createNotificationChannel(channelId, channelName)
|
||||
|
@ -300,6 +301,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
var metadata = playbackSession.getMediaMetadataCompat()
|
||||
mediaSession.setMetadata(metadata)
|
||||
var mediaItems = playbackSession.getMediaItems()
|
||||
|
||||
if (mediaItems.isEmpty()) {
|
||||
Log.e(tag, "Invalid playback session no media items to play")
|
||||
currentPlaybackSession = null
|
||||
return
|
||||
}
|
||||
|
||||
if (mPlayer == currentPlayer) {
|
||||
var mediaSource:MediaSource
|
||||
|
||||
|
@ -561,7 +569,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
// No further calls will be made to other media browsing methods.
|
||||
null
|
||||
} 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
|
||||
|
||||
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
|
||||
|
||||
result.detach()
|
||||
mediaManager.loadLibraryItems { libraryItems ->
|
||||
var itemMediaMetadata:List<MediaMetadataCompat> = libraryItems.map { it.getMediaMetadata() }
|
||||
browseTree = BrowseTree(this, mutableListOf(), itemMediaMetadata, mutableListOf())
|
||||
|
||||
mediaManager.loadAndroidAutoItems("main") { libraryCategories ->
|
||||
browseTree = BrowseTree(this, libraryCategories)
|
||||
val children = browseTree[parentMediaId]?.map { item ->
|
||||
MediaBrowserCompat.MediaItem(item.description, flag)
|
||||
}
|
||||
|
@ -605,9 +618,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
|
||||
override fun onSearch(query: String, extras: Bundle?, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
|
||||
result.detach()
|
||||
mediaManager.loadLibraryItems { libraryItems ->
|
||||
var itemMediaMetadata:List<MediaMetadataCompat> = libraryItems.map { it.getMediaMetadata() }
|
||||
browseTree = BrowseTree(this, mutableListOf(), itemMediaMetadata, mutableListOf())
|
||||
mediaManager.loadAndroidAutoItems("main") { libraryCategories ->
|
||||
browseTree = BrowseTree(this, libraryCategories)
|
||||
val children = browseTree[ALL_ROOT]?.map { item ->
|
||||
MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
|
||||
}
|
||||
|
|
|
@ -5,11 +5,7 @@ import android.content.SharedPreferences
|
|||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
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.data.*
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
import com.audiobookshelf.app.player.MediaProgressSyncData
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
@ -21,14 +17,15 @@ import com.getcapacitor.JSObject
|
|||
import okhttp3.*
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class ApiHandler {
|
||||
class ApiHandler(var ctx:Context) {
|
||||
val tag = "ApiHandler"
|
||||
|
||||
private var client = OkHttpClient()
|
||||
var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
var ctx: Context
|
||||
|
||||
var storageSharedPreferences: SharedPreferences? = null
|
||||
|
||||
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 LocalMediaProgressSyncResultsPayload(var numLocalMediaProgressForServer:Int, var numServerProgressUpdates:Int, var numLocalProgressUpdates:Int)
|
||||
|
||||
constructor(_ctx: Context) {
|
||||
ctx = _ctx
|
||||
}
|
||||
|
||||
fun getRequest(endpoint:String, cb: (JSObject) -> Unit) {
|
||||
val request = Request.Builder()
|
||||
.url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}")
|
||||
|
@ -67,19 +60,17 @@ class ApiHandler {
|
|||
|
||||
fun isOnline(): Boolean {
|
||||
val connectivityManager = ctx.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
if (connectivityManager != null) {
|
||||
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||
if (capabilities != null) {
|
||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_CELLULAR")
|
||||
return true
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_WIFI")
|
||||
return true
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_ETHERNET")
|
||||
return true
|
||||
}
|
||||
val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||
if (capabilities != null) {
|
||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_CELLULAR")
|
||||
return true
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_WIFI")
|
||||
return true
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
Log.i("Internet", "NetworkCapabilities.TRANSPORT_ETHERNET")
|
||||
return true
|
||||
}
|
||||
}
|
||||
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) {
|
||||
var payload = JSObject()
|
||||
payload.put("mediaPlayer", mediaPlayer)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue