diff --git a/app/src/main/java/com/dd3boh/outertune/constants/PreferenceKeys.kt b/app/src/main/java/com/dd3boh/outertune/constants/PreferenceKeys.kt index d706e43bf..ff883d6b2 100644 --- a/app/src/main/java/com/dd3boh/outertune/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/dd3boh/outertune/constants/PreferenceKeys.kt @@ -49,6 +49,7 @@ val PersistentQueueKey = booleanPreferencesKey("persistentQueue") val SkipSilenceKey = booleanPreferencesKey("skipSilence") val SkipOnErrorKey = booleanPreferencesKey("skipOnError") val AudioNormalizationKey = booleanPreferencesKey("audioNormalization") +val AutoLoadMoreKey = booleanPreferencesKey("autoLoadMore") val KeepAliveKey = booleanPreferencesKey("keepAlive") val StopMusicOnTaskClearKey = booleanPreferencesKey("stopMusicOnTaskClear") diff --git a/app/src/main/java/com/dd3boh/outertune/db/DatabaseDao.kt b/app/src/main/java/com/dd3boh/outertune/db/DatabaseDao.kt index 831ffc1d5..1053a9c81 100644 --- a/app/src/main/java/com/dd3boh/outertune/db/DatabaseDao.kt +++ b/app/src/main/java/com/dd3boh/outertune/db/DatabaseDao.kt @@ -304,7 +304,8 @@ interface DatabaseDao : SongsDao, AlbumsDao, ArtistsDao, PlaylistsDao, QueueDao title = mq.title, shuffled = mq.shuffled, queuePos = mq.queuePos, - index = mq.index + index = mq.index, + playlistId = mq.playlistId ) ) diff --git a/app/src/main/java/com/dd3boh/outertune/db/daos/QueueDao.kt b/app/src/main/java/com/dd3boh/outertune/db/daos/QueueDao.kt index 6150e7b94..385c1d051 100644 --- a/app/src/main/java/com/dd3boh/outertune/db/daos/QueueDao.kt +++ b/app/src/main/java/com/dd3boh/outertune/db/daos/QueueDao.kt @@ -55,7 +55,8 @@ interface QueueDao { unShuffled = unshuffledSongs.map { it.toMediaMetadata() }.toMutableList(), shuffled = queue.shuffled, queuePos = queue.queuePos, - index = queue.index + index = queue.index, + playlistId = queue.playlistId ) ) } diff --git a/app/src/main/java/com/dd3boh/outertune/models/QueueBoard.kt b/app/src/main/java/com/dd3boh/outertune/models/QueueBoard.kt index 853c14564..ebe39e276 100644 --- a/app/src/main/java/com/dd3boh/outertune/models/QueueBoard.kt +++ b/app/src/main/java/com/dd3boh/outertune/models/QueueBoard.kt @@ -1,10 +1,9 @@ package com.dd3boh.outertune.models import androidx.media3.common.C -import androidx.media3.common.MediaItem import com.dd3boh.outertune.constants.PersistentQueueKey import com.dd3boh.outertune.db.entities.QueueEntity -import com.dd3boh.outertune.extensions.metadata +import com.dd3boh.outertune.extensions.currentMetadata import com.dd3boh.outertune.extensions.move import com.dd3boh.outertune.extensions.toMediaItem import com.dd3boh.outertune.playback.MusicService @@ -33,7 +32,7 @@ var isShuffleEnabled: MutableStateFlow = MutableStateFlow(false) * @param title Queue title (and UID) * @param queue List of media items */ -class MultiQueueObject( +data class MultiQueueObject( val id: Long, val title: String, /** @@ -47,7 +46,11 @@ class MultiQueueObject( var shuffled: Boolean = false, var queuePos: Int = -1, // position of current song var index: Int, // order of queue - val playlistId: String? = null, + /** + * Song id to start watch endpoint + * TODO: change this in database too + */ + var playlistId: String? = null, ) { /** @@ -169,6 +172,7 @@ class QueueBoard(queues: MutableList = ArrayList()) { forceInsert: Boolean = false, replace: Boolean = false, delta: Boolean = true, + isRadio: Boolean = false, startIndex: Int = 0 ): Boolean { if (QUEUE_DEBUG) @@ -296,17 +300,20 @@ class QueueBoard(queues: MutableList = ArrayList()) { } } else { // add entirely new queue + // Precondition(s): radio queues never include local songs if (masterQueues.size > MAX_QUEUES) { deleteQueue(masterQueues.first(), player) } + val q = ArrayList(mediaList.filterNotNull()) val newQueue = MultiQueueObject( QueueEntity.generateQueueId(), title, - ArrayList(mediaList.filterNotNull()), + q, ArrayList(mediaList.filterNotNull()), false, startIndex, - masterQueues.size + masterQueues.size, + if (isRadio) q.lastOrNull()?.id else null ) masterQueues.add(newQueue) if (player.dataStore.get(PersistentQueueKey, true)) { @@ -326,16 +333,17 @@ class QueueBoard(queues: MutableList = ArrayList()) { forceInsert: Boolean = false, replace: Boolean = false, delta: Boolean = true, - startIndex: Int = 0 - ) = addQueue(title, mediaList, playerConnection.service, forceInsert, replace, delta, startIndex) + startIndex: Int = 0, + isRadio: Boolean = false, + ) = addQueue(title, mediaList, playerConnection.service, forceInsert, replace, delta, isRadio, startIndex) /** * Add songs to end of CURRENT QUEUE & update it in the player */ - fun enqueueEnd(mediaList: List, player: MusicService) { + fun enqueueEnd(mediaList: List, player: MusicService, isRadio: Boolean = false) { getCurrentQueue()?.let { - addSongsToQueue(it, Int.MAX_VALUE, mediaList, player) + addSongsToQueue(it, Int.MAX_VALUE, mediaList, player, isRadio = isRadio) } } @@ -347,12 +355,13 @@ class QueueBoard(queues: MutableList = ArrayList()) { pos: Int, mediaList: List, player: MusicService, - saveToDb: Boolean = true + saveToDb: Boolean = true, + isRadio: Boolean = false ) { val listPos = if (pos < 0) { 0 } else if (pos > q.queue.size) { - q.queue.size - 1 + q.queue.size } else { pos } @@ -360,14 +369,16 @@ class QueueBoard(queues: MutableList = ArrayList()) { // Add to current queue at the position. For the other queue, just add to end if (q.shuffled) { q.queue.addAll(listPos, mediaList) - q.unShuffled.addAll(q.queue.size - 1, mediaList) + q.unShuffled.addAll(q.queue.size, mediaList) } else { - q.queue.addAll(q.queue.size - 1, mediaList) + q.queue.addAll(q.queue.size, mediaList) q.unShuffled.addAll(listPos, mediaList) } - // copy so ui doesnt crash - player.player.replaceMediaItems(listPos, pos, mediaList.map { it.toMediaItem() }) + setCurrQueue(q, player) + if (isRadio) { + q.playlistId = mediaList.lastOrNull()?.id + } if (saveToDb && player.dataStore.get(PersistentQueueKey, true)) { CoroutineScope(Dispatchers.IO).launch { @@ -706,14 +717,45 @@ class QueueBoard(queues: MutableList = ArrayList()) { masterIndex = masterQueues.indexOf(item) // if requested to get shuffled queue + val mediaItems: MutableList? if (item.shuffled) { - player.player.setMediaItems(item.queue.map { it.toMediaItem() }) + mediaItems = item.queue } else { - player.player.setMediaItems(item.unShuffled.map { it.toMediaItem() }) + mediaItems = item.unShuffled + } + + /** + * current playing == jump target, do seamlessly + */ + val seamlessSupported = player.player.currentMetadata?.id == mediaItems[queuePos].id + if (seamlessSupported) { + if (queuePos == 0) { + val playerIndex = player.player.currentMediaItemIndex + val playerItemCount = player.player.mediaItemCount + // player.player.replaceMediaItems seems to stop playback so we + // remove all songs except the currently playing one and then add the list of new items + if (playerIndex < playerItemCount - 1) { + player.player.removeMediaItems(playerIndex + 1, playerItemCount) + } + if (playerIndex > 0) { + player.player.removeMediaItems(0, playerIndex) + } + // add all songs except the first one since it is already present and playing + player.player.addMediaItems(mediaItems.drop(1).map { it.toMediaItem() }) + } else { + // replace items up to current playing, then replace items after current + player.player.replaceMediaItems(0, queuePos, + mediaItems.subList(0, queuePos).map { it.toMediaItem() }) + player.player.replaceMediaItems(queuePos + 1, Int.MAX_VALUE, + mediaItems.subList(queuePos + 1, mediaItems.size).map { it.toMediaItem() }) + } + } else { + player.player.setMediaItems(mediaItems.map { it.toMediaItem() }) } + isShuffleEnabled.value = item.shuffled - if (autoSeek) { + if (autoSeek && !seamlessSupported) { player.player.seekTo(queuePos, C.TIME_UNSET) } @@ -739,30 +781,6 @@ class QueueBoard(queues: MutableList = ArrayList()) { } } - /** - * Update the current position index of the current queue to the index of the FIRST media item match - * - * @param mediaItem - */ - fun setCurrQueuePosIndex(mediaItem: MediaItem?, player: MusicService) { - val currentQueue = getCurrentQueue() - if (mediaItem == null || currentQueue == null) { - return - } - - if (currentQueue.shuffled) { - currentQueue.queuePos = currentQueue.queue.indexOf(mediaItem.metadata) - } else { - currentQueue.queuePos = currentQueue.unShuffled.indexOf(mediaItem.metadata) - } - - if (player.dataStore.get(PersistentQueueKey, true)) { - CoroutineScope(Dispatchers.IO).launch { - player.database.updateQueue(currentQueue) - } - } - } - companion object { val mutex = Mutex() diff --git a/app/src/main/java/com/dd3boh/outertune/playback/MusicService.kt b/app/src/main/java/com/dd3boh/outertune/playback/MusicService.kt index 8a5e98960..5d8d90481 100644 --- a/app/src/main/java/com/dd3boh/outertune/playback/MusicService.kt +++ b/app/src/main/java/com/dd3boh/outertune/playback/MusicService.kt @@ -62,6 +62,7 @@ import com.dd3boh.outertune.constants.AudioNormalizationKey import com.dd3boh.outertune.constants.AudioOffload import com.dd3boh.outertune.constants.AudioQuality import com.dd3boh.outertune.constants.AudioQualityKey +import com.dd3boh.outertune.constants.AutoLoadMoreKey import com.dd3boh.outertune.constants.KeepAliveKey import com.dd3boh.outertune.constants.LastPosKey import com.dd3boh.outertune.constants.MediaSessionConstants.CommandToggleLike @@ -91,6 +92,7 @@ import com.dd3boh.outertune.extensions.metadata import com.dd3boh.outertune.extensions.setOffloadEnabled import com.dd3boh.outertune.extensions.toMediaItem import com.dd3boh.outertune.lyrics.LyricsHelper +import com.dd3boh.outertune.models.MediaMetadata import com.dd3boh.outertune.models.QueueBoard import com.dd3boh.outertune.models.isShuffleEnabled import com.dd3boh.outertune.models.toMediaMetadata @@ -137,6 +139,7 @@ import java.net.SocketTimeoutException import java.net.UnknownHostException import java.time.LocalDateTime import javax.inject.Inject +import kotlin.collections.map import kotlin.math.min import kotlin.math.pow @@ -279,6 +282,22 @@ class MusicService : MediaLibraryService(), player.play() } + // Auto load more songs + val q = queueBoard.getCurrentQueue() + val songId = q?.playlistId + if (dataStore.get(AutoLoadMoreKey, true) && + reason != Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT && + player.mediaItemCount - player.currentMediaItemIndex <= 5 && + songId != null // aka "hasNext" + ) { + scope.launch(SilentHandler) { + val mediaItems = YouTubeQueue(WatchEndpoint(songId)).nextPage() + if (player.playbackState != STATE_IDLE) { + queueBoard.enqueueEnd(mediaItems.drop(1), this@MusicService, isRadio = true) + } + } + } + // this absolute eye sore detects if we loop back to the beginning of queue, when shuffle AND repeat all // no, when repeat mode is on, player does not "STATE_ENDED" if (player.currentMediaItemIndex == 0 && lastMediaItemIndex == player.mediaItemCount - 1 && @@ -292,7 +311,7 @@ class MusicService : MediaLibraryService(), updateNotification() // also updates when queue changes queueBoard.setCurrQueuePosIndex(player.currentMediaItemIndex, this@MusicService) - queueTitle = queueBoard.getCurrentQueue()?.title + queueTitle = q?.title } }) sleepTimer = SleepTimer(scope, this) @@ -568,7 +587,13 @@ class MusicService : MediaLibraryService(), * @param title Title override for the queue. If this value us unspecified, this method takes the value from queue. * If both are unspecified, the title will default to "Queue". */ - fun playQueue(queue: Queue, playWhenReady: Boolean = true, replace: Boolean = false, title: String? = null) { + fun playQueue( + queue: Queue, + playWhenReady: Boolean = true, + replace: Boolean = false, + isRadio: Boolean = false, + title: String? = null + ) { if (!queueBoard.initialized) { initQueue() queueBoard.initialized = true @@ -581,17 +606,26 @@ class MusicService : MediaLibraryService(), if (queueTitle == null && initialStatus.title != null) { // do not find a title if an override is provided queueTitle = initialStatus.title } + val items = ArrayList() + val preloadItem = queue.preloadItem // print out queue // println("-----------------------------") // initialStatus.items.map { println(it.title) } if (initialStatus.items.isEmpty()) return@launch + if (preloadItem != null) { + items.add(preloadItem) + items.addAll(initialStatus.items.subList(1, initialStatus.items.size)) + } else { + items.addAll(initialStatus.items) + } queueBoard.addQueue( queueTitle?: "Queue", - initialStatus.items, + items, player = this@MusicService, startIndex = if (initialStatus.mediaItemIndex > 0) initialStatus.mediaItemIndex else 0, - replace = replace + replace = replace, + isRadio = isRadio ) queueBoard.setCurrQueue(this@MusicService) @@ -600,30 +634,14 @@ class MusicService : MediaLibraryService(), } } - fun startRadioSeamlessly() { - val currentMediaMetadata = player.currentMetadata ?: return - if (player.currentMediaItemIndex > 0) player.removeMediaItems(0, player.currentMediaItemIndex) - if (player.currentMediaItemIndex < player.mediaItemCount - 1) player.removeMediaItems(player.currentMediaItemIndex + 1, player.mediaItemCount) - scope.launch(SilentHandler) { - val radioQueue = YouTubeQueue(endpoint = WatchEndpoint(videoId = currentMediaMetadata.id)) - val initialStatus = radioQueue.getInitialStatus() - if (initialStatus.title != null) { - queueTitle = initialStatus.title - } - player.addMediaItems(initialStatus.items.drop(1).map { it.toMediaItem() }) - } - } - /** * Add items to queue, right after current playing item */ fun enqueueNext(items: List) { - println("wtf "+ "ENQUEUNE NEXT CALLED " + queueBoard.initialized) if (!queueBoard.initialized) { // when enqueuing next when player isn't active, play as a new song if (items.isNotEmpty()) { - println("wtf "+ "PLAYING FROMe") CoroutineScope(Dispatchers.Main).launch { playQueue( ListQueue( @@ -668,7 +686,8 @@ class MusicService : MediaLibraryService(), } fun toggleStartRadio() { - startRadioSeamlessly() + val mediaMetadata = player.currentMetadata ?: return + playQueue(YouTubeQueue.radio(mediaMetadata), isRadio = true) } private fun openAudioEffectSession() { diff --git a/app/src/main/java/com/dd3boh/outertune/playback/PlayerConnection.kt b/app/src/main/java/com/dd3boh/outertune/playback/PlayerConnection.kt index c1ab20f5c..ecddc29d5 100644 --- a/app/src/main/java/com/dd3boh/outertune/playback/PlayerConnection.kt +++ b/app/src/main/java/com/dd3boh/outertune/playback/PlayerConnection.kt @@ -95,8 +95,8 @@ class PlayerConnection( repeatMode.value = player.repeatMode } - fun playQueue(queue: Queue, replace: Boolean = true, title: String? = null) { - service.playQueue(queue, replace = replace, title = title) + fun playQueue(queue: Queue, replace: Boolean = true, isRadio: Boolean = false, title: String? = null) { + service.playQueue(queue, replace = replace, title = title, isRadio = isRadio) } /** diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlayerMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlayerMenu.kt index b4b93c7fd..b7f54cfae 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlayerMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlayerMenu.kt @@ -336,7 +336,7 @@ fun PlayerMenu( icon = Icons.Rounded.Radio, title = R.string.start_radio ) { - playerConnection.playQueue(YouTubeQueue.radio(mediaMetadata)) + playerConnection.playQueue(YouTubeQueue.radio(mediaMetadata), isRadio = true) onDismiss() } GridMenuItem( diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt index 56efc8118..f8ee72ddc 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt @@ -331,7 +331,7 @@ fun PlaylistMenu( playerConnection.playQueue(YouTubeQueue(WatchEndpoint( playlistId = "RDAMPL$browseId", params = radioEndpointParams, - ))) + )), isRadio = true) onDismiss() } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt index c144a67c1..f6a515542 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt @@ -277,7 +277,7 @@ fun SongMenu( title = R.string.start_radio ) { onDismiss() - playerConnection.playQueue(YouTubeQueue.radio(song.toMediaMetadata())) + playerConnection.playQueue(YouTubeQueue.radio(song.toMediaMetadata()), isRadio = true) } GridMenuItem( icon = Icons.AutoMirrored.Rounded.PlaylistPlay, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubeArtistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubeArtistMenu.kt index b20ba70ba..4547853e2 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubeArtistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubeArtistMenu.kt @@ -87,7 +87,7 @@ fun YouTubeArtistMenu( icon = Icons.Rounded.Radio, title = R.string.start_radio ) { - playerConnection.playQueue(YouTubeQueue(watchEndpoint)) + playerConnection.playQueue(YouTubeQueue(watchEndpoint), isRadio = true) onDismiss() } } @@ -96,7 +96,7 @@ fun YouTubeArtistMenu( icon = Icons.Rounded.Shuffle, title = R.string.shuffle ) { - playerConnection.playQueue(YouTubeQueue(watchEndpoint)) + playerConnection.playQueue(YouTubeQueue(watchEndpoint), isRadio = true) onDismiss() } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubePlaylistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubePlaylistMenu.kt index 6d8c002c2..4e63eb4ee 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubePlaylistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/YouTubePlaylistMenu.kt @@ -334,7 +334,7 @@ fun YouTubePlaylistMenu( title = R.string.start_radio ) { println("Radio: ${radioEndpoint.playlistId}, ${radioEndpoint.params}") - playerConnection.playQueue(YouTubeQueue(radioEndpoint)) + playerConnection.playQueue(YouTubeQueue(radioEndpoint), isRadio = true) onDismiss() } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt index fbb0f8865..deadd7d44 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt @@ -828,11 +828,11 @@ fun HomeScreen( is SongItem -> playerConnection.playQueue(YouTubeQueue.radio(luckyItem.toMediaMetadata())) is AlbumItem -> playerConnection.playQueue(YouTubeAlbumRadio(luckyItem.playlistId)) is ArtistItem -> luckyItem.radioEndpoint?.let { - playerConnection.playQueue(YouTubeQueue(it)) + playerConnection.playQueue(YouTubeQueue(it), isRadio = true) } is PlaylistItem -> luckyItem.playEndpoint?.let { - playerConnection.playQueue(YouTubeQueue(it)) + playerConnection.playQueue(YouTubeQueue(it), isRadio = true) } } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt index 48a25f879..ebb98f31f 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt @@ -217,6 +217,7 @@ fun ArtistScreen( title = artistName, items = librarySongsAvailable().shuffled(), ), + isRadio = true, title = artistName ) }, @@ -240,6 +241,7 @@ fun ArtistScreen( onClick = { playerConnection.playQueue( YouTubeQueue(radioEndpoint), + isRadio = true, title = "Radio: ${artistPage.artist.title}" ) }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/PlayerSettings.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/PlayerSettings.kt index d99daedd2..b75558876 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/PlayerSettings.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/PlayerSettings.kt @@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.QueueMusic import androidx.compose.material.icons.automirrored.rounded.VolumeUp +import androidx.compose.material.icons.rounded.Autorenew import androidx.compose.material.icons.rounded.Bolt import androidx.compose.material.icons.rounded.ClearAll import androidx.compose.material.icons.rounded.FastForward @@ -35,6 +36,7 @@ import com.dd3boh.outertune.constants.AudioNormalizationKey import com.dd3boh.outertune.constants.AudioOffload import com.dd3boh.outertune.constants.AudioQuality import com.dd3boh.outertune.constants.AudioQualityKey +import com.dd3boh.outertune.constants.AutoLoadMoreKey import com.dd3boh.outertune.constants.KeepAliveKey import com.dd3boh.outertune.constants.PersistentQueueKey import com.dd3boh.outertune.constants.SkipOnErrorKey @@ -62,6 +64,7 @@ fun PlayerSettings( val (skipSilence, onSkipSilenceChange) = rememberPreference(key = SkipSilenceKey, defaultValue = false) val (skipOnErrorKey, onSkipOnErrorChange) = rememberPreference(key = SkipOnErrorKey, defaultValue = true) val (audioNormalization, onAudioNormalizationChange) = rememberPreference(key = AudioNormalizationKey, defaultValue = true) + val (autoLoadMore, onAutoLoadMoreChange) = rememberPreference(AutoLoadMoreKey, defaultValue = true) val (stopMusicOnTaskClear, onStopMusicOnTaskClearChange) = rememberPreference(key = StopMusicOnTaskClearKey, defaultValue = false) val (minPlaybackDur, onMinPlaybackDurChange) = rememberPreference(minPlaybackDurKey, defaultValue = 30) val (audioOffload, onAudioOffloadChange) = rememberPreference(key = AudioOffload, defaultValue = false) @@ -96,7 +99,7 @@ fun PlayerSettings( .verticalScroll(rememberScrollState()) ) { PreferenceGroupTitle( - title = stringResource(R.string.grp_layout) + title = stringResource(R.string.grp_general) ) SwitchPreference( title = { Text(stringResource(R.string.persistent_queue)) }, @@ -105,6 +108,13 @@ fun PlayerSettings( checked = persistentQueue, onCheckedChange = onPersistentQueueChange ) + SwitchPreference( + title = { Text(stringResource(R.string.auto_load_more)) }, + description = stringResource(R.string.auto_load_more_desc), + icon = { Icon(Icons.Rounded.Autorenew, null) }, + checked = autoLoadMore, + onCheckedChange = onAutoLoadMoreChange + ) // lyrics settings PreferenceEntry( title = { Text(stringResource(R.string.lyrics_settings_title)) }, diff --git a/app/src/main/res/values/strings-ot.xml b/app/src/main/res/values/strings-ot.xml index 05ec5d768..68e16476f 100644 --- a/app/src/main/res/values/strings-ot.xml +++ b/app/src/main/res/values/strings-ot.xml @@ -57,6 +57,7 @@ Audio Additional scanner settings + General Interface Layout Localization