Enchancements for Android Auto library

- Hide libraries without audiobooks
- Sort books in series by sequence value
- Added option for selecting ASC or DESC sorting for series
- Order authors alphabetically
This commit is contained in:
ISO-B
2024-10-27 21:55:17 +02:00
parent e4a3cc5290
commit 8134ec84c6
11 changed files with 189 additions and 47 deletions
@@ -214,7 +214,9 @@ class BookMetadata(
var authorName:String?, var authorName:String?,
var authorNameLF:String?, var authorNameLF:String?,
var narratorName:String?, var narratorName:String?,
var seriesName:String? var seriesName:String?,
@JsonFormat(with=[JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY])
var series:List<SeriesType>?
) : MediaTypeMetadata(title, explicit) { ) : MediaTypeMetadata(title, explicit) {
@JsonIgnore @JsonIgnore
override fun getAuthorDisplayName():String { return authorName ?: "Unknown" } override fun getAuthorDisplayName():String { return authorName ?: "Unknown" }
@@ -342,7 +344,8 @@ data class Library(
var name:String, var name:String,
var folders:MutableList<Folder>, var folders:MutableList<Folder>,
var icon:String, var icon:String,
var mediaType:String var mediaType:String,
var stats: LibraryStats?
) { ) {
@JsonIgnore @JsonIgnore
fun getMediaMetadata(): MediaMetadataCompat { fun getMediaMetadata(): MediaMetadataCompat {
@@ -354,6 +357,20 @@ data class Library(
} }
} }
@JsonIgnoreProperties(ignoreUnknown = true)
data class LibraryStats(
var totalItems: Int,
var totalAuthors: Int,
var numAudioTracks: Int
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class SeriesType(
var id: String,
var name: String,
var sequence: String?
)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class Folder( data class Folder(
var id:String, var id:String,
@@ -28,6 +28,10 @@ enum class StreamingUsingCellularSetting {
ASK, ALWAYS, NEVER ASK, ALWAYS, NEVER
} }
enum class AndroidAutoBrowseSeriesSequenceOrderSetting {
ASC, DESC
}
data class ServerConnectionConfig( data class ServerConnectionConfig(
var id:String, var id:String,
var index:Int, var index:Int,
@@ -136,7 +140,8 @@ data class DeviceSettings(
var streamingUsingCellular: StreamingUsingCellularSetting, var streamingUsingCellular: StreamingUsingCellularSetting,
var androidAutoBrowseForceGrouping: Boolean, var androidAutoBrowseForceGrouping: Boolean,
var androidAutoBrowseTopLevelLimitForGrouping: Int, var androidAutoBrowseTopLevelLimitForGrouping: Int,
var androidAutoBrowseLimitForGrouping: Int var androidAutoBrowseLimitForGrouping: Int,
var androidAutoBrowseSeriesSequenceOrder: AndroidAutoBrowseSeriesSequenceOrderSetting
) { ) {
companion object { companion object {
// Static method to get default device settings // Static method to get default device settings
@@ -165,7 +170,8 @@ data class DeviceSettings(
streamingUsingCellular = StreamingUsingCellularSetting.ALWAYS, streamingUsingCellular = StreamingUsingCellularSetting.ALWAYS,
androidAutoBrowseForceGrouping = false, androidAutoBrowseForceGrouping = false,
androidAutoBrowseTopLevelLimitForGrouping = 100, androidAutoBrowseTopLevelLimitForGrouping = 100,
androidAutoBrowseLimitForGrouping = 50 androidAutoBrowseLimitForGrouping = 50,
androidAutoBrowseSeriesSequenceOrder = AndroidAutoBrowseSeriesSequenceOrderSetting.ASC
) )
} }
} }
@@ -64,8 +64,27 @@ class LibraryItem(
} }
} }
@get:JsonIgnore
val seriesSequence: String
get() {
if (mediaType != "podcast") {
return ((media as Book).metadata as BookMetadata).series?.get(0)?.sequence.orEmpty()
} else {
return ""
}
}
@get:JsonIgnore
val seriesSequenceParts: List<String>
get() {
if (seriesSequence.isEmpty()) {
return listOf("")
}
return seriesSequence.split(".", limit = 2)
}
@JsonIgnore @JsonIgnore
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?): MediaDescriptionCompat { fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?): MediaDescriptionCompat {
val extras = Bundle() val extras = Bundle()
if (collapsedSeries == null) { if (collapsedSeries == null) {
@@ -121,20 +140,29 @@ class LibraryItem(
if (collapsedSeries != null) { if (collapsedSeries != null) {
subtitle = "${collapsedSeries!!.numBooks} books" subtitle = "${collapsedSeries!!.numBooks} books"
} }
var itemTitle = title
if (showSeriesNumber == true && seriesSequence != "") {
itemTitle = "$seriesSequence. $itemTitle"
}
return MediaDescriptionCompat.Builder() return MediaDescriptionCompat.Builder()
.setMediaId(mediaId) .setMediaId(mediaId)
.setTitle(title) .setTitle(itemTitle)
.setIconUri(getCoverUri()) .setIconUri(getCoverUri())
.setSubtitle(subtitle) .setSubtitle(subtitle)
.setExtras(extras) .setExtras(extras)
.build() .build()
} }
@JsonIgnore
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?): MediaDescriptionCompat {
return getMediaDescription(progress, ctx, authorId, null)
}
@JsonIgnore @JsonIgnore
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat { override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat {
/* /*
This is needed so Android auto library hierarchy for author series can be implemented This is needed so Android auto library hierarchy for author series can be implemented
*/ */
return getMediaDescription(progress, ctx, null) return getMediaDescription(progress, ctx, null, null)
} }
} }
@@ -41,7 +41,7 @@ data class LocalMediaItem(
@JsonIgnore @JsonIgnore
fun getMediaMetadata():MediaTypeMetadata { fun getMediaMetadata():MediaTypeMetadata {
return if (mediaType == "book") { return if (mediaType == "book") {
BookMetadata(name,null, mutableListOf(), mutableListOf(), mutableListOf(),null,null,null,null,null,null,null,false,null,null,null,null) BookMetadata(name,null, mutableListOf(), mutableListOf(), mutableListOf(),null,null,null,null,null,null,null,false,null,null,null, null, null)
} else { } else {
PodcastMetadata(name,null,null, mutableListOf(), false) PodcastMetadata(name,null,null, mutableListOf(), false)
} }
@@ -71,6 +71,9 @@ object DeviceManager {
if (deviceData.deviceSettings?.androidAutoBrowseLimitForGrouping == null) { if (deviceData.deviceSettings?.androidAutoBrowseLimitForGrouping == null) {
deviceData.deviceSettings?.androidAutoBrowseLimitForGrouping = 50 deviceData.deviceSettings?.androidAutoBrowseLimitForGrouping = 50
} }
if (deviceData.deviceSettings?.androidAutoBrowseSeriesSequenceOrder == null) {
deviceData.deviceSettings?.androidAutoBrowseSeriesSequenceOrder = AndroidAutoBrowseSeriesSequenceOrderSetting.ASC
}
} }
fun getBase64Id(id:String):String { fun getBase64Id(id:String):String {
@@ -46,6 +46,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
return serverLibraries.find { it.id == id } != null return serverLibraries.find { it.id == id } != null
} }
fun getLibrary(id:String) : Library? {
return serverLibraries.find { it.id == id }
}
fun getSavedPlaybackRate():Float { fun getSavedPlaybackRate():Float {
if (userSettingsPlaybackRate != null) { if (userSettingsPlaybackRate != null) {
return userSettingsPlaybackRate ?: 1f return userSettingsPlaybackRate ?: 1f
@@ -185,6 +189,14 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cb(seriesWithBooks) cb(seriesWithBooks)
} }
fun sortSeriesBooks(seriesBooks: List<LibraryItem>) : List<LibraryItem> {
val sortingLogic = compareBy<LibraryItem> { it.seriesSequenceParts[0].length }
.thenBy { it.seriesSequenceParts[0].ifEmpty { "" } }
.thenBy { it.seriesSequenceParts.getOrElse(1) { "" }.length }
.thenBy { it.seriesSequenceParts.getOrElse(1) { "" } }
return seriesBooks.sortedWith(sortingLogic)
}
/** /**
* Returns books for series from library. * Returns books for series from library.
* If data is not found from local cache then it will be fetched from server * If data is not found from local cache then it will be fetched from server
@@ -202,14 +214,15 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
Log.d(tag, "Items for series $seriesId loaded from server | Library $libraryId") Log.d(tag, "Items for series $seriesId loaded from server | Library $libraryId")
val libraryItemsWithAudio = libraryItems.filter { li -> li.checkHasTracks() } val libraryItemsWithAudio = libraryItems.filter { li -> li.checkHasTracks() }
cachedLibrarySeriesItem[libraryId]!![seriesId] = libraryItemsWithAudio val sortedLibraryItemsWithAudio = sortSeriesBooks(libraryItemsWithAudio)
cachedLibrarySeriesItem[libraryId]!![seriesId] = sortedLibraryItemsWithAudio
libraryItemsWithAudio.forEach { libraryItem -> sortedLibraryItemsWithAudio.forEach { 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)
} }
} }
cb(libraryItemsWithAudio) cb(sortedLibraryItemsWithAudio)
} }
} }
} }
@@ -228,8 +241,8 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
apiHandler.getLibraryAuthors(libraryId) { authorItems -> apiHandler.getLibraryAuthors(libraryId) { authorItems ->
Log.d(tag, "Authors with books loaded from server | Library $libraryId ") Log.d(tag, "Authors with books loaded from server | Library $libraryId ")
// TO-DO: This check won't ensure that there is audiobooks. Current API won't offer ability to do so // TO-DO: This check won't ensure that there is audiobooks. Current API won't offer ability to do so
val authorItemsWithBooks = authorItems.filter { li -> li.bookCount != null && li.bookCount!! > 0 } var authorItemsWithBooks = authorItems.filter { li -> li.bookCount != null && li.bookCount!! > 0 }
authorItemsWithBooks = authorItemsWithBooks.sortedBy { it.name }
// Ensure that there is map for library // Ensure that there is map for library
cachedLibraryAuthors[libraryId] = mutableMapOf() cachedLibraryAuthors[libraryId] = mutableMapOf()
// Cache authors // Cache authors
@@ -314,15 +327,16 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
Log.d(tag, "Using author name: $authorName") Log.d(tag, "Using author name: $authorName")
val libraryItemsFromAuthorWithAudio = libraryItemsWithAudio.filter { li -> li.authorName.indexOf(authorName, ignoreCase = true) >= 0 } val libraryItemsFromAuthorWithAudio = libraryItemsWithAudio.filter { li -> li.authorName.indexOf(authorName, ignoreCase = true) >= 0 }
cachedLibraryAuthorSeriesItems[libraryId]!![authorId] = libraryItemsFromAuthorWithAudio val sortedLibraryItemsWithAudio = sortSeriesBooks(libraryItemsFromAuthorWithAudio)
cachedLibraryAuthorSeriesItems[libraryId]!![authorId] = sortedLibraryItemsWithAudio
libraryItemsFromAuthorWithAudio.forEach { libraryItem -> sortedLibraryItemsWithAudio.forEach { 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)
} }
} }
cb(libraryItemsFromAuthorWithAudio) cb(sortedLibraryItemsWithAudio)
} }
} }
} }
@@ -443,9 +457,15 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
if (serverLibraries.isNotEmpty()) { if (serverLibraries.isNotEmpty()) {
cb(serverLibraries) cb(serverLibraries)
} else { } else {
apiHandler.getLibraries { apiHandler.getLibraries { loadedLibraries ->
serverLibraries = it serverLibraries = loadedLibraries.map { library ->
cb(it) apiHandler.getLibraryStats(library.id) { libraryStats ->
Log.d(tag, "Library stats for library ${library.id} | $libraryStats")
library.stats = libraryStats
}
library
}
cb(serverLibraries)
} }
} }
} }
@@ -56,6 +56,7 @@ class BrowseTree(
rootList += librariesMetadata rootList += librariesMetadata
libraries.forEach { library -> libraries.forEach { library ->
if (library.stats?.numAudioTracks == 0) return@forEach
val libraryMediaMetadata = library.getMediaMetadata() val libraryMediaMetadata = library.getMediaMetadata()
val children = mediaIdToChildren[LIBRARIES_ROOT] ?: mutableListOf() val children = mediaIdToChildren[LIBRARIES_ROOT] ?: mutableListOf()
children += libraryMediaMetadata children += libraryMediaMetadata
@@ -1099,30 +1099,44 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
} }
} else if (mediaManager.getIsLibrary(parentMediaId)) { // Load library items for library } else if (mediaManager.getIsLibrary(parentMediaId)) { // Load library items for library
Log.d(tag, "Loading items for library $parentMediaId") Log.d(tag, "Loading items for library $parentMediaId")
val children = mutableListOf( val selectedLibrary = mediaManager.getLibrary(parentMediaId)
MediaBrowserCompat.MediaItem( if (selectedLibrary?.mediaType == "podcast") { // Podcasts are browseable
MediaDescriptionCompat.Builder() mediaManager.loadLibraryItemsWithAudio(parentMediaId) { libraryItems ->
.setTitle("Library") val children = libraryItems.map { libraryItem ->
.setMediaId("__LIBRARY__${parentMediaId}__AUTHORS") val mediaDescription = libraryItem.getMediaDescription(null, ctx)
.build(), MediaBrowserCompat.MediaItem(
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE mediaDescription,
), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
MediaBrowserCompat.MediaItem( )
MediaDescriptionCompat.Builder() }
.setTitle("Series") result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
.setMediaId("__LIBRARY__${parentMediaId}__SERIES_LIST") }
.build(), } else {
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE val children = mutableListOf(
), MediaBrowserCompat.MediaItem(
MediaBrowserCompat.MediaItem( MediaDescriptionCompat.Builder()
MediaDescriptionCompat.Builder() .setTitle("Library")
.setTitle("Collections") .setMediaId("__LIBRARY__${parentMediaId}__AUTHORS")
.setMediaId("__LIBRARY__${parentMediaId}__COLLECTIONS") .build(),
.build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE ),
MediaBrowserCompat.MediaItem(
MediaDescriptionCompat.Builder()
.setTitle("Series")
.setMediaId("__LIBRARY__${parentMediaId}__SERIES_LIST")
.build(),
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
),
MediaBrowserCompat.MediaItem(
MediaDescriptionCompat.Builder()
.setTitle("Collections")
.setMediaId("__LIBRARY__${parentMediaId}__COLLECTIONS")
.build(),
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
)
) )
) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) }
} else if (parentMediaId.startsWith("__LIBRARY__")) { } else if (parentMediaId.startsWith("__LIBRARY__")) {
Log.d(tag, "Browsing library $parentMediaId") Log.d(tag, "Browsing library $parentMediaId")
val mediaIdParts = parentMediaId.split("__") val mediaIdParts = parentMediaId.split("__")
@@ -1201,12 +1215,16 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaIdParts[4] mediaIdParts[4]
) { libraryItems -> ) { libraryItems ->
Log.d(tag, "Received ${libraryItems.size} library items") Log.d(tag, "Received ${libraryItems.size} library items")
val children = libraryItems.map { libraryItem -> var items = libraryItems
if (DeviceManager.deviceData.deviceSettings!!.androidAutoBrowseSeriesSequenceOrder === AndroidAutoBrowseSeriesSequenceOrderSetting.DESC) {
items = libraryItems.reversed()
}
val children = items.map { libraryItem ->
val progress = val progress =
mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id } mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id }
val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id) val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id)
libraryItem.localLibraryItemId = localLibraryItem?.id libraryItem.localLibraryItemId = localLibraryItem?.id
val description = libraryItem.getMediaDescription(progress, ctx) val description = libraryItem.getMediaDescription(progress, ctx, null, true)
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
@@ -1279,11 +1297,15 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
} }
} else if (mediaIdParts[3] == "AUTHOR_SERIES") { } else if (mediaIdParts[3] == "AUTHOR_SERIES") {
mediaManager.loadAuthorSeriesBooksWithAudio(mediaIdParts[2], mediaIdParts[4], mediaIdParts[5]) { libraryItems -> mediaManager.loadAuthorSeriesBooksWithAudio(mediaIdParts[2], mediaIdParts[4], mediaIdParts[5]) { libraryItems ->
val children = libraryItems.map { libraryItem -> var items = libraryItems
if (DeviceManager.deviceData.deviceSettings!!.androidAutoBrowseSeriesSequenceOrder === AndroidAutoBrowseSeriesSequenceOrderSetting.DESC) {
items = libraryItems.reversed()
}
val children = items.map { libraryItem ->
val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id } val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id }
val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id) val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id)
libraryItem.localLibraryItemId = localLibraryItem?.id libraryItem.localLibraryItemId = localLibraryItem?.id
val description = libraryItem.getMediaDescription(progress, ctx) val description = libraryItem.getMediaDescription(progress, ctx, null, true)
if (libraryItem.collapsedSeries != null) { if (libraryItem.collapsedSeries != null) {
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
} else { } else {
@@ -150,6 +150,18 @@ class ApiHandler(var ctx:Context) {
} }
} }
fun getLibraryStats(libraryItemId:String, cb: (LibraryStats?) -> Unit) {
getRequest("/api/libraries/$libraryItemId/stats", null, null) {
if (it.has("error")) {
Log.e(tag, it.getString("error") ?: "getLibraryStats Failed")
cb(null)
} else {
val libraryStats = jacksonMapper.readValue<LibraryStats>(it.toString())
cb(libraryStats)
}
}
}
fun getLibraryItem(libraryItemId:String, cb: (LibraryItem?) -> Unit) { fun getLibraryItem(libraryItemId:String, cb: (LibraryItem?) -> Unit) {
getRequest("/api/items/$libraryItemId?expanded=1", null, null) { getRequest("/api/items/$libraryItemId?expanded=1", null, null) {
if (it.has("error")) { if (it.has("error")) {
+31 -1
View File
@@ -170,6 +170,12 @@
<ui-text-input type="number" v-model="settings.androidAutoBrowseLimitForGrouping" style="width: 145px; max-width: 145px" @input="androidAutoBrowseLimitForGroupingUpdated" /> <ui-text-input type="number" v-model="settings.androidAutoBrowseLimitForGrouping" style="width: 145px; max-width: 145px" @input="androidAutoBrowseLimitForGroupingUpdated" />
<span class="material-icons-outlined ml-2" @click.stop="showInfo('androidAutoBrowseLimitForGrouping')">info</span> <span class="material-icons-outlined ml-2" @click.stop="showInfo('androidAutoBrowseLimitForGrouping')">info</span>
</div> </div>
<div class="py-3 flex items-center">
<p class="pr-4 w-36">{{ $strings.LabelAndroidAutoBrowseSeriesSequenceOrder }}</p>
<div @click.stop="showAndroidAutoBrowseSeriesSequenceOrderOptions">
<ui-text-input :value="androidAutoBrowseSeriesSequenceOrderOption" readonly append-icon="expand_more" style="max-width: 200px" />
</div>
</div>
</template> </template>
<div v-show="loading" class="w-full h-full absolute top-0 left-0 flex items-center justify-center z-10"> <div v-show="loading" class="w-full h-full absolute top-0 left-0 flex items-center justify-center z-10">
@@ -218,7 +224,8 @@ export default {
streamingUsingCellular: 'ALWAYS', streamingUsingCellular: 'ALWAYS',
androidAutoBrowseForceGrouping: false, androidAutoBrowseForceGrouping: false,
androidAutoBrowseTopLevelLimitForGrouping: 100, androidAutoBrowseTopLevelLimitForGrouping: 100,
androidAutoBrowseLimitForGrouping: 50 androidAutoBrowseLimitForGrouping: 50,
androidAutoBrowseSeriesSequenceOrder: 'ASC'
}, },
theme: 'dark', theme: 'dark',
lockCurrentOrientation: false, lockCurrentOrientation: false,
@@ -323,6 +330,16 @@ export default {
text: this.$strings.LabelNever, text: this.$strings.LabelNever,
value: 'NEVER' value: 'NEVER'
} }
],
androidAutoBrowseSeriesSequenceOrderItems: [
{
text: this.$strings.LabelAscending,
value: 'ASC'
},
{
text: this.$strings.LabelDescending,
value: 'DESC'
}
] ]
} }
}, },
@@ -405,6 +422,10 @@ export default {
const item = this.streamingUsingCellularItems.find((i) => i.value === this.settings.streamingUsingCellular) const item = this.streamingUsingCellularItems.find((i) => i.value === this.settings.streamingUsingCellular)
return item?.text || 'Error' return item?.text || 'Error'
}, },
androidAutoBrowseSeriesSequenceOrderOption() {
const item = this.androidAutoBrowseSeriesSequenceOrderItems.find((i) => i.value === this.settings.androidAutoBrowseSeriesSequenceOrder)
return item?.text || 'Error'
},
moreMenuItems() { moreMenuItems() {
if (this.moreMenuSetting === 'shakeSensitivity') return this.shakeSensitivityItems if (this.moreMenuSetting === 'shakeSensitivity') return this.shakeSensitivityItems
else if (this.moreMenuSetting === 'hapticFeedback') return this.hapticFeedbackItems else if (this.moreMenuSetting === 'hapticFeedback') return this.hapticFeedbackItems
@@ -412,6 +433,7 @@ export default {
else if (this.moreMenuSetting === 'theme') return this.themeOptionItems else if (this.moreMenuSetting === 'theme') return this.themeOptionItems
else if (this.moreMenuSetting === 'downloadUsingCellular') return this.downloadUsingCellularItems else if (this.moreMenuSetting === 'downloadUsingCellular') return this.downloadUsingCellularItems
else if (this.moreMenuSetting === 'streamingUsingCellular') return this.streamingUsingCellularItems else if (this.moreMenuSetting === 'streamingUsingCellular') return this.streamingUsingCellularItems
else if (this.moreMenuSetting === 'androidAutoBrowseSeriesSequenceOrder') return this.androidAutoBrowseSeriesSequenceOrderItems
return [] return []
} }
}, },
@@ -454,6 +476,10 @@ export default {
this.moreMenuSetting = 'streamingUsingCellular' this.moreMenuSetting = 'streamingUsingCellular'
this.showMoreMenuDialog = true this.showMoreMenuDialog = true
}, },
showAndroidAutoBrowseSeriesSequenceOrderOptions() {
this.moreMenuSetting = 'androidAutoBrowseSeriesSequenceOrder'
this.showMoreMenuDialog = true
},
clickMenuAction(action) { clickMenuAction(action) {
this.showMoreMenuDialog = false this.showMoreMenuDialog = false
if (this.moreMenuSetting === 'shakeSensitivity') { if (this.moreMenuSetting === 'shakeSensitivity') {
@@ -474,6 +500,9 @@ export default {
} else if (this.moreMenuSetting === 'streamingUsingCellular') { } else if (this.moreMenuSetting === 'streamingUsingCellular') {
this.settings.streamingUsingCellular = action this.settings.streamingUsingCellular = action
this.saveSettings() this.saveSettings()
} else if (this.moreMenuSetting === 'androidAutoBrowseSeriesSequenceOrder') {
this.settings.androidAutoBrowseSeriesSequenceOrder = action
this.saveSettings()
} }
}, },
saveTheme(theme) { saveTheme(theme) {
@@ -629,6 +658,7 @@ export default {
this.settings.androidAutoBrowseForceGrouping = deviceSettings.androidAutoBrowseForceGrouping this.settings.androidAutoBrowseForceGrouping = deviceSettings.androidAutoBrowseForceGrouping
this.settings.androidAutoBrowseTopLevelLimitForGrouping = deviceSettings.androidAutoBrowseTopLevelLimitForGrouping this.settings.androidAutoBrowseTopLevelLimitForGrouping = deviceSettings.androidAutoBrowseTopLevelLimitForGrouping
this.settings.androidAutoBrowseLimitForGrouping = deviceSettings.androidAutoBrowseLimitForGrouping this.settings.androidAutoBrowseLimitForGrouping = deviceSettings.androidAutoBrowseLimitForGrouping
this.settings.androidAutoBrowseSeriesSequenceOrder = deviceSettings.androidAutoBrowseSeriesSequenceOrder || 'ASC'
}, },
async init() { async init() {
this.loading = true this.loading = true
+3
View File
@@ -85,6 +85,7 @@
"HeaderTableOfContents": "Table of Contents", "HeaderTableOfContents": "Table of Contents",
"HeaderUserInterfaceSettings": "User Interface Settings", "HeaderUserInterfaceSettings": "User Interface Settings",
"HeaderYourStats": "Your Stats", "HeaderYourStats": "Your Stats",
"LabelAscending": "Ascending",
"LabelAddToPlaylist": "Add to Playlist", "LabelAddToPlaylist": "Add to Playlist",
"LabelAdded": "Added", "LabelAdded": "Added",
"LabelAddedAt": "Added At", "LabelAddedAt": "Added At",
@@ -95,6 +96,7 @@
"LabelAndroidAutoBrowseForceGroupingHelp": "Forces alphabetical drawdown while browsing library and series in Android Auto", "LabelAndroidAutoBrowseForceGroupingHelp": "Forces alphabetical drawdown while browsing library and series in Android Auto",
"LabelAndroidAutoBrowseLimitForGrouping": "Alphabetical drawdown stopitems", "LabelAndroidAutoBrowseLimitForGrouping": "Alphabetical drawdown stopitems",
"LabelAndroidAutoBrowseLimitForGroupingHelp": "Stop alphabetical drawdown when there is less than this amount of items to show", "LabelAndroidAutoBrowseLimitForGroupingHelp": "Stop alphabetical drawdown when there is less than this amount of items to show",
"LabelAndroidAutoBrowseSeriesSequenceOrder": "Series books order",
"LabelAndroidAutoBrowseTopLevelLimitForGrouping": "Alphabetical drawdown start items", "LabelAndroidAutoBrowseTopLevelLimitForGrouping": "Alphabetical drawdown start items",
"LabelAndroidAutoBrowseTopLevelLimitForGroupingHelp": "If top-level has more items than this alphabetical drawdown will be used", "LabelAndroidAutoBrowseTopLevelLimitForGroupingHelp": "If top-level has more items than this alphabetical drawdown will be used",
"LabelAskConfirmation": "Ask for confirmation", "LabelAskConfirmation": "Ask for confirmation",
@@ -120,6 +122,7 @@
"LabelContinueReading": "Continue Reading", "LabelContinueReading": "Continue Reading",
"LabelContinueSeries": "Continue Series", "LabelContinueSeries": "Continue Series",
"LabelCustomTime": "Custom time", "LabelCustomTime": "Custom time",
"LabelDescending": "Descending",
"LabelDescription": "Description", "LabelDescription": "Description",
"LabelDisableAudioFadeOut": "Disable audio fade out", "LabelDisableAudioFadeOut": "Disable audio fade out",
"LabelDisableAudioFadeOutHelp": "Audio volume will start decreasing when there is less than 1 minute remaining on the sleep timer. Enable this setting to not fade out.", "LabelDisableAudioFadeOutHelp": "Audio volume will start decreasing when there is less than 1 minute remaining on the sleep timer. Enable this setting to not fade out.",