playback: use single-queue system
Switch auxio to a single-queue system. "Play next" adds songs to the top of the queue, similar to before, and then "Add to queue" adds songs to the bottom of the queue. This enables many more enhancements to be made to the playback experience, at the cost of a feature I preferred. Resolves #44.
This commit is contained in:
parent
b92b08f8ab
commit
25dd276bd8
43 changed files with 187 additions and 478 deletions
|
@ -29,7 +29,6 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
|
|
|
@ -66,7 +66,7 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
|
|
||||||
setupToolbar(detailModel.curAlbum.value!!, R.menu.menu_album_detail) { itemId ->
|
setupToolbar(detailModel.curAlbum.value!!, R.menu.menu_album_detail) { itemId ->
|
||||||
if (itemId == R.id.action_queue_add) {
|
if (itemId == R.id.action_queue_add) {
|
||||||
playbackModel.addToUserQueue(detailModel.curAlbum.value!!)
|
playbackModel.playNext(detailModel.curAlbum.value!!)
|
||||||
requireContext().showToast(R.string.lbl_queue_added)
|
requireContext().showToast(R.string.lbl_queue_added)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -147,12 +147,6 @@ class AlbumDetailFragment : DetailFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.isInUserQueue.observe(viewLifecycleOwner) { inUserQueue ->
|
|
||||||
if (inUserQueue) {
|
|
||||||
detailAdapter.highlightSong(null, binding.detailRecycler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logD("Fragment created.")
|
logD("Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -115,12 +115,6 @@ class GenreDetailFragment : DetailFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.isInUserQueue.observe(viewLifecycleOwner) { inUserQueue ->
|
|
||||||
if (inUserQueue) {
|
|
||||||
detailAdapter.highlightSong(null, binding.detailRecycler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logD("Fragment created.")
|
logD("Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -15,6 +15,7 @@ class AdaptiveFloatingActionButton @JvmOverloads constructor(
|
||||||
size = SIZE_NORMAL
|
size = SIZE_NORMAL
|
||||||
|
|
||||||
if (resources.configuration.smallestScreenWidthDp >= 640) {
|
if (resources.configuration.smallestScreenWidthDp >= 640) {
|
||||||
|
// Use a large FAB on large screens, as it makes it easier to touch.
|
||||||
customSize = resources.getDimensionPixelSize(MaterialR.dimen.m3_large_fab_size)
|
customSize = resources.getDimensionPixelSize(MaterialR.dimen.m3_large_fab_size)
|
||||||
setMaxImageSize(
|
setMaxImageSize(
|
||||||
resources.getDimensionPixelSize(MaterialR.dimen.m3_large_fab_max_image_size)
|
resources.getDimensionPixelSize(MaterialR.dimen.m3_large_fab_max_image_size)
|
||||||
|
|
|
@ -131,8 +131,10 @@ class PlaybackFragment : Fragment() {
|
||||||
binding.playbackSeekBar.setProgress(pos)
|
binding.playbackSeekBar.setProgress(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.displayQueue.observe(viewLifecycleOwner) {
|
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
||||||
updateQueueIcon(queueItem)
|
// The queue icon uses a selector that will automatically tint the icon as active or
|
||||||
|
// inactive. We just need to set the flag.
|
||||||
|
queueItem.isEnabled = playbackModel.nextItemsInQueue.value!!.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->
|
playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->
|
||||||
|
@ -155,13 +157,4 @@ class PlaybackFragment : Fragment() {
|
||||||
// so we can't really do much
|
// so we can't really do much
|
||||||
(requireView().parent.parent.parent as PlaybackLayout).collapse()
|
(requireView().parent.parent.parent as PlaybackLayout).collapse()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateQueueIcon(queueItem: MenuItem) {
|
|
||||||
val userQueue = playbackModel.userQueue.value!!
|
|
||||||
val nextQueue = playbackModel.nextItemsInQueue.value!!
|
|
||||||
|
|
||||||
// The queue icon uses a selector that will automatically tint the icon as active or
|
|
||||||
// inactive. We just need to set the flag.
|
|
||||||
queueItem.isEnabled = !(userQueue.isEmpty() && nextQueue.isEmpty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,24 +21,17 @@ package org.oxycblt.auxio.playback
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MediatorLiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Transformations
|
import androidx.lifecycle.Transformations
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.oxycblt.auxio.R
|
|
||||||
import org.oxycblt.auxio.music.ActionHeader
|
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Header
|
|
||||||
import org.oxycblt.auxio.music.HeaderString
|
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.queue.QueueAdapter
|
|
||||||
import org.oxycblt.auxio.playback.state.LoopMode
|
import org.oxycblt.auxio.playback.state.LoopMode
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
|
@ -61,7 +54,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
|
|
||||||
// Queue
|
// Queue
|
||||||
private val mQueue = MutableLiveData(listOf<Song>())
|
private val mQueue = MutableLiveData(listOf<Song>())
|
||||||
private val mUserQueue = MutableLiveData(listOf<Song>())
|
|
||||||
private val mIndex = MutableLiveData(0)
|
private val mIndex = MutableLiveData(0)
|
||||||
private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS)
|
private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS)
|
||||||
|
|
||||||
|
@ -69,7 +61,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
private val mIsPlaying = MutableLiveData(false)
|
private val mIsPlaying = MutableLiveData(false)
|
||||||
private val mIsShuffling = MutableLiveData(false)
|
private val mIsShuffling = MutableLiveData(false)
|
||||||
private val mLoopMode = MutableLiveData(LoopMode.NONE)
|
private val mLoopMode = MutableLiveData(LoopMode.NONE)
|
||||||
private val mIsInUserQueue = MutableLiveData(false)
|
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
private var mIntentUri: Uri? = null
|
private var mIntentUri: Uri? = null
|
||||||
|
@ -83,12 +74,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
|
|
||||||
/** The current queue determined by [playbackMode] and [parent] */
|
/** The current queue determined by [playbackMode] and [parent] */
|
||||||
val queue: LiveData<List<Song>> get() = mQueue
|
val queue: LiveData<List<Song>> get() = mQueue
|
||||||
/** The queue created by the user. */
|
|
||||||
val userQueue: LiveData<List<Song>> get() = mUserQueue
|
|
||||||
/** The current [PlaybackMode] that also determines the queue */
|
/** The current [PlaybackMode] that also determines the queue */
|
||||||
val playbackMode: LiveData<PlaybackMode> get() = mMode
|
val playbackMode: LiveData<PlaybackMode> get() = mMode
|
||||||
/** Whether playback is originating from the user-generated queue or not */
|
|
||||||
val isInUserQueue: LiveData<Boolean> = mIsInUserQueue
|
|
||||||
|
|
||||||
val isPlaying: LiveData<Boolean> get() = mIsPlaying
|
val isPlaying: LiveData<Boolean> get() = mIsPlaying
|
||||||
val isShuffling: LiveData<Boolean> get() = mIsShuffling
|
val isShuffling: LiveData<Boolean> get() = mIsShuffling
|
||||||
|
@ -100,62 +87,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
queue.slice((mIndex.value!! + 1) until queue.size)
|
queue.slice((mIndex.value!! + 1) until queue.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The combined queue data used for UIs, with header data included */
|
|
||||||
val displayQueue = MediatorLiveData<List<BaseModel>>().apply {
|
|
||||||
val combine: (userQueue: List<Song>, nextQueue: List<Song>) -> List<BaseModel> =
|
|
||||||
{ userQueue, nextQueue ->
|
|
||||||
val queue = mutableListOf<BaseModel>()
|
|
||||||
|
|
||||||
if (userQueue.isNotEmpty()) {
|
|
||||||
queue += ActionHeader(
|
|
||||||
id = -2,
|
|
||||||
string = HeaderString.Single(R.string.lbl_next_user_queue),
|
|
||||||
icon = R.drawable.ic_clear,
|
|
||||||
desc = R.string.desc_clear_user_queue,
|
|
||||||
onClick = { playbackManager.clearUserQueue() }
|
|
||||||
)
|
|
||||||
|
|
||||||
queue += userQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextQueue.isNotEmpty()) {
|
|
||||||
val parentName = parent.value?.name
|
|
||||||
|
|
||||||
queue += Header(
|
|
||||||
id = -3,
|
|
||||||
string = HeaderString.WithArg(
|
|
||||||
R.string.fmt_next_from,
|
|
||||||
if (parentName != null) {
|
|
||||||
HeaderString.Arg.Value(parentName)
|
|
||||||
} else {
|
|
||||||
HeaderString.Arg.Resource(R.string.lbl_all_songs)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
queue += nextQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
queue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not move these around. The transformed value must be generated through this
|
|
||||||
// observer call first before the userQueue source uses it assuming that it's not
|
|
||||||
// null.
|
|
||||||
addSource(nextItemsInQueue) { nextQueue ->
|
|
||||||
value = combine(userQueue.value!!, nextQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
addSource(userQueue) { userQueue ->
|
|
||||||
value = combine(
|
|
||||||
userQueue,
|
|
||||||
requireNotNull(nextItemsInQueue.value) {
|
|
||||||
"Transformed value was not generated yet."
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val playbackManager = PlaybackStateManager.maybeGetInstance()
|
private val playbackManager = PlaybackStateManager.maybeGetInstance()
|
||||||
private val settingsManager = SettingsManager.getInstance()
|
private val settingsManager = SettingsManager.getInstance()
|
||||||
|
|
||||||
|
@ -285,95 +216,65 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an item at [adapterIndex], being a non-header data index.
|
* Remove a queue item using it's recyclerview adapter index. If the indices are valid,
|
||||||
* @param queueAdapter [QueueAdapter] instance to push changes to when successful.
|
* [apply] is called just before the change is committed so that the adapter can be updated.
|
||||||
*/
|
*/
|
||||||
fun removeQueueDataItem(adapterIndex: Int, queueAdapter: QueueAdapter) {
|
fun removeQueueDataItem(adapterIndex: Int, apply: () -> Unit) {
|
||||||
var index = adapterIndex.dec()
|
val adjusted = adapterIndex + (mQueue.value!!.size - nextItemsInQueue.value!!.size)
|
||||||
|
|
||||||
// If the item is in the user queue, then remove it from there after accounting for the header.
|
if (adjusted in mQueue.value!!.indices) {
|
||||||
if (index < mUserQueue.value!!.size) {
|
apply()
|
||||||
queueAdapter.removeItem(adapterIndex)
|
playbackManager.removeQueueItem(adjusted)
|
||||||
|
|
||||||
playbackManager.removeUserQueueItem(index)
|
|
||||||
} else {
|
|
||||||
// Translate the indices into proper queue indices if removing an item from there.
|
|
||||||
index += (mQueue.value!!.size - nextItemsInQueue.value!!.size)
|
|
||||||
|
|
||||||
if (userQueue.value!!.isNotEmpty()) {
|
|
||||||
index -= mUserQueue.value!!.size.inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
queueAdapter.removeItem(adapterIndex)
|
|
||||||
|
|
||||||
playbackManager.removeQueueItem(index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move a queue OR user queue item from [adapterFrom] to [adapterTo], as long as both
|
* Move queue items using their recyclerview adapter indices. If the indices are valid,
|
||||||
* indices are non-header data indices.
|
* [apply] is called just before the change is committed so that the adapter can be updated.
|
||||||
* @param queueAdapter [QueueAdapter] instance to push changes to when successful.
|
|
||||||
*/
|
*/
|
||||||
fun moveQueueDataItems(
|
fun moveQueueDataItems(adapterFrom: Int, adapterTo: Int, apply: () -> Unit): Boolean {
|
||||||
adapterFrom: Int,
|
val delta = (mQueue.value!!.size - nextItemsInQueue.value!!.size)
|
||||||
adapterTo: Int,
|
|
||||||
queueAdapter: QueueAdapter
|
|
||||||
): Boolean {
|
|
||||||
var from = adapterFrom.dec()
|
|
||||||
var to = adapterTo.dec()
|
|
||||||
|
|
||||||
if (from < mUserQueue.value!!.size) {
|
val from = adapterFrom + delta
|
||||||
// Ignore invalid movements to out of bounds, header, or queue positions
|
val to = adapterTo + delta
|
||||||
if (to >= mUserQueue.value!!.size || to < 0) return false
|
|
||||||
|
|
||||||
queueAdapter.moveItems(adapterFrom, adapterTo)
|
|
||||||
|
|
||||||
playbackManager.moveUserQueueItems(from, to)
|
|
||||||
} else {
|
|
||||||
// Ignore invalid movements to out of bounds or header positions
|
|
||||||
if (to < 0) return false
|
|
||||||
|
|
||||||
// Get the real queue positions from the nextInQueue positions
|
|
||||||
val delta = mQueue.value!!.size - nextItemsInQueue.value!!.size
|
|
||||||
|
|
||||||
from += delta
|
|
||||||
to += delta
|
|
||||||
|
|
||||||
if (userQueue.value!!.isNotEmpty()) {
|
|
||||||
// Ignore user queue positions
|
|
||||||
if (to <= mUserQueue.value!!.size.inc()) return false
|
|
||||||
|
|
||||||
from -= mUserQueue.value!!.size.inc()
|
|
||||||
to -= mUserQueue.value!!.size.inc()
|
|
||||||
|
|
||||||
// Ignore movements that are past the next songs
|
|
||||||
if (to <= mIndex.value!!) return false
|
|
||||||
}
|
|
||||||
|
|
||||||
queueAdapter.moveItems(adapterFrom, adapterTo)
|
|
||||||
|
|
||||||
|
if (from in mQueue.value!!.indices && to in mQueue.value!!.indices) {
|
||||||
|
apply()
|
||||||
playbackManager.moveQueueItems(from, to)
|
playbackManager.moveQueueItems(from, to)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a [Song] to the user queue.
|
* Add a [Song] to the top of the queue.
|
||||||
*/
|
*/
|
||||||
fun addToUserQueue(song: Song) {
|
fun playNext(song: Song) {
|
||||||
playbackManager.addToUserQueue(song)
|
playbackManager.playNext(song)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an [Album] to the user queue
|
* Add an [Album] to the top of the queue.
|
||||||
*/
|
*/
|
||||||
fun addToUserQueue(album: Album) {
|
fun playNext(album: Album) {
|
||||||
playbackManager.addToUserQueue(settingsManager.detailAlbumSort.sortAlbum(album))
|
playbackManager.playNext(settingsManager.detailAlbumSort.sortAlbum(album))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- STATUS FUNCTIONS ---
|
/**
|
||||||
|
* Add a [Song] to the end of the queue.
|
||||||
|
*/
|
||||||
|
fun addToQueue(song: Song) {
|
||||||
|
playbackManager.addToQueue(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an [Album] to the end of the queue.
|
||||||
|
*/
|
||||||
|
fun addToQueue(album: Album) {
|
||||||
|
playbackManager.addToQueue(settingsManager.detailAlbumSort.sortAlbum(album))
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- STATUS FUNCTIONS ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flip the playing status, e.g from playing to paused
|
* Flip the playing status, e.g from playing to paused
|
||||||
|
@ -405,7 +306,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
fun savePlaybackState(context: Context, onDone: () -> Unit) {
|
fun savePlaybackState(context: Context, onDone: () -> Unit) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
playbackManager.saveStateToDatabase(context)
|
playbackManager.saveStateToDatabase(context)
|
||||||
|
|
||||||
onDone()
|
onDone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,7 +345,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
mParent.value = playbackManager.parent
|
mParent.value = playbackManager.parent
|
||||||
mQueue.value = playbackManager.queue
|
mQueue.value = playbackManager.queue
|
||||||
mMode.value = playbackManager.playbackMode
|
mMode.value = playbackManager.playbackMode
|
||||||
mUserQueue.value = playbackManager.userQueue
|
|
||||||
mIndex.value = playbackManager.index
|
mIndex.value = playbackManager.index
|
||||||
mIsPlaying.value = playbackManager.isPlaying
|
mIsPlaying.value = playbackManager.isPlaying
|
||||||
mIsShuffling.value = playbackManager.isShuffling
|
mIsShuffling.value = playbackManager.isShuffling
|
||||||
|
@ -474,10 +373,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
mQueue.value = queue
|
mQueue.value = queue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUserQueueUpdate(userQueue: List<Song>) {
|
|
||||||
mUserQueue.value = userQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIndexUpdate(index: Int) {
|
override fun onIndexUpdate(index: Int) {
|
||||||
mIndex.value = index
|
mIndex.value = index
|
||||||
}
|
}
|
||||||
|
@ -497,8 +392,4 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
override fun onLoopUpdate(loopMode: LoopMode) {
|
override fun onLoopUpdate(loopMode: LoopMode) {
|
||||||
mLoopMode.value = loopMode
|
mLoopMode.value = loopMode
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInUserQueueUpdate(isInUserQueue: Boolean) {
|
|
||||||
mIsInUserQueue.value = isInUserQueue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ import org.oxycblt.auxio.ui.BaseViewHolder
|
||||||
import org.oxycblt.auxio.ui.DiffCallback
|
import org.oxycblt.auxio.ui.DiffCallback
|
||||||
import org.oxycblt.auxio.ui.HeaderViewHolder
|
import org.oxycblt.auxio.ui.HeaderViewHolder
|
||||||
import org.oxycblt.auxio.util.inflater
|
import org.oxycblt.auxio.util.inflater
|
||||||
import org.oxycblt.auxio.util.logD
|
|
||||||
import org.oxycblt.auxio.util.logE
|
import org.oxycblt.auxio.util.logE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,9 +104,7 @@ class QueueAdapter(
|
||||||
* Used since [submitList] will cause QueueAdapter to freak out.
|
* Used since [submitList] will cause QueueAdapter to freak out.
|
||||||
*/
|
*/
|
||||||
fun moveItems(adapterFrom: Int, adapterTo: Int) {
|
fun moveItems(adapterFrom: Int, adapterTo: Int) {
|
||||||
val item = data.removeAt(adapterFrom)
|
data.add(adapterTo, data.removeAt(adapterFrom))
|
||||||
data.add(adapterTo, item)
|
|
||||||
|
|
||||||
notifyItemMoved(adapterFrom, adapterTo)
|
notifyItemMoved(adapterFrom, adapterTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,30 +114,7 @@ class QueueAdapter(
|
||||||
*/
|
*/
|
||||||
fun removeItem(adapterIndex: Int) {
|
fun removeItem(adapterIndex: Int) {
|
||||||
data.removeAt(adapterIndex)
|
data.removeAt(adapterIndex)
|
||||||
|
notifyItemRemoved(adapterIndex)
|
||||||
/*
|
|
||||||
* If the data from the next queue is now entirely empty [Signified by a header at the
|
|
||||||
* end, remove the next queue header as notify as such.
|
|
||||||
*
|
|
||||||
* If the user queue is empty [Signified by there being two headers at the beginning with
|
|
||||||
* nothing in between], then remove the user queue header and notify as such.
|
|
||||||
*
|
|
||||||
* Otherwise just remove the item as usual.
|
|
||||||
*/
|
|
||||||
if (data[data.lastIndex] is Header) {
|
|
||||||
logD("Queue is empty, removing header")
|
|
||||||
|
|
||||||
val lastIndex = data.lastIndex
|
|
||||||
data.removeAt(lastIndex)
|
|
||||||
notifyItemRangeRemoved(lastIndex, 2)
|
|
||||||
} else if (data.lastIndex >= 1 && data[0] is ActionHeader && data[1] is Header) {
|
|
||||||
logD("User queue is empty, removing header")
|
|
||||||
|
|
||||||
data.removeAt(0)
|
|
||||||
notifyItemRangeRemoved(0, 2)
|
|
||||||
} else {
|
|
||||||
notifyItemRemoved(adapterIndex)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -44,14 +44,9 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
||||||
override fun getMovementFlags(
|
override fun getMovementFlags(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder
|
viewHolder: RecyclerView.ViewHolder
|
||||||
): Int {
|
): Int =
|
||||||
// Only allow dragging/swiping with the queue item ViewHolder, not the headers.
|
makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN) or
|
||||||
return if (viewHolder is QueueAdapter.QueueSongViewHolder) {
|
makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.START)
|
||||||
makeFlag(
|
|
||||||
ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
|
||||||
) or makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.START)
|
|
||||||
} else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun interpolateOutOfBoundsScroll(
|
override fun interpolateOutOfBoundsScroll(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
|
@ -152,15 +147,18 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
target: RecyclerView.ViewHolder
|
target: RecyclerView.ViewHolder
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return playbackModel.moveQueueDataItems(
|
val from = viewHolder.bindingAdapterPosition
|
||||||
viewHolder.bindingAdapterPosition,
|
val to = target.bindingAdapterPosition
|
||||||
target.bindingAdapterPosition,
|
|
||||||
queueAdapter
|
return playbackModel.moveQueueDataItems(from, to) {
|
||||||
)
|
queueAdapter.moveItems(from, to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
playbackModel.removeQueueDataItem(viewHolder.bindingAdapterPosition, queueAdapter)
|
playbackModel.removeQueueDataItem(viewHolder.bindingAdapterPosition) {
|
||||||
|
queueAdapter.removeItem(viewHolder.bindingAdapterPosition)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,8 +30,7 @@ import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Fragment] that contains both the user queue and the next queue, with the ability to
|
* A [Fragment] that shows the queue and enables editing as well.
|
||||||
* edit them as well.
|
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class QueueFragment : Fragment() {
|
class QueueFragment : Fragment() {
|
||||||
|
@ -68,7 +67,7 @@ class QueueFragment : Fragment() {
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ----
|
// --- VIEWMODEL SETUP ----
|
||||||
|
|
||||||
playbackModel.displayQueue.observe(viewLifecycleOwner) { queue ->
|
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { queue ->
|
||||||
if (queue.isEmpty()) {
|
if (queue.isEmpty()) {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
return@observe
|
return@observe
|
||||||
|
|
|
@ -44,7 +44,10 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
createTable(db, TABLE_NAME_QUEUE)
|
createTable(db, TABLE_NAME_QUEUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = nuke(db)
|
||||||
|
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = nuke(db)
|
||||||
|
|
||||||
|
private fun nuke(db: SQLiteDatabase) {
|
||||||
db.apply {
|
db.apply {
|
||||||
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_STATE")
|
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_STATE")
|
||||||
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_QUEUE")
|
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_QUEUE")
|
||||||
|
@ -53,10 +56,6 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
|
||||||
onUpgrade(db, newVersion, oldVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- DATABASE CONSTRUCTION FUNCTIONS ---
|
// --- DATABASE CONSTRUCTION FUNCTIONS ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -86,8 +85,7 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
.append("${StateColumns.COLUMN_QUEUE_INDEX} INTEGER NOT NULL,")
|
.append("${StateColumns.COLUMN_QUEUE_INDEX} INTEGER NOT NULL,")
|
||||||
.append("${StateColumns.COLUMN_PLAYBACK_MODE} INTEGER NOT NULL,")
|
.append("${StateColumns.COLUMN_PLAYBACK_MODE} INTEGER NOT NULL,")
|
||||||
.append("${StateColumns.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,")
|
.append("${StateColumns.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,")
|
||||||
.append("${StateColumns.COLUMN_LOOP_MODE} INTEGER NOT NULL,")
|
.append("${StateColumns.COLUMN_LOOP_MODE} INTEGER NOT NULL)")
|
||||||
.append("${StateColumns.COLUMN_IS_IN_USER_QUEUE} BOOLEAN NOT NULL)")
|
|
||||||
|
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
@ -98,8 +96,7 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
private fun constructQueueTable(command: StringBuilder): StringBuilder {
|
private fun constructQueueTable(command: StringBuilder): StringBuilder {
|
||||||
command.append("${QueueColumns.ID} LONG PRIMARY KEY,")
|
command.append("${QueueColumns.ID} LONG PRIMARY KEY,")
|
||||||
.append("${QueueColumns.SONG_HASH} INTEGER NOT NULL,")
|
.append("${QueueColumns.SONG_HASH} INTEGER NOT NULL,")
|
||||||
.append("${QueueColumns.ALBUM_HASH} INTEGER NOT NULL,")
|
.append("${QueueColumns.ALBUM_HASH} INTEGER NOT NULL)")
|
||||||
.append("${QueueColumns.IS_USER_QUEUE} BOOLEAN NOT NULL)")
|
|
||||||
|
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
@ -126,7 +123,6 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
|
put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
|
||||||
put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
|
put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
|
||||||
put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
|
put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
|
||||||
put(StateColumns.COLUMN_IS_IN_USER_QUEUE, state.isInUserQueue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(TABLE_NAME_STATE, null, stateData)
|
insert(TABLE_NAME_STATE, null, stateData)
|
||||||
|
@ -155,9 +151,6 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
val modeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_PLAYBACK_MODE)
|
val modeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_PLAYBACK_MODE)
|
||||||
val shuffleIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_IS_SHUFFLING)
|
val shuffleIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_IS_SHUFFLING)
|
||||||
val loopModeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_LOOP_MODE)
|
val loopModeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_LOOP_MODE)
|
||||||
val isInUserQueueIndex = cursor.getColumnIndexOrThrow(
|
|
||||||
StateColumns.COLUMN_IS_IN_USER_QUEUE
|
|
||||||
)
|
|
||||||
|
|
||||||
cursor.moveToFirst()
|
cursor.moveToFirst()
|
||||||
|
|
||||||
|
@ -184,7 +177,6 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
playbackMode = mode,
|
playbackMode = mode,
|
||||||
isShuffling = cursor.getInt(shuffleIndex) == 1,
|
isShuffling = cursor.getInt(shuffleIndex) == 1,
|
||||||
loopMode = LoopMode.fromInt(cursor.getInt(loopModeIndex)) ?: LoopMode.NONE,
|
loopMode = LoopMode.fromInt(cursor.getInt(loopModeIndex)) ?: LoopMode.NONE,
|
||||||
isInUserQueue = cursor.getInt(isInUserQueueIndex) == 1
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,9 +184,9 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a [SavedQueue] to the database.
|
* Write a queue to the database.
|
||||||
*/
|
*/
|
||||||
fun writeQueue(queue: SavedQueue) {
|
fun writeQueue(queue: MutableList<Song>) {
|
||||||
assertBackgroundThread()
|
assertBackgroundThread()
|
||||||
|
|
||||||
val database = writableDatabase
|
val database = writableDatabase
|
||||||
|
@ -205,12 +197,11 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
|
|
||||||
logD("Wiped queue db.")
|
logD("Wiped queue db.")
|
||||||
|
|
||||||
writeQueueBatch(queue.user, true, 0)
|
writeQueueBatch(queue, queue.size)
|
||||||
writeQueueBatch(queue.queue, false, queue.user.size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeQueueBatch(queue: List<Song>, isUserQueue: Boolean, idStart: Int) {
|
private fun writeQueueBatch(queue: List<Song>, idStart: Int) {
|
||||||
logD("Beginning queue write [start: $idStart, userQueue: $isUserQueue]")
|
logD("Beginning queue write [start: $idStart]")
|
||||||
|
|
||||||
val database = writableDatabase
|
val database = writableDatabase
|
||||||
var position = 0
|
var position = 0
|
||||||
|
@ -227,7 +218,6 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
put(QueueColumns.ID, idStart + i)
|
put(QueueColumns.ID, idStart + i)
|
||||||
put(QueueColumns.SONG_HASH, song.hash)
|
put(QueueColumns.SONG_HASH, song.hash)
|
||||||
put(QueueColumns.ALBUM_HASH, song.album.hash)
|
put(QueueColumns.ALBUM_HASH, song.album.hash)
|
||||||
put(QueueColumns.IS_USER_QUEUE, isUserQueue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insert(TABLE_NAME_QUEUE, null, itemData)
|
insert(TABLE_NAME_QUEUE, null, itemData)
|
||||||
|
@ -243,29 +233,25 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a [SavedQueue] from this database.
|
* Read a list of queue items from this database.
|
||||||
* @param musicStore Required to transform database songs into actual song instances
|
* @param musicStore Required to transform database songs into actual song instances
|
||||||
*/
|
*/
|
||||||
fun readQueue(musicStore: MusicStore): SavedQueue {
|
fun readQueue(musicStore: MusicStore): MutableList<Song> {
|
||||||
assertBackgroundThread()
|
assertBackgroundThread()
|
||||||
|
|
||||||
val queue = SavedQueue(mutableListOf(), mutableListOf())
|
val queue = mutableListOf<Song>()
|
||||||
|
|
||||||
readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
|
readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
|
||||||
if (cursor.count == 0) return@queryAll
|
if (cursor.count == 0) return@queryAll
|
||||||
|
|
||||||
val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_HASH)
|
val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_HASH)
|
||||||
val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_HASH)
|
val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_HASH)
|
||||||
val isUserQueueIndex = cursor.getColumnIndexOrThrow(QueueColumns.IS_USER_QUEUE)
|
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))?.let { song ->
|
musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))
|
||||||
if (cursor.getInt(isUserQueueIndex) == 1) {
|
?.let { song ->
|
||||||
queue.user.add(song)
|
queue.add(song)
|
||||||
} else {
|
|
||||||
queue.queue.add(song)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,11 +266,8 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
val playbackMode: PlaybackMode,
|
val playbackMode: PlaybackMode,
|
||||||
val isShuffling: Boolean,
|
val isShuffling: Boolean,
|
||||||
val loopMode: LoopMode,
|
val loopMode: LoopMode,
|
||||||
val isInUserQueue: Boolean
|
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SavedQueue(val user: MutableList<Song>, val queue: MutableList<Song>)
|
|
||||||
|
|
||||||
private object StateColumns {
|
private object StateColumns {
|
||||||
const val COLUMN_ID = "id"
|
const val COLUMN_ID = "id"
|
||||||
const val COLUMN_SONG_HASH = "song"
|
const val COLUMN_SONG_HASH = "song"
|
||||||
|
@ -294,19 +277,17 @@ class PlaybackStateDatabase(context: Context) :
|
||||||
const val COLUMN_PLAYBACK_MODE = "playback_mode"
|
const val COLUMN_PLAYBACK_MODE = "playback_mode"
|
||||||
const val COLUMN_IS_SHUFFLING = "is_shuffling"
|
const val COLUMN_IS_SHUFFLING = "is_shuffling"
|
||||||
const val COLUMN_LOOP_MODE = "loop_mode"
|
const val COLUMN_LOOP_MODE = "loop_mode"
|
||||||
const val COLUMN_IS_IN_USER_QUEUE = "is_in_user_queue"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private object QueueColumns {
|
private object QueueColumns {
|
||||||
const val ID = "id"
|
const val ID = "id"
|
||||||
const val SONG_HASH = "song"
|
const val SONG_HASH = "song"
|
||||||
const val ALBUM_HASH = "album"
|
const val ALBUM_HASH = "album"
|
||||||
const val IS_USER_QUEUE = "is_user_queue"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DB_NAME = "auxio_state_database.db"
|
const val DB_NAME = "auxio_state_database.db"
|
||||||
const val DB_VERSION = 5
|
const val DB_VERSION = 6
|
||||||
|
|
||||||
const val TABLE_NAME_STATE = "playback_state_table"
|
const val TABLE_NAME_STATE = "playback_state_table"
|
||||||
const val TABLE_NAME_QUEUE = "queue_table"
|
const val TABLE_NAME_QUEUE = "queue_table"
|
||||||
|
|
|
@ -30,6 +30,8 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.settings.SettingsManager
|
import org.oxycblt.auxio.settings.SettingsManager
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.logE
|
import org.oxycblt.auxio.util.logE
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Master class (and possible god object) for the playback state.
|
* Master class (and possible god object) for the playback state.
|
||||||
|
@ -65,11 +67,6 @@ class PlaybackStateManager private constructor() {
|
||||||
field = value
|
field = value
|
||||||
callbacks.forEach { it.onQueueUpdate(value) }
|
callbacks.forEach { it.onQueueUpdate(value) }
|
||||||
}
|
}
|
||||||
private var mUserQueue = mutableListOf<Song>()
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
callbacks.forEach { it.onUserQueueUpdate(value) }
|
|
||||||
}
|
|
||||||
private var mIndex = 0
|
private var mIndex = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
@ -98,11 +95,7 @@ class PlaybackStateManager private constructor() {
|
||||||
field = value
|
field = value
|
||||||
callbacks.forEach { it.onLoopUpdate(value) }
|
callbacks.forEach { it.onLoopUpdate(value) }
|
||||||
}
|
}
|
||||||
private var mIsInUserQueue = false
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
callbacks.forEach { it.onInUserQueueUpdate(value) }
|
|
||||||
}
|
|
||||||
private var mIsRestored = false
|
private var mIsRestored = false
|
||||||
private var mHasPlayed = false
|
private var mHasPlayed = false
|
||||||
|
|
||||||
|
@ -114,8 +107,6 @@ class PlaybackStateManager private constructor() {
|
||||||
val position: Long get() = mPosition
|
val position: Long get() = mPosition
|
||||||
/** The current queue determined by [parent] and [playbackMode] */
|
/** The current queue determined by [parent] and [playbackMode] */
|
||||||
val queue: List<Song> get() = mQueue
|
val queue: List<Song> get() = mQueue
|
||||||
/** The queue created by the user. */
|
|
||||||
val userQueue: List<Song> get() = mUserQueue
|
|
||||||
/** The current index of the queue */
|
/** The current index of the queue */
|
||||||
val index: Int get() = mIndex
|
val index: Int get() = mIndex
|
||||||
/** The current [PlaybackMode] */
|
/** The current [PlaybackMode] */
|
||||||
|
@ -250,8 +241,6 @@ class PlaybackStateManager private constructor() {
|
||||||
* Update the playback to a new [song], doing all the required logic.
|
* Update the playback to a new [song], doing all the required logic.
|
||||||
*/
|
*/
|
||||||
private fun updatePlayback(song: Song, shouldPlay: Boolean = true) {
|
private fun updatePlayback(song: Song, shouldPlay: Boolean = true) {
|
||||||
mIsInUserQueue = false
|
|
||||||
|
|
||||||
mSong = song
|
mSong = song
|
||||||
mPosition = 0
|
mPosition = 0
|
||||||
|
|
||||||
|
@ -264,29 +253,17 @@ class PlaybackStateManager private constructor() {
|
||||||
* Go to the next song, along with doing all the checks that entails.
|
* Go to the next song, along with doing all the checks that entails.
|
||||||
*/
|
*/
|
||||||
fun next() {
|
fun next() {
|
||||||
// If there's anything in the user queue, go to the first song in there instead
|
// Increment the index, if it cannot be incremented any further, then
|
||||||
// of incrementing the index.
|
// loop and pause/resume playback depending on the setting
|
||||||
if (mUserQueue.isNotEmpty()) {
|
if (mIndex < mQueue.lastIndex) {
|
||||||
updatePlayback(mUserQueue[0])
|
mIndex = mIndex.inc()
|
||||||
mUserQueue.removeAt(0)
|
updatePlayback(mQueue[mIndex])
|
||||||
|
|
||||||
// Mark that the playback state is currently in the user queue, for later.
|
|
||||||
mIsInUserQueue = true
|
|
||||||
|
|
||||||
forceUserQueueUpdate()
|
|
||||||
} else {
|
} else {
|
||||||
// Increment the index, if it cannot be incremented any further, then
|
mIndex = 0
|
||||||
// loop and pause/resume playback depending on the setting
|
updatePlayback(mQueue[mIndex], shouldPlay = mLoopMode == LoopMode.ALL)
|
||||||
if (mIndex < mQueue.lastIndex) {
|
|
||||||
mIndex = mIndex.inc()
|
|
||||||
updatePlayback(mQueue[mIndex])
|
|
||||||
} else {
|
|
||||||
mIndex = 0
|
|
||||||
updatePlayback(mQueue[mIndex], shouldPlay = mLoopMode == LoopMode.ALL)
|
|
||||||
}
|
|
||||||
|
|
||||||
forceQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceQueueUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,9 +274,8 @@ class PlaybackStateManager private constructor() {
|
||||||
if (settingsManager.rewindWithPrev && mPosition >= REWIND_THRESHOLD) {
|
if (settingsManager.rewindWithPrev && mPosition >= REWIND_THRESHOLD) {
|
||||||
rewind()
|
rewind()
|
||||||
} else {
|
} else {
|
||||||
// Only decrement the index if there's a song to move back to AND if we are not exiting
|
// Only decrement the index if there's a song to move back to
|
||||||
// the user queue.
|
if (mIndex > 0) {
|
||||||
if (mIndex > 0 && !mIsInUserQueue) {
|
|
||||||
mIndex = mIndex.dec()
|
mIndex = mIndex.dec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,63 +324,35 @@ class PlaybackStateManager private constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a [song] to the user queue.
|
* Add a [song] to the top of the queue.
|
||||||
*/
|
*/
|
||||||
fun addToUserQueue(song: Song) {
|
fun playNext(song: Song) {
|
||||||
mUserQueue.add(song)
|
mQueue.add(min(mIndex + 1, max(mQueue.lastIndex, 0)), song)
|
||||||
|
forceQueueUpdate()
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a list of [songs] to the user queue.
|
* Add a list of [songs] to the top of the queue.
|
||||||
*/
|
*/
|
||||||
fun addToUserQueue(songs: List<Song>) {
|
fun playNext(songs: List<Song>) {
|
||||||
mUserQueue.addAll(songs)
|
mQueue.addAll(min(mIndex + 1, max(mQueue.lastIndex, 0)), songs)
|
||||||
|
forceQueueUpdate()
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a USER queue item at [index]. Will ignore invalid indexes.
|
* Add a [song] to the end of the queue.
|
||||||
*/
|
*/
|
||||||
fun removeUserQueueItem(index: Int) {
|
fun addToQueue(song: Song) {
|
||||||
logD("Removing item ${mUserQueue[index].name}.")
|
mQueue.add(song)
|
||||||
|
forceQueueUpdate()
|
||||||
if (index > mUserQueue.size || index < 0) {
|
|
||||||
logE("Index is out of bounds, did not remove user queue item.")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mUserQueue.removeAt(index)
|
|
||||||
|
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move a USER queue item at [from] to a position at [to]. Will ignore invalid indexes.
|
* Add a list of [songs] to the end of the queue.
|
||||||
*/
|
*/
|
||||||
fun moveUserQueueItems(from: Int, to: Int) {
|
fun addToQueue(songs: List<Song>) {
|
||||||
if (from > mUserQueue.size || from < 0 || to > mUserQueue.size || to < 0) {
|
mQueue.addAll(songs)
|
||||||
logE("Indices were out of bounds, did not move user queue item")
|
forceQueueUpdate()
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val item = mUserQueue.removeAt(from)
|
|
||||||
mUserQueue.add(to, item)
|
|
||||||
|
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the user queue. Forces a user queue update.
|
|
||||||
*/
|
|
||||||
fun clearUserQueue() {
|
|
||||||
mUserQueue.clear()
|
|
||||||
|
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -414,36 +362,28 @@ class PlaybackStateManager private constructor() {
|
||||||
mQueue = mQueue
|
mQueue = mQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Force any callbacks to recieve a user queue update.
|
|
||||||
*/
|
|
||||||
private fun forceUserQueueUpdate() {
|
|
||||||
mUserQueue = mUserQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SHUFFLE FUNCTIONS ---
|
// --- SHUFFLE FUNCTIONS ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether this instance is [shuffled]. Updates the queue accordingly
|
* Set whether this instance is [shuffled]. Updates the queue accordingly.
|
||||||
* @param keepSong Whether the current song should be kept as the queue is shuffled/unshuffled
|
* @param keepSong Whether the current song should be kept as the queue is shuffled/unshuffled
|
||||||
*/
|
*/
|
||||||
fun setShuffling(shuffled: Boolean, keepSong: Boolean) {
|
fun setShuffling(shuffled: Boolean, keepSong: Boolean) {
|
||||||
mIsShuffling = shuffled
|
mIsShuffling = shuffled
|
||||||
|
|
||||||
if (mIsShuffling) {
|
if (mIsShuffling) {
|
||||||
genShuffle(keepSong, mIsInUserQueue)
|
genShuffle(keepSong)
|
||||||
} else {
|
} else {
|
||||||
resetShuffle(keepSong, mIsInUserQueue)
|
resetShuffle(keepSong)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new shuffled queue.
|
* Generate a new shuffled queue.
|
||||||
* @param keepSong Whether the current song should be kept as the queue is shuffled
|
* @param keepSong Whether the current song should be kept as the queue is shuffled
|
||||||
* @param useLastSong Whether to use the last song in the queue instead of the current one
|
|
||||||
*/
|
*/
|
||||||
private fun genShuffle(keepSong: Boolean, useLastSong: Boolean) {
|
private fun genShuffle(keepSong: Boolean) {
|
||||||
val lastSong = if (useLastSong) mQueue[0] else mSong
|
val lastSong = mSong
|
||||||
|
|
||||||
logD("Shuffling queue")
|
logD("Shuffling queue")
|
||||||
|
|
||||||
|
@ -464,11 +404,10 @@ class PlaybackStateManager private constructor() {
|
||||||
/**
|
/**
|
||||||
* Reset the queue to its normal, ordered state.
|
* Reset the queue to its normal, ordered state.
|
||||||
* @param keepSong Whether the current song should be kept as the queue is unshuffled
|
* @param keepSong Whether the current song should be kept as the queue is unshuffled
|
||||||
* @param useLastSong Whether to use the previous song for the index calculations.
|
|
||||||
*/
|
*/
|
||||||
private fun resetShuffle(keepSong: Boolean, useLastSong: Boolean) {
|
private fun resetShuffle(keepSong: Boolean) {
|
||||||
val musicStore = MusicStore.maybeGetInstance() ?: return
|
val musicStore = MusicStore.maybeGetInstance() ?: return
|
||||||
val lastSong = if (useLastSong) mQueue[mIndex] else mSong
|
val lastSong = mSong
|
||||||
|
|
||||||
mQueue = when (mPlaybackMode) {
|
mQueue = when (mPlaybackMode) {
|
||||||
PlaybackMode.ALL_SONGS ->
|
PlaybackMode.ALL_SONGS ->
|
||||||
|
@ -585,11 +524,12 @@ class PlaybackStateManager private constructor() {
|
||||||
database.writeState(
|
database.writeState(
|
||||||
PlaybackStateDatabase.SavedState(
|
PlaybackStateDatabase.SavedState(
|
||||||
mSong, mPosition, mParent, mIndex,
|
mSong, mPosition, mParent, mIndex,
|
||||||
mPlaybackMode, mIsShuffling, mLoopMode, mIsInUserQueue
|
mPlaybackMode, mIsShuffling, mLoopMode,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
database.writeQueue(PlaybackStateDatabase.SavedQueue(mUserQueue, mQueue))
|
// TODO: Re-add state saving
|
||||||
|
database.writeQueue(mQueue)
|
||||||
|
|
||||||
this@PlaybackStateManager.logD(
|
this@PlaybackStateManager.logD(
|
||||||
"Save finished in ${System.currentTimeMillis() - start}ms"
|
"Save finished in ${System.currentTimeMillis() - start}ms"
|
||||||
|
@ -608,7 +548,7 @@ class PlaybackStateManager private constructor() {
|
||||||
|
|
||||||
val start: Long
|
val start: Long
|
||||||
val playbackState: PlaybackStateDatabase.SavedState?
|
val playbackState: PlaybackStateDatabase.SavedState?
|
||||||
val queue: PlaybackStateDatabase.SavedQueue
|
val queue: MutableList<Song>
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
start = System.currentTimeMillis()
|
start = System.currentTimeMillis()
|
||||||
|
@ -622,7 +562,7 @@ class PlaybackStateManager private constructor() {
|
||||||
// Get off the IO coroutine since it will cause LiveData updates to throw an exception
|
// Get off the IO coroutine since it will cause LiveData updates to throw an exception
|
||||||
|
|
||||||
if (playbackState != null) {
|
if (playbackState != null) {
|
||||||
logD("Found playback state $playbackState with queue size ${queue.user.size + queue.queue.size}")
|
logD("Found playback state $playbackState")
|
||||||
|
|
||||||
unpackFromPlaybackState(playbackState)
|
unpackFromPlaybackState(playbackState)
|
||||||
unpackQueue(queue)
|
unpackQueue(queue)
|
||||||
|
@ -649,30 +589,21 @@ class PlaybackStateManager private constructor() {
|
||||||
mSong = playbackState.song
|
mSong = playbackState.song
|
||||||
mLoopMode = playbackState.loopMode
|
mLoopMode = playbackState.loopMode
|
||||||
mIsShuffling = playbackState.isShuffling
|
mIsShuffling = playbackState.isShuffling
|
||||||
mIsInUserQueue = playbackState.isInUserQueue
|
|
||||||
|
|
||||||
seekTo(playbackState.position)
|
seekTo(playbackState.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun unpackQueue(queue: MutableList<Song>) {
|
||||||
* Unpack a list of queue items into a queue & user queue.
|
mQueue = queue
|
||||||
*/
|
|
||||||
private fun unpackQueue(queue: PlaybackStateDatabase.SavedQueue) {
|
|
||||||
mUserQueue = queue.user
|
|
||||||
mQueue = queue.queue
|
|
||||||
|
|
||||||
// When done, get a more accurate index to prevent issues with queue songs that were saved
|
// Sanity check: Ensure that the
|
||||||
// to the db but are now deleted when the restore occurred.
|
mSong?.let { song ->
|
||||||
// Not done if in user queue because that could result in a bad index being created.
|
while (mQueue.getOrNull(mIndex) != song) {
|
||||||
if (!mIsInUserQueue) {
|
mIndex--
|
||||||
mSong?.let { song ->
|
|
||||||
val index = mQueue.indexOf(song)
|
|
||||||
mIndex = if (index != -1) index else mIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forceQueueUpdate()
|
forceQueueUpdate()
|
||||||
forceUserQueueUpdate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -702,14 +633,12 @@ class PlaybackStateManager private constructor() {
|
||||||
fun onParentUpdate(parent: MusicParent?) {}
|
fun onParentUpdate(parent: MusicParent?) {}
|
||||||
fun onPositionUpdate(position: Long) {}
|
fun onPositionUpdate(position: Long) {}
|
||||||
fun onQueueUpdate(queue: List<Song>) {}
|
fun onQueueUpdate(queue: List<Song>) {}
|
||||||
fun onUserQueueUpdate(userQueue: List<Song>) {}
|
|
||||||
fun onModeUpdate(mode: PlaybackMode) {}
|
fun onModeUpdate(mode: PlaybackMode) {}
|
||||||
fun onIndexUpdate(index: Int) {}
|
fun onIndexUpdate(index: Int) {}
|
||||||
fun onPlayingUpdate(isPlaying: Boolean) {}
|
fun onPlayingUpdate(isPlaying: Boolean) {}
|
||||||
fun onShuffleUpdate(isShuffling: Boolean) {}
|
fun onShuffleUpdate(isShuffling: Boolean) {}
|
||||||
fun onLoopUpdate(loopMode: LoopMode) {}
|
fun onLoopUpdate(loopMode: LoopMode) {}
|
||||||
fun onSeek(position: Long) {}
|
fun onSeek(position: Long) {}
|
||||||
fun onInUserQueueUpdate(isInUserQueue: Boolean) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -145,7 +145,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsManager.KEY_SHOW_COVERS, SettingsManager.KEY_QUALITY_COVERS, -> {
|
SettingsManager.KEY_SHOW_COVERS, SettingsManager.KEY_QUALITY_COVERS -> {
|
||||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||||
Coil.imageLoader(requireContext()).apply {
|
Coil.imageLoader(requireContext()).apply {
|
||||||
this.memoryCache?.clear()
|
this.memoryCache?.clear()
|
||||||
|
|
|
@ -144,15 +144,31 @@ class ActionMenu(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_queue_add -> {
|
R.id.action_play_next -> {
|
||||||
when (data) {
|
when (data) {
|
||||||
is Song -> {
|
is Song -> {
|
||||||
playbackModel.addToUserQueue(data)
|
playbackModel.playNext(data)
|
||||||
context.showToast(R.string.lbl_queue_added)
|
context.showToast(R.string.lbl_queue_added)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Album -> {
|
is Album -> {
|
||||||
playbackModel.addToUserQueue(data)
|
playbackModel.playNext(data)
|
||||||
|
context.showToast(R.string.lbl_queue_added)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_queue_add -> {
|
||||||
|
when (data) {
|
||||||
|
is Song -> {
|
||||||
|
playbackModel.addToQueue(data)
|
||||||
|
context.showToast(R.string.lbl_queue_added)
|
||||||
|
}
|
||||||
|
|
||||||
|
is Album -> {
|
||||||
|
playbackModel.addToQueue(data)
|
||||||
context.showToast(R.string.lbl_queue_added)
|
context.showToast(R.string.lbl_queue_added)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
This is part of a stopgap animation used by QueueFragment until it's fully integrated
|
|
||||||
into PlaybackLayout.
|
|
||||||
-->
|
|
||||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:duration="500"
|
|
||||||
android:fromAlpha="1"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_quint"
|
|
||||||
android:toAlpha="0" />
|
|
|
@ -6,6 +6,9 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_shuffle"
|
android:id="@+id/action_shuffle"
|
||||||
android:title="@string/lbl_shuffle" />
|
android:title="@string/lbl_shuffle" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add" />
|
android:title="@string/lbl_queue_add" />
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add"
|
android:title="@string/lbl_queue_add" />
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
</menu>
|
|
@ -1,5 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add" />
|
android:title="@string/lbl_queue_add" />
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
android:id="@+id/action_shuffle"
|
android:id="@+id/action_shuffle"
|
||||||
android:title="@string/lbl_shuffle"
|
android:title="@string/lbl_shuffle"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add"
|
android:title="@string/lbl_queue_add" />
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
</menu>
|
|
@ -1,5 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add" />
|
android:title="@string/lbl_queue_add" />
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/lbl_play_next" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_queue_add"
|
android:id="@+id/action_queue_add"
|
||||||
android:title="@string/lbl_queue_add" />
|
android:title="@string/lbl_queue_add" />
|
||||||
|
|
|
@ -30,9 +30,9 @@
|
||||||
<string name="lbl_play_genre">"Přehrát z žánru"</string>
|
<string name="lbl_play_genre">"Přehrát z žánru"</string>
|
||||||
<string name="lbl_playback">"Nyní hraje"</string>
|
<string name="lbl_playback">"Nyní hraje"</string>
|
||||||
<string name="lbl_queue">"Fronta"</string>
|
<string name="lbl_queue">"Fronta"</string>
|
||||||
|
<string name="lbl_play_next">Další skladba</string>
|
||||||
<string name="lbl_queue_add">"Přidat do fronty"</string>
|
<string name="lbl_queue_add">"Přidat do fronty"</string>
|
||||||
<string name="lbl_queue_added">"Přidáno do fronty"</string>
|
<string name="lbl_queue_added">"Přidáno do fronty"</string>
|
||||||
<string name="lbl_next_user_queue">"Další ve frontě"</string>
|
|
||||||
<string name="lbl_go_artist">"Jít na umělce"</string>
|
<string name="lbl_go_artist">"Jít na umělce"</string>
|
||||||
<string name="lbl_go_album">"Jít na album"</string>
|
<string name="lbl_go_album">"Jít na album"</string>
|
||||||
<string name="lbl_state_saved">"Stav uložen"</string>
|
<string name="lbl_state_saved">"Stav uložen"</string>
|
||||||
|
@ -100,7 +100,6 @@
|
||||||
<string name="desc_skip_prev">"Přeskočit na poslední skladbu"</string>
|
<string name="desc_skip_prev">"Přeskočit na poslední skladbu"</string>
|
||||||
<string name="desc_change_loop">"Změnit režim opakování"</string>
|
<string name="desc_change_loop">"Změnit režim opakování"</string>
|
||||||
<string name="desc_shuffle">"Zapnout nebo vypnout náhodné"</string>
|
<string name="desc_shuffle">"Zapnout nebo vypnout náhodné"</string>
|
||||||
<string name="desc_clear_user_queue">"Vymazat frontu"</string>
|
|
||||||
<string name="desc_queue_handle">"Přesunout skladbu ve frontě"</string>
|
<string name="desc_queue_handle">"Přesunout skladbu ve frontě"</string>
|
||||||
<string name="desc_clear_search">"Vymazat vyhledávání"</string>
|
<string name="desc_clear_search">"Vymazat vyhledávání"</string>
|
||||||
<string name="desc_blacklist_delete">"Vymazat vyloučený adresář"</string>
|
<string name="desc_blacklist_delete">"Vymazat vyloučený adresář"</string>
|
||||||
|
@ -136,7 +135,6 @@
|
||||||
<string name="clr_grey">"Šedá"</string>
|
<string name="clr_grey">"Šedá"</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">"Další z: %s"</string>
|
|
||||||
<string name="fmt_songs_loaded">"Načtené skladby: %d"</string>
|
<string name="fmt_songs_loaded">"Načtené skladby: %d"</string>
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="one">"%d skladba"</item>
|
<item quantity="one">"%d skladba"</item>
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
<string name="lbl_playback">Aktuelle Wiedergabe</string>
|
<string name="lbl_playback">Aktuelle Wiedergabe</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Warteschlange</string>
|
<string name="lbl_queue">Warteschlange</string>
|
||||||
|
<string name="lbl_play_next">Spiele als nächstes</string>
|
||||||
<string name="lbl_queue_add">Zur Warteschlange hinzufügen</string>
|
<string name="lbl_queue_add">Zur Warteschlange hinzufügen</string>
|
||||||
<string name="lbl_queue_added">Der Warteschlange hinzugefügt</string>
|
<string name="lbl_queue_added">Der Warteschlange hinzugefügt</string>
|
||||||
<string name="lbl_next_user_queue">Nächstes in Warteschlange</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Zum Künstler gehen</string>
|
<string name="lbl_go_artist">Zum Künstler gehen</string>
|
||||||
<string name="lbl_go_album">Zum Album gehen</string>
|
<string name="lbl_go_album">Zum Album gehen</string>
|
||||||
|
@ -106,7 +106,6 @@
|
||||||
<string name="desc_change_loop">Art der Wiederholung ändern</string>
|
<string name="desc_change_loop">Art der Wiederholung ändern</string>
|
||||||
|
|
||||||
<string name="desc_clear_search">Suchanfrage löschen</string>
|
<string name="desc_clear_search">Suchanfrage löschen</string>
|
||||||
<string name="desc_clear_user_queue">Warteschlange leeren</string>
|
|
||||||
|
|
||||||
<string name="desc_auxio_icon">Auxio-Icon</string>
|
<string name="desc_auxio_icon">Auxio-Icon</string>
|
||||||
<string name="desc_album_cover">Albumcover für %s</string>
|
<string name="desc_album_cover">Albumcover für %s</string>
|
||||||
|
@ -136,7 +135,6 @@
|
||||||
<string name="clr_grey">Grau</string>
|
<string name="clr_grey">Grau</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">Nächsten von: %s</string>
|
|
||||||
<string name="fmt_songs_loaded">Geladene Lieder: %d</string>
|
<string name="fmt_songs_loaded">Geladene Lieder: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
<string name="lbl_playback">Παίζει τώρα</string>
|
<string name="lbl_playback">Παίζει τώρα</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Ουρά αναπαραγωγής</string>
|
<string name="lbl_queue">Ουρά αναπαραγωγής</string>
|
||||||
|
<string name="lbl_play_next">Επόμενο</string>
|
||||||
<string name="lbl_queue_add">Προσθήκη στην ουρά αναπ/γής</string>
|
<string name="lbl_queue_add">Προσθήκη στην ουρά αναπ/γής</string>
|
||||||
<string name="lbl_queue_added">Προστέθηκε ένας τίτλος στην ουρά αναπαραγωγής</string>
|
<string name="lbl_queue_added">Προστέθηκε ένας τίτλος στην ουρά αναπαραγωγής</string>
|
||||||
<string name="lbl_next_user_queue">Επόμενο</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Πήγαινε στον καλλιτέχνη</string>
|
<string name="lbl_go_artist">Πήγαινε στον καλλιτέχνη</string>
|
||||||
<string name="lbl_go_album">Πήγαινε στο άλμπουμ</string>
|
<string name="lbl_go_album">Πήγαινε στο άλμπουμ</string>
|
||||||
|
@ -55,8 +55,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Αναπαραγωγή/Παύση</string>
|
<string name="desc_play_pause">Αναπαραγωγή/Παύση</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Εκκαθάριση ουράς αναπαραγωγής</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Αναζήτηση στη βιβλιοθήκη…</string>
|
<string name="hint_search_library">Αναζήτηση στη βιβλιοθήκη…</string>
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@
|
||||||
<string name="lbl_playback">Reproducción actual</string>
|
<string name="lbl_playback">Reproducción actual</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Cola</string>
|
<string name="lbl_queue">Cola</string>
|
||||||
|
<string name="lbl_play_next">Siguiente</string>
|
||||||
<string name="lbl_queue_add">Agregar a la cola</string>
|
<string name="lbl_queue_add">Agregar a la cola</string>
|
||||||
<string name="lbl_queue_added">Agregada a la cola</string>
|
<string name="lbl_queue_added">Agregada a la cola</string>
|
||||||
<string name="lbl_next_user_queue">A continuación…</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Ir al artista</string>
|
<string name="lbl_go_artist">Ir al artista</string>
|
||||||
<string name="lbl_go_album">Ir al álbum</string>
|
<string name="lbl_go_album">Ir al álbum</string>
|
||||||
|
@ -108,7 +108,6 @@
|
||||||
<string name="desc_skip_prev">Saltar a la última canción</string>
|
<string name="desc_skip_prev">Saltar a la última canción</string>
|
||||||
<string name="desc_change_loop">Cambiar el modo de repetición</string>
|
<string name="desc_change_loop">Cambiar el modo de repetición</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Limpiar cola</string>
|
|
||||||
<string name="desc_clear_search">Borrar historial de búsqueda</string>
|
<string name="desc_clear_search">Borrar historial de búsqueda</string>
|
||||||
<string name="desc_blacklist_delete">Eliminar directorio excluido</string>
|
<string name="desc_blacklist_delete">Eliminar directorio excluido</string>
|
||||||
|
|
||||||
|
@ -140,7 +139,6 @@
|
||||||
<string name="clr_grey">Gris</string>
|
<string name="clr_grey">Gris</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">Siguiente de: %s</string>
|
|
||||||
<string name="fmt_songs_loaded">Canciones encontradas: %d</string>
|
<string name="fmt_songs_loaded">Canciones encontradas: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Lecture en cours</string>
|
<string name="lbl_playback">Lecture en cours</string>
|
||||||
|
|
||||||
<string name="lbl_queue">File d\'attente</string>
|
<string name="lbl_queue">File d\'attente</string>
|
||||||
|
<string name="lbl_play_next">Jouer ensuite</string>
|
||||||
<string name="lbl_queue_add">Ajouter à la file d\'attente</string>
|
<string name="lbl_queue_add">Ajouter à la file d\'attente</string>
|
||||||
<string name="lbl_queue_added">Ajouté à la file d\'attente</string>
|
<string name="lbl_queue_added">Ajouté à la file d\'attente</string>
|
||||||
<string name="lbl_next_user_queue">Suivant</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_album">Aller à l\'album</string>
|
<string name="lbl_go_album">Aller à l\'album</string>
|
||||||
<string name="lbl_go_artist">Aller à l\'artiste</string>
|
<string name="lbl_go_artist">Aller à l\'artiste</string>
|
||||||
|
@ -62,8 +62,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Lecture/Pause</string>
|
<string name="desc_play_pause">Lecture/Pause</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Effacer la file d\'attente</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Recherche dans votre bibliothèque…</string>
|
<string name="hint_search_library">Recherche dans votre bibliothèque…</string>
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Most Játszott</string>
|
<string name="lbl_playback">Most Játszott</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Lejátszási sor</string>
|
<string name="lbl_queue">Lejátszási sor</string>
|
||||||
|
<string name="lbl_play_next">Lejátszás következőnek</string>
|
||||||
<string name="lbl_queue_add">Lejátszás sorhoz adás</string>
|
<string name="lbl_queue_add">Lejátszás sorhoz adás</string>
|
||||||
<string name="lbl_queue_added">Sorbaállítva</string>
|
<string name="lbl_queue_added">Sorbaállítva</string>
|
||||||
<string name="lbl_next_user_queue">Lejátszási sor</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Ugrás az előadóhoz</string>
|
<string name="lbl_go_artist">Ugrás az előadóhoz</string>
|
||||||
<string name="lbl_go_album">Ugrás az albumhoz</string>
|
<string name="lbl_go_album">Ugrás az albumhoz</string>
|
||||||
|
@ -64,8 +64,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Lejátszás/Szünet</string>
|
<string name="desc_play_pause">Lejátszás/Szünet</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Lejátszási sor</string>
|
|
||||||
|
|
||||||
<!-- Color Label namespace | Accent names -->
|
<!-- Color Label namespace | Accent names -->
|
||||||
<string name="clr_red">Piros</string>
|
<string name="clr_red">Piros</string>
|
||||||
<string name="clr_pink">Rózsaszínű</string>
|
<string name="clr_pink">Rózsaszínű</string>
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Sedang Diputar</string>
|
<string name="lbl_playback">Sedang Diputar</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Antrean</string>
|
<string name="lbl_queue">Antrean</string>
|
||||||
|
<string name="lbl_play_next">Putar selanjutnya</string>
|
||||||
<string name="lbl_queue_add">Tambahkan ke antrean</string>
|
<string name="lbl_queue_add">Tambahkan ke antrean</string>
|
||||||
<string name="lbl_queue_added">Ditambahkan ke antrean</string>
|
<string name="lbl_queue_added">Ditambahkan ke antrean</string>
|
||||||
<string name="lbl_next_user_queue">Berikutnya</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Pergi ke artis</string>
|
<string name="lbl_go_artist">Pergi ke artis</string>
|
||||||
<string name="lbl_go_album">Pergi ke album</string>
|
<string name="lbl_go_album">Pergi ke album</string>
|
||||||
|
@ -64,8 +64,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Putar/Jeda</string>
|
<string name="desc_play_pause">Putar/Jeda</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Kosongkan antrean</string>
|
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Schermata di riproduzione</string>
|
<string name="lbl_playback">Schermata di riproduzione</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Coda</string>
|
<string name="lbl_queue">Coda</string>
|
||||||
|
<string name="lbl_play_next">Riproduci successivo</string>
|
||||||
<string name="lbl_queue_add">Aggiungi alla coda</string>
|
<string name="lbl_queue_add">Aggiungi alla coda</string>
|
||||||
<string name="lbl_queue_added">Aggiunta alla coda</string>
|
<string name="lbl_queue_added">Aggiunta alla coda</string>
|
||||||
<string name="lbl_next_user_queue">A seguire</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Vai all\'artista</string>
|
<string name="lbl_go_artist">Vai all\'artista</string>
|
||||||
<string name="lbl_go_album">Vai all\'album</string>
|
<string name="lbl_go_album">Vai all\'album</string>
|
||||||
|
@ -63,8 +63,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Play/Pausa</string>
|
<string name="desc_play_pause">Play/Pausa</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Svuota coda</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Cerca nella tua libreria…</string>
|
<string name="hint_search_library">Cerca nella tua libreria…</string>
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">지금 재생 중</string>
|
<string name="lbl_playback">지금 재생 중</string>
|
||||||
|
|
||||||
<string name="lbl_queue">대기열</string>
|
<string name="lbl_queue">대기열</string>
|
||||||
|
<string name="lbl_play_next">다음 곡으로 재생</string>
|
||||||
<string name="lbl_queue_add">대기열에 추가</string>
|
<string name="lbl_queue_add">대기열에 추가</string>
|
||||||
<string name="lbl_queue_added">가 대기열에 추가되었습니다</string>
|
<string name="lbl_queue_added">가 대기열에 추가되었습니다</string>
|
||||||
<string name="lbl_next_user_queue">다음 곡</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">아티스트로 가기</string>
|
<string name="lbl_go_artist">아티스트로 가기</string>
|
||||||
<string name="lbl_go_album">앨범으로 가기</string>
|
<string name="lbl_go_album">앨범으로 가기</string>
|
||||||
|
@ -60,8 +60,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">재생/일시 정지</string>
|
<string name="desc_play_pause">재생/일시 정지</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">재생 대기열 비우기</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">저장소 검색…</string>
|
<string name="hint_search_library">저장소 검색…</string>
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@
|
||||||
<string name="lbl_playback">Afspeelscherm</string>
|
<string name="lbl_playback">Afspeelscherm</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Wachtrij</string>
|
<string name="lbl_queue">Wachtrij</string>
|
||||||
|
<string name="lbl_play_next">Afspelen als volgende</string>
|
||||||
<string name="lbl_queue_add">Toevoegen aan wachtrij</string>
|
<string name="lbl_queue_add">Toevoegen aan wachtrij</string>
|
||||||
<string name="lbl_queue_added">Toegevoegd aan de wachtrij</string>
|
<string name="lbl_queue_added">Toegevoegd aan de wachtrij</string>
|
||||||
<string name="lbl_next_user_queue">Als volgende</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Ga naar artiest</string>
|
<string name="lbl_go_artist">Ga naar artiest</string>
|
||||||
<string name="lbl_go_album">Ga naar album</string>
|
<string name="lbl_go_album">Ga naar album</string>
|
||||||
|
@ -106,7 +106,6 @@
|
||||||
<string name="desc_skip_prev">Naar het laatste nummer gaan</string>
|
<string name="desc_skip_prev">Naar het laatste nummer gaan</string>
|
||||||
<string name="desc_change_loop">Herhaalfunctie wijzigen</string>
|
<string name="desc_change_loop">Herhaalfunctie wijzigen</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Wachtrij wissen</string>
|
|
||||||
<string name="desc_clear_search">Zoekopdracht wissen</string>
|
<string name="desc_clear_search">Zoekopdracht wissen</string>
|
||||||
<string name="desc_blacklist_delete">Verwijder uitgesloten map</string>
|
<string name="desc_blacklist_delete">Verwijder uitgesloten map</string>
|
||||||
|
|
||||||
|
@ -138,7 +137,6 @@
|
||||||
<string name="clr_grey">Grijis</string>
|
<string name="clr_grey">Grijis</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">Volgende van: %s</string>
|
|
||||||
<string name="fmt_songs_loaded">Nummers geladen: %d</string>
|
<string name="fmt_songs_loaded">Nummers geladen: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Obecnie Grane</string>
|
<string name="lbl_playback">Obecnie Grane</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Kolejka</string>
|
<string name="lbl_queue">Kolejka</string>
|
||||||
|
<string name="lbl_play_next">Odtwarzaj następny</string>
|
||||||
<string name="lbl_queue_add">Dodaj do kolejki</string>
|
<string name="lbl_queue_add">Dodaj do kolejki</string>
|
||||||
<string name="lbl_queue_added">Dodany do kolejki</string>
|
<string name="lbl_queue_added">Dodany do kolejki</string>
|
||||||
<string name="lbl_next_user_queue">Za chwilę zagra</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Przejdź do wykonawcy</string>
|
<string name="lbl_go_artist">Przejdź do wykonawcy</string>
|
||||||
<string name="lbl_go_album">Przejdź do albumu</string>
|
<string name="lbl_go_album">Przejdź do albumu</string>
|
||||||
|
@ -61,8 +61,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Odtwarzanie/Pauza</string>
|
<string name="desc_play_pause">Odtwarzanie/Pauza</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Wyczyść kolejkę</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Szukaj w bibliotece…</string>
|
<string name="hint_search_library">Szukaj w bibliotece…</string>
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
<string name="lbl_playback">Tocando agora</string>
|
<string name="lbl_playback">Tocando agora</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Fila</string>
|
<string name="lbl_queue">Fila</string>
|
||||||
|
<string name="lbl_play_next">Reproduzir próxima</string>
|
||||||
<string name="lbl_queue_add">Adicionar à fila</string>
|
<string name="lbl_queue_add">Adicionar à fila</string>
|
||||||
<string name="lbl_queue_added">Adicionada à fila</string>
|
<string name="lbl_queue_added">Adicionada à fila</string>
|
||||||
<string name="lbl_next_user_queue">Próximo</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Ir para o artista</string>
|
<string name="lbl_go_artist">Ir para o artista</string>
|
||||||
<string name="lbl_go_album">Ir para o álbum</string>
|
<string name="lbl_go_album">Ir para o álbum</string>
|
||||||
|
@ -62,8 +62,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Limpar fila</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Procurar na biblioteca…</string>
|
<string name="hint_search_library">Procurar na biblioteca…</string>
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">A reproduzir</string>
|
<string name="lbl_playback">A reproduzir</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Fila</string>
|
<string name="lbl_queue">Fila</string>
|
||||||
|
<string name="lbl_play_next">Reproduzir a próxima</string>
|
||||||
<string name="lbl_queue_add">Adicionar à fila</string>
|
<string name="lbl_queue_add">Adicionar à fila</string>
|
||||||
<string name="lbl_queue_added">Adicionada à fila</string>
|
<string name="lbl_queue_added">Adicionada à fila</string>
|
||||||
<string name="lbl_next_user_queue">A seguir</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Ir para o artista</string>
|
<string name="lbl_go_artist">Ir para o artista</string>
|
||||||
<string name="lbl_go_album">Ir para o álbum</string>
|
<string name="lbl_go_album">Ir para o álbum</string>
|
||||||
|
@ -63,8 +63,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Limpar fila</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">Procurar na biblioteca…</string>
|
<string name="hint_search_library">Procurar na biblioteca…</string>
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Redare Acum</string>
|
<string name="lbl_playback">Redare Acum</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Fila de așteptare</string>
|
<string name="lbl_queue">Fila de așteptare</string>
|
||||||
|
<string name="lbl_play_next">Redaţi următoarea</string>
|
||||||
<string name="lbl_queue_add">Adăugați la lista de așteptare</string>
|
<string name="lbl_queue_add">Adăugați la lista de așteptare</string>
|
||||||
<string name="lbl_queue_added">A fost adăugat la liste de așteptare</string>
|
<string name="lbl_queue_added">A fost adăugat la liste de așteptare</string>
|
||||||
<string name="lbl_next_user_queue">Urmează</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Accesaţi artistul</string>
|
<string name="lbl_go_artist">Accesaţi artistul</string>
|
||||||
<string name="lbl_go_album">Accesaţi albumul</string>
|
<string name="lbl_go_album">Accesaţi albumul</string>
|
||||||
|
@ -65,8 +65,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Redă/Pauză</string>
|
<string name="desc_play_pause">Redă/Pauză</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Golește lista de redare</string>
|
|
||||||
|
|
||||||
<!-- Color Label namespace | Accent names -->
|
<!-- Color Label namespace | Accent names -->
|
||||||
<string name="clr_red">Roșu</string>
|
<string name="clr_red">Roșu</string>
|
||||||
<string name="clr_pink">Roz</string>
|
<string name="clr_pink">Roz</string>
|
||||||
|
|
|
@ -36,9 +36,9 @@
|
||||||
<string name="lbl_play_genre">Воспроизвести из жанра</string>
|
<string name="lbl_play_genre">Воспроизвести из жанра</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Очередь</string>
|
<string name="lbl_queue">Очередь</string>
|
||||||
|
<string name="lbl_play_next">Воспроизвести далее</string>
|
||||||
<string name="lbl_queue_add">Добавить в очередь</string>
|
<string name="lbl_queue_add">Добавить в очередь</string>
|
||||||
<string name="lbl_queue_added">Добавлено в очередь</string>
|
<string name="lbl_queue_added">Добавлено в очередь</string>
|
||||||
<string name="lbl_next_user_queue">Далее в очереди</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Перейти к исполнителю</string>
|
<string name="lbl_go_artist">Перейти к исполнителю</string>
|
||||||
<string name="lbl_go_album">Перейти к альбому</string>
|
<string name="lbl_go_album">Перейти к альбому</string>
|
||||||
|
@ -121,7 +121,6 @@
|
||||||
<string name="desc_shuffle">Включить или выключить перемешивание</string>
|
<string name="desc_shuffle">Включить или выключить перемешивание</string>
|
||||||
<string name="desc_shuffle_all">Перемешать все композиции</string>
|
<string name="desc_shuffle_all">Перемешать все композиции</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Очистить очередь</string>
|
|
||||||
<string name="desc_clear_queue_item">Удалить композицию из очереди</string>
|
<string name="desc_clear_queue_item">Удалить композицию из очереди</string>
|
||||||
<string name="desc_queue_handle">Переместить композицию в очереди</string>
|
<string name="desc_queue_handle">Переместить композицию в очереди</string>
|
||||||
<string name="desc_tab_handle">Переместить вкладку</string>
|
<string name="desc_tab_handle">Переместить вкладку</string>
|
||||||
|
@ -161,7 +160,6 @@
|
||||||
<string name="clr_grey">Серый</string>
|
<string name="clr_grey">Серый</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">Далее из: %s</string>
|
|
||||||
<string name="fmt_songs_loaded">Всего композиций: %d</string>
|
<string name="fmt_songs_loaded">Всего композиций: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">Şuan çalınan</string>
|
<string name="lbl_playback">Şuan çalınan</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Kuyruk</string>
|
<string name="lbl_queue">Kuyruk</string>
|
||||||
|
<string name="lbl_play_next">Sonraki şarkı</string>
|
||||||
<string name="lbl_queue_add">Kuyruğa ekle</string>
|
<string name="lbl_queue_add">Kuyruğa ekle</string>
|
||||||
<string name="lbl_queue_added">Kuyruğa eklendi</string>
|
<string name="lbl_queue_added">Kuyruğa eklendi</string>
|
||||||
<string name="lbl_next_user_queue">Sırada olanlar</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Sanatçıya git</string>
|
<string name="lbl_go_artist">Sanatçıya git</string>
|
||||||
<string name="lbl_go_album">Albüme git</string>
|
<string name="lbl_go_album">Albüme git</string>
|
||||||
|
@ -63,8 +63,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Başlat/Durdur</string>
|
<string name="desc_play_pause">Başlat/Durdur</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Temizle Kuyruk</string>
|
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
<string name="lbl_playback">Відтворюється</string>
|
<string name="lbl_playback">Відтворюється</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Черга</string>
|
<string name="lbl_queue">Черга</string>
|
||||||
|
<string name="lbl_play_next">Відтворити наступною</string>
|
||||||
<string name="lbl_queue_add">Додати в чергу</string>
|
<string name="lbl_queue_add">Додати в чергу</string>
|
||||||
<string name="lbl_queue_added">Додана в чергу</string>
|
<string name="lbl_queue_added">Додана в чергу</string>
|
||||||
<string name="lbl_next_user_queue">До наступного</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Перейти до виконавця</string>
|
<string name="lbl_go_artist">Перейти до виконавця</string>
|
||||||
<string name="lbl_go_album">Перейти до альбому</string>
|
<string name="lbl_go_album">Перейти до альбому</string>
|
||||||
|
@ -59,8 +59,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">Відтворити/Зупинити</string>
|
<string name="desc_play_pause">Відтворити/Зупинити</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Очистити черга</string>
|
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_songs_loaded">Пісні завантажено: %d</string>
|
<string name="fmt_songs_loaded">Пісні завантажено: %d</string>
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">正在播放界面</string>
|
<string name="lbl_playback">正在播放界面</string>
|
||||||
|
|
||||||
<string name="lbl_queue">播放队列</string>
|
<string name="lbl_queue">播放队列</string>
|
||||||
|
<string name="lbl_play_next">作为下一首播放</string>
|
||||||
<string name="lbl_queue_add">加入播放队列</string>
|
<string name="lbl_queue_add">加入播放队列</string>
|
||||||
<string name="lbl_queue_added">此歌曲已加入播放队列</string>
|
<string name="lbl_queue_added">此歌曲已加入播放队列</string>
|
||||||
<string name="lbl_next_user_queue">即将播放</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">查看艺术家</string>
|
<string name="lbl_go_artist">查看艺术家</string>
|
||||||
<string name="lbl_go_album">查看专辑</string>
|
<string name="lbl_go_album">查看专辑</string>
|
||||||
|
@ -56,8 +56,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">播放/暂停</string>
|
<string name="desc_play_pause">播放/暂停</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">清空播放队列</string>
|
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
<item quantity="other">%d 歌曲</item>
|
<item quantity="other">%d 歌曲</item>
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
<string name="lbl_playback">播放面板</string>
|
<string name="lbl_playback">播放面板</string>
|
||||||
|
|
||||||
<string name="lbl_queue">隊列</string>
|
<string name="lbl_queue">隊列</string>
|
||||||
|
<string name="lbl_play_next">下一首播放</string>
|
||||||
<string name="lbl_queue_add">添加到隊列</string>
|
<string name="lbl_queue_add">添加到隊列</string>
|
||||||
<string name="lbl_queue_added">已加入隊列</string>
|
<string name="lbl_queue_added">已加入隊列</string>
|
||||||
<string name="lbl_next_user_queue">播放佇列:即將播放</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">前往該歌手頁面</string>
|
<string name="lbl_go_artist">前往該歌手頁面</string>
|
||||||
<string name="lbl_go_album">專輯</string>
|
<string name="lbl_go_album">專輯</string>
|
||||||
|
@ -61,8 +61,6 @@
|
||||||
|
|
||||||
<string name="desc_play_pause">播放/暫停</string>
|
<string name="desc_play_pause">播放/暫停</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">清空播放佇列</string>
|
|
||||||
|
|
||||||
<!-- Hint Namespace | EditText Hints -->
|
<!-- Hint Namespace | EditText Hints -->
|
||||||
<string name="hint_search_library">搜尋音樂庫…</string>
|
<string name="hint_search_library">搜尋音樂庫…</string>
|
||||||
|
|
||||||
|
|
|
@ -6,26 +6,6 @@
|
||||||
<attr name="entryValues" format="reference" />
|
<attr name="entryValues" format="reference" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="SlidingUpPanelLayout">
|
|
||||||
<attr name="umanoPanelHeight" format="dimension" />
|
|
||||||
<attr name="umanoShadowHeight" format="dimension" />
|
|
||||||
<attr name="umanoParallaxOffset" format="dimension" />
|
|
||||||
<attr name="umanoFadeColor" format="color" />
|
|
||||||
<attr name="umanoFlingVelocity" format="integer" />
|
|
||||||
<attr name="umanoDragView" format="reference" />
|
|
||||||
<attr name="umanoScrollableView" format="reference" />
|
|
||||||
<attr name="umanoOverlay" format="boolean"/>
|
|
||||||
<attr name="umanoClipPanel" format="boolean"/>
|
|
||||||
<attr name="umanoAnchorPoint" format="float" />
|
|
||||||
<attr name="umanoInitialState" format="enum">
|
|
||||||
<enum name="expanded" value="0" />
|
|
||||||
<enum name="collapsed" value="1" />
|
|
||||||
<enum name="anchored" value="2" />
|
|
||||||
<enum name="hidden" value="3" />
|
|
||||||
</attr>
|
|
||||||
<attr name="umanoScrollInterpolator" format="reference" />
|
|
||||||
</declare-styleable>
|
|
||||||
|
|
||||||
<string-array name="entires_theme">
|
<string-array name="entires_theme">
|
||||||
<item>@string/set_theme_auto</item>
|
<item>@string/set_theme_auto</item>
|
||||||
<item>@string/set_theme_day</item>
|
<item>@string/set_theme_day</item>
|
||||||
|
|
|
@ -36,9 +36,9 @@
|
||||||
<string name="lbl_play_genre">Play from genre</string>
|
<string name="lbl_play_genre">Play from genre</string>
|
||||||
|
|
||||||
<string name="lbl_queue">Queue</string>
|
<string name="lbl_queue">Queue</string>
|
||||||
|
<string name="lbl_play_next">Play next</string>
|
||||||
<string name="lbl_queue_add">Add to queue</string>
|
<string name="lbl_queue_add">Add to queue</string>
|
||||||
<string name="lbl_queue_added">Added to queue</string>
|
<string name="lbl_queue_added">Added to queue</string>
|
||||||
<string name="lbl_next_user_queue">Next in Queue</string>
|
|
||||||
|
|
||||||
<string name="lbl_go_artist">Go to artist</string>
|
<string name="lbl_go_artist">Go to artist</string>
|
||||||
<string name="lbl_go_album">Go to album</string>
|
<string name="lbl_go_album">Go to album</string>
|
||||||
|
@ -122,7 +122,6 @@
|
||||||
<string name="desc_shuffle">Turn shuffle on or off</string>
|
<string name="desc_shuffle">Turn shuffle on or off</string>
|
||||||
<string name="desc_shuffle_all">Shuffle all songs</string>
|
<string name="desc_shuffle_all">Shuffle all songs</string>
|
||||||
|
|
||||||
<string name="desc_clear_user_queue">Clear queue</string>
|
|
||||||
<string name="desc_clear_queue_item">Remove this queue song</string>
|
<string name="desc_clear_queue_item">Remove this queue song</string>
|
||||||
<string name="desc_queue_handle">Move this queue song</string>
|
<string name="desc_queue_handle">Move this queue song</string>
|
||||||
<string name="desc_tab_handle">Move this tab</string>
|
<string name="desc_tab_handle">Move this tab</string>
|
||||||
|
@ -162,7 +161,6 @@
|
||||||
<string name="clr_grey">Grey</string>
|
<string name="clr_grey">Grey</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
<string name="fmt_next_from">Next From: %s</string>
|
|
||||||
<string name="fmt_songs_loaded">Songs loaded: %d</string>
|
<string name="fmt_songs_loaded">Songs loaded: %d</string>
|
||||||
|
|
||||||
<plurals name="fmt_song_count">
|
<plurals name="fmt_song_count">
|
||||||
|
|
|
@ -171,11 +171,6 @@
|
||||||
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.Auxio.FloatingActionButton.PlayPause</item>
|
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.Auxio.FloatingActionButton.PlayPause</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ThemeOverlay.Auxio.FloatingActionButton.PlayPause" parent="">
|
|
||||||
<item name="colorContainer">?attr/colorSecondary</item>
|
|
||||||
<item name="colorOnContainer">?attr/colorOnSecondary</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="ShapeAppearance.Auxio.FloatingActionButton.PlayPause" parent="">
|
<style name="ShapeAppearance.Auxio.FloatingActionButton.PlayPause" parent="">
|
||||||
<item name="cornerSize">50%</item>
|
<item name="cornerSize">50%</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -27,4 +27,3 @@ Feel free to fork Auxio to add your own feature set however.
|
||||||
- Recently added list [#18]
|
- Recently added list [#18]
|
||||||
- Lyrics [#19]
|
- Lyrics [#19]
|
||||||
- Tag editing [#33]
|
- Tag editing [#33]
|
||||||
- Specialized queue adding (ex. Play Next) [#44]
|
|
Loading…
Reference in a new issue