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.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
|
|
|
@ -66,7 +66,7 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
|
||||
setupToolbar(detailModel.curAlbum.value!!, R.menu.menu_album_detail) { itemId ->
|
||||
if (itemId == R.id.action_queue_add) {
|
||||
playbackModel.addToUserQueue(detailModel.curAlbum.value!!)
|
||||
playbackModel.playNext(detailModel.curAlbum.value!!)
|
||||
requireContext().showToast(R.string.lbl_queue_added)
|
||||
true
|
||||
} else {
|
||||
|
@ -147,12 +147,6 @@ class AlbumDetailFragment : DetailFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
playbackModel.isInUserQueue.observe(viewLifecycleOwner) { inUserQueue ->
|
||||
if (inUserQueue) {
|
||||
detailAdapter.highlightSong(null, binding.detailRecycler)
|
||||
}
|
||||
}
|
||||
|
||||
logD("Fragment created.")
|
||||
|
||||
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.")
|
||||
|
||||
return binding.root
|
||||
|
|
|
@ -15,6 +15,7 @@ class AdaptiveFloatingActionButton @JvmOverloads constructor(
|
|||
size = SIZE_NORMAL
|
||||
|
||||
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)
|
||||
setMaxImageSize(
|
||||
resources.getDimensionPixelSize(MaterialR.dimen.m3_large_fab_max_image_size)
|
||||
|
|
|
@ -131,8 +131,10 @@ class PlaybackFragment : Fragment() {
|
|||
binding.playbackSeekBar.setProgress(pos)
|
||||
}
|
||||
|
||||
playbackModel.displayQueue.observe(viewLifecycleOwner) {
|
||||
updateQueueIcon(queueItem)
|
||||
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
||||
// 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 ->
|
||||
|
@ -155,13 +157,4 @@ class PlaybackFragment : Fragment() {
|
|||
// so we can't really do much
|
||||
(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.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
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.MusicStore
|
||||
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.PlaybackMode
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
|
@ -61,7 +54,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
|
||||
// Queue
|
||||
private val mQueue = MutableLiveData(listOf<Song>())
|
||||
private val mUserQueue = MutableLiveData(listOf<Song>())
|
||||
private val mIndex = MutableLiveData(0)
|
||||
private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS)
|
||||
|
||||
|
@ -69,7 +61,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
private val mIsPlaying = MutableLiveData(false)
|
||||
private val mIsShuffling = MutableLiveData(false)
|
||||
private val mLoopMode = MutableLiveData(LoopMode.NONE)
|
||||
private val mIsInUserQueue = MutableLiveData(false)
|
||||
|
||||
// Other
|
||||
private var mIntentUri: Uri? = null
|
||||
|
@ -83,12 +74,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
|
||||
/** The current queue determined by [playbackMode] and [parent] */
|
||||
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 */
|
||||
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 isShuffling: LiveData<Boolean> get() = mIsShuffling
|
||||
|
@ -100,62 +87,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
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 settingsManager = SettingsManager.getInstance()
|
||||
|
||||
|
@ -285,95 +216,65 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove an item at [adapterIndex], being a non-header data index.
|
||||
* @param queueAdapter [QueueAdapter] instance to push changes to when successful.
|
||||
* Remove a queue item using it's recyclerview adapter index. If the indices are valid,
|
||||
* [apply] is called just before the change is committed so that the adapter can be updated.
|
||||
*/
|
||||
fun removeQueueDataItem(adapterIndex: Int, queueAdapter: QueueAdapter) {
|
||||
var index = adapterIndex.dec()
|
||||
fun removeQueueDataItem(adapterIndex: Int, apply: () -> Unit) {
|
||||
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 (index < mUserQueue.value!!.size) {
|
||||
queueAdapter.removeItem(adapterIndex)
|
||||
|
||||
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)
|
||||
if (adjusted in mQueue.value!!.indices) {
|
||||
apply()
|
||||
playbackManager.removeQueueItem(adjusted)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a queue OR user queue item from [adapterFrom] to [adapterTo], as long as both
|
||||
* indices are non-header data indices.
|
||||
* @param queueAdapter [QueueAdapter] instance to push changes to when successful.
|
||||
* Move queue items using their recyclerview adapter indices. If the indices are valid,
|
||||
* [apply] is called just before the change is committed so that the adapter can be updated.
|
||||
*/
|
||||
fun moveQueueDataItems(
|
||||
adapterFrom: Int,
|
||||
adapterTo: Int,
|
||||
queueAdapter: QueueAdapter
|
||||
): Boolean {
|
||||
var from = adapterFrom.dec()
|
||||
var to = adapterTo.dec()
|
||||
fun moveQueueDataItems(adapterFrom: Int, adapterTo: Int, apply: () -> Unit): Boolean {
|
||||
val delta = (mQueue.value!!.size - nextItemsInQueue.value!!.size)
|
||||
|
||||
if (from < mUserQueue.value!!.size) {
|
||||
// Ignore invalid movements to out of bounds, header, or queue positions
|
||||
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)
|
||||
val from = adapterFrom + delta
|
||||
val to = adapterTo + delta
|
||||
|
||||
if (from in mQueue.value!!.indices && to in mQueue.value!!.indices) {
|
||||
apply()
|
||||
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) {
|
||||
playbackManager.addToUserQueue(song)
|
||||
fun playNext(song: Song) {
|
||||
playbackManager.playNext(song)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an [Album] to the user queue
|
||||
* Add an [Album] to the top of the queue.
|
||||
*/
|
||||
fun addToUserQueue(album: Album) {
|
||||
playbackManager.addToUserQueue(settingsManager.detailAlbumSort.sortAlbum(album))
|
||||
fun playNext(album: 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
|
||||
|
@ -405,7 +306,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
fun savePlaybackState(context: Context, onDone: () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
playbackManager.saveStateToDatabase(context)
|
||||
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +345,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
mParent.value = playbackManager.parent
|
||||
mQueue.value = playbackManager.queue
|
||||
mMode.value = playbackManager.playbackMode
|
||||
mUserQueue.value = playbackManager.userQueue
|
||||
mIndex.value = playbackManager.index
|
||||
mIsPlaying.value = playbackManager.isPlaying
|
||||
mIsShuffling.value = playbackManager.isShuffling
|
||||
|
@ -474,10 +373,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
mQueue.value = queue
|
||||
}
|
||||
|
||||
override fun onUserQueueUpdate(userQueue: List<Song>) {
|
||||
mUserQueue.value = userQueue
|
||||
}
|
||||
|
||||
override fun onIndexUpdate(index: Int) {
|
||||
mIndex.value = index
|
||||
}
|
||||
|
@ -497,8 +392,4 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
override fun onLoopUpdate(loopMode: 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.HeaderViewHolder
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
||||
/**
|
||||
|
@ -105,9 +104,7 @@ class QueueAdapter(
|
|||
* Used since [submitList] will cause QueueAdapter to freak out.
|
||||
*/
|
||||
fun moveItems(adapterFrom: Int, adapterTo: Int) {
|
||||
val item = data.removeAt(adapterFrom)
|
||||
data.add(adapterTo, item)
|
||||
|
||||
data.add(adapterTo, data.removeAt(adapterFrom))
|
||||
notifyItemMoved(adapterFrom, adapterTo)
|
||||
}
|
||||
|
||||
|
@ -117,30 +114,7 @@ class QueueAdapter(
|
|||
*/
|
||||
fun removeItem(adapterIndex: Int) {
|
||||
data.removeAt(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)
|
||||
}
|
||||
notifyItemRemoved(adapterIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -44,14 +44,9 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
|||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int {
|
||||
// Only allow dragging/swiping with the queue item ViewHolder, not the headers.
|
||||
return if (viewHolder is QueueAdapter.QueueSongViewHolder) {
|
||||
makeFlag(
|
||||
ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||
) or makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.START)
|
||||
} else 0
|
||||
}
|
||||
): Int =
|
||||
makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN) or
|
||||
makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE, ItemTouchHelper.START)
|
||||
|
||||
override fun interpolateOutOfBoundsScroll(
|
||||
recyclerView: RecyclerView,
|
||||
|
@ -152,15 +147,18 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
|||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return playbackModel.moveQueueDataItems(
|
||||
viewHolder.bindingAdapterPosition,
|
||||
target.bindingAdapterPosition,
|
||||
queueAdapter
|
||||
)
|
||||
val from = viewHolder.bindingAdapterPosition
|
||||
val to = target.bindingAdapterPosition
|
||||
|
||||
return playbackModel.moveQueueDataItems(from, to) {
|
||||
queueAdapter.moveItems(from, to)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* A [Fragment] that contains both the user queue and the next queue, with the ability to
|
||||
* edit them as well.
|
||||
* A [Fragment] that shows the queue and enables editing as well.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueFragment : Fragment() {
|
||||
|
@ -68,7 +67,7 @@ class QueueFragment : Fragment() {
|
|||
|
||||
// --- VIEWMODEL SETUP ----
|
||||
|
||||
playbackModel.displayQueue.observe(viewLifecycleOwner) { queue ->
|
||||
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { queue ->
|
||||
if (queue.isEmpty()) {
|
||||
findNavController().navigateUp()
|
||||
return@observe
|
||||
|
|
|
@ -44,7 +44,10 @@ class PlaybackStateDatabase(context: Context) :
|
|||
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 {
|
||||
execSQL("DROP TABLE IF EXISTS $TABLE_NAME_STATE")
|
||||
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 ---
|
||||
|
||||
/**
|
||||
|
@ -86,8 +85,7 @@ class PlaybackStateDatabase(context: Context) :
|
|||
.append("${StateColumns.COLUMN_QUEUE_INDEX} INTEGER NOT NULL,")
|
||||
.append("${StateColumns.COLUMN_PLAYBACK_MODE} INTEGER NOT NULL,")
|
||||
.append("${StateColumns.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,")
|
||||
.append("${StateColumns.COLUMN_LOOP_MODE} INTEGER NOT NULL,")
|
||||
.append("${StateColumns.COLUMN_IS_IN_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
.append("${StateColumns.COLUMN_LOOP_MODE} INTEGER NOT NULL)")
|
||||
|
||||
return command
|
||||
}
|
||||
|
@ -98,8 +96,7 @@ class PlaybackStateDatabase(context: Context) :
|
|||
private fun constructQueueTable(command: StringBuilder): StringBuilder {
|
||||
command.append("${QueueColumns.ID} LONG PRIMARY KEY,")
|
||||
.append("${QueueColumns.SONG_HASH} INTEGER NOT NULL,")
|
||||
.append("${QueueColumns.ALBUM_HASH} INTEGER NOT NULL,")
|
||||
.append("${QueueColumns.IS_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
.append("${QueueColumns.ALBUM_HASH} INTEGER NOT NULL)")
|
||||
|
||||
return command
|
||||
}
|
||||
|
@ -126,7 +123,6 @@ class PlaybackStateDatabase(context: Context) :
|
|||
put(StateColumns.COLUMN_PLAYBACK_MODE, state.playbackMode.toInt())
|
||||
put(StateColumns.COLUMN_IS_SHUFFLING, state.isShuffling)
|
||||
put(StateColumns.COLUMN_LOOP_MODE, state.loopMode.toInt())
|
||||
put(StateColumns.COLUMN_IS_IN_USER_QUEUE, state.isInUserQueue)
|
||||
}
|
||||
|
||||
insert(TABLE_NAME_STATE, null, stateData)
|
||||
|
@ -155,9 +151,6 @@ class PlaybackStateDatabase(context: Context) :
|
|||
val modeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_PLAYBACK_MODE)
|
||||
val shuffleIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_IS_SHUFFLING)
|
||||
val loopModeIndex = cursor.getColumnIndexOrThrow(StateColumns.COLUMN_LOOP_MODE)
|
||||
val isInUserQueueIndex = cursor.getColumnIndexOrThrow(
|
||||
StateColumns.COLUMN_IS_IN_USER_QUEUE
|
||||
)
|
||||
|
||||
cursor.moveToFirst()
|
||||
|
||||
|
@ -184,7 +177,6 @@ class PlaybackStateDatabase(context: Context) :
|
|||
playbackMode = mode,
|
||||
isShuffling = cursor.getInt(shuffleIndex) == 1,
|
||||
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()
|
||||
|
||||
val database = writableDatabase
|
||||
|
@ -205,12 +197,11 @@ class PlaybackStateDatabase(context: Context) :
|
|||
|
||||
logD("Wiped queue db.")
|
||||
|
||||
writeQueueBatch(queue.user, true, 0)
|
||||
writeQueueBatch(queue.queue, false, queue.user.size)
|
||||
writeQueueBatch(queue, queue.size)
|
||||
}
|
||||
|
||||
private fun writeQueueBatch(queue: List<Song>, isUserQueue: Boolean, idStart: Int) {
|
||||
logD("Beginning queue write [start: $idStart, userQueue: $isUserQueue]")
|
||||
private fun writeQueueBatch(queue: List<Song>, idStart: Int) {
|
||||
logD("Beginning queue write [start: $idStart]")
|
||||
|
||||
val database = writableDatabase
|
||||
var position = 0
|
||||
|
@ -227,7 +218,6 @@ class PlaybackStateDatabase(context: Context) :
|
|||
put(QueueColumns.ID, idStart + i)
|
||||
put(QueueColumns.SONG_HASH, song.hash)
|
||||
put(QueueColumns.ALBUM_HASH, song.album.hash)
|
||||
put(QueueColumns.IS_USER_QUEUE, isUserQueue)
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
fun readQueue(musicStore: MusicStore): SavedQueue {
|
||||
fun readQueue(musicStore: MusicStore): MutableList<Song> {
|
||||
assertBackgroundThread()
|
||||
|
||||
val queue = SavedQueue(mutableListOf(), mutableListOf())
|
||||
val queue = mutableListOf<Song>()
|
||||
|
||||
readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
|
||||
if (cursor.count == 0) return@queryAll
|
||||
|
||||
val songIndex = cursor.getColumnIndexOrThrow(QueueColumns.SONG_HASH)
|
||||
val albumIndex = cursor.getColumnIndexOrThrow(QueueColumns.ALBUM_HASH)
|
||||
val isUserQueueIndex = cursor.getColumnIndexOrThrow(QueueColumns.IS_USER_QUEUE)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))?.let { song ->
|
||||
if (cursor.getInt(isUserQueueIndex) == 1) {
|
||||
queue.user.add(song)
|
||||
} else {
|
||||
queue.queue.add(song)
|
||||
musicStore.findSongFast(cursor.getLong(songIndex), cursor.getLong(albumIndex))
|
||||
?.let { song ->
|
||||
queue.add(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,11 +266,8 @@ class PlaybackStateDatabase(context: Context) :
|
|||
val playbackMode: PlaybackMode,
|
||||
val isShuffling: Boolean,
|
||||
val loopMode: LoopMode,
|
||||
val isInUserQueue: Boolean
|
||||
)
|
||||
|
||||
data class SavedQueue(val user: MutableList<Song>, val queue: MutableList<Song>)
|
||||
|
||||
private object StateColumns {
|
||||
const val COLUMN_ID = "id"
|
||||
const val COLUMN_SONG_HASH = "song"
|
||||
|
@ -294,19 +277,17 @@ class PlaybackStateDatabase(context: Context) :
|
|||
const val COLUMN_PLAYBACK_MODE = "playback_mode"
|
||||
const val COLUMN_IS_SHUFFLING = "is_shuffling"
|
||||
const val COLUMN_LOOP_MODE = "loop_mode"
|
||||
const val COLUMN_IS_IN_USER_QUEUE = "is_in_user_queue"
|
||||
}
|
||||
|
||||
private object QueueColumns {
|
||||
const val ID = "id"
|
||||
const val SONG_HASH = "song"
|
||||
const val ALBUM_HASH = "album"
|
||||
const val IS_USER_QUEUE = "is_user_queue"
|
||||
}
|
||||
|
||||
companion object {
|
||||
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_QUEUE = "queue_table"
|
||||
|
|
|
@ -30,6 +30,8 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Master class (and possible god object) for the playback state.
|
||||
|
@ -65,11 +67,6 @@ class PlaybackStateManager private constructor() {
|
|||
field = value
|
||||
callbacks.forEach { it.onQueueUpdate(value) }
|
||||
}
|
||||
private var mUserQueue = mutableListOf<Song>()
|
||||
set(value) {
|
||||
field = value
|
||||
callbacks.forEach { it.onUserQueueUpdate(value) }
|
||||
}
|
||||
private var mIndex = 0
|
||||
set(value) {
|
||||
field = value
|
||||
|
@ -98,11 +95,7 @@ class PlaybackStateManager private constructor() {
|
|||
field = 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 mHasPlayed = false
|
||||
|
||||
|
@ -114,8 +107,6 @@ class PlaybackStateManager private constructor() {
|
|||
val position: Long get() = mPosition
|
||||
/** The current queue determined by [parent] and [playbackMode] */
|
||||
val queue: List<Song> get() = mQueue
|
||||
/** The queue created by the user. */
|
||||
val userQueue: List<Song> get() = mUserQueue
|
||||
/** The current index of the queue */
|
||||
val index: Int get() = mIndex
|
||||
/** The current [PlaybackMode] */
|
||||
|
@ -250,8 +241,6 @@ class PlaybackStateManager private constructor() {
|
|||
* Update the playback to a new [song], doing all the required logic.
|
||||
*/
|
||||
private fun updatePlayback(song: Song, shouldPlay: Boolean = true) {
|
||||
mIsInUserQueue = false
|
||||
|
||||
mSong = song
|
||||
mPosition = 0
|
||||
|
||||
|
@ -264,29 +253,17 @@ class PlaybackStateManager private constructor() {
|
|||
* Go to the next song, along with doing all the checks that entails.
|
||||
*/
|
||||
fun next() {
|
||||
// If there's anything in the user queue, go to the first song in there instead
|
||||
// of incrementing the index.
|
||||
if (mUserQueue.isNotEmpty()) {
|
||||
updatePlayback(mUserQueue[0])
|
||||
mUserQueue.removeAt(0)
|
||||
|
||||
// Mark that the playback state is currently in the user queue, for later.
|
||||
mIsInUserQueue = true
|
||||
|
||||
forceUserQueueUpdate()
|
||||
// Increment the index, if it cannot be incremented any further, then
|
||||
// loop and pause/resume playback depending on the setting
|
||||
if (mIndex < mQueue.lastIndex) {
|
||||
mIndex = mIndex.inc()
|
||||
updatePlayback(mQueue[mIndex])
|
||||
} else {
|
||||
// Increment the index, if it cannot be incremented any further, then
|
||||
// loop and pause/resume playback depending on the setting
|
||||
if (mIndex < mQueue.lastIndex) {
|
||||
mIndex = mIndex.inc()
|
||||
updatePlayback(mQueue[mIndex])
|
||||
} else {
|
||||
mIndex = 0
|
||||
updatePlayback(mQueue[mIndex], shouldPlay = mLoopMode == LoopMode.ALL)
|
||||
}
|
||||
|
||||
forceQueueUpdate()
|
||||
mIndex = 0
|
||||
updatePlayback(mQueue[mIndex], shouldPlay = mLoopMode == LoopMode.ALL)
|
||||
}
|
||||
|
||||
forceQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,9 +274,8 @@ class PlaybackStateManager private constructor() {
|
|||
if (settingsManager.rewindWithPrev && mPosition >= REWIND_THRESHOLD) {
|
||||
rewind()
|
||||
} else {
|
||||
// Only decrement the index if there's a song to move back to AND if we are not exiting
|
||||
// the user queue.
|
||||
if (mIndex > 0 && !mIsInUserQueue) {
|
||||
// Only decrement the index if there's a song to move back to
|
||||
if (mIndex > 0) {
|
||||
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) {
|
||||
mUserQueue.add(song)
|
||||
|
||||
forceUserQueueUpdate()
|
||||
fun playNext(song: Song) {
|
||||
mQueue.add(min(mIndex + 1, max(mQueue.lastIndex, 0)), song)
|
||||
forceQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>) {
|
||||
mUserQueue.addAll(songs)
|
||||
|
||||
forceUserQueueUpdate()
|
||||
fun playNext(songs: List<Song>) {
|
||||
mQueue.addAll(min(mIndex + 1, max(mQueue.lastIndex, 0)), songs)
|
||||
forceQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a USER queue item at [index]. Will ignore invalid indexes.
|
||||
* Add a [song] to the end of the queue.
|
||||
*/
|
||||
fun removeUserQueueItem(index: Int) {
|
||||
logD("Removing item ${mUserQueue[index].name}.")
|
||||
|
||||
if (index > mUserQueue.size || index < 0) {
|
||||
logE("Index is out of bounds, did not remove user queue item.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
mUserQueue.removeAt(index)
|
||||
|
||||
forceUserQueueUpdate()
|
||||
fun addToQueue(song: Song) {
|
||||
mQueue.add(song)
|
||||
forceQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (from > mUserQueue.size || from < 0 || to > mUserQueue.size || to < 0) {
|
||||
logE("Indices were out of bounds, did not move user queue item")
|
||||
|
||||
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()
|
||||
fun addToQueue(songs: List<Song>) {
|
||||
mQueue.addAll(songs)
|
||||
forceQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -414,36 +362,28 @@ class PlaybackStateManager private constructor() {
|
|||
mQueue = mQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* Force any callbacks to recieve a user queue update.
|
||||
*/
|
||||
private fun forceUserQueueUpdate() {
|
||||
mUserQueue = mUserQueue
|
||||
}
|
||||
|
||||
// --- 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
|
||||
*/
|
||||
fun setShuffling(shuffled: Boolean, keepSong: Boolean) {
|
||||
mIsShuffling = shuffled
|
||||
|
||||
if (mIsShuffling) {
|
||||
genShuffle(keepSong, mIsInUserQueue)
|
||||
genShuffle(keepSong)
|
||||
} else {
|
||||
resetShuffle(keepSong, mIsInUserQueue)
|
||||
resetShuffle(keepSong)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new shuffled queue.
|
||||
* @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) {
|
||||
val lastSong = if (useLastSong) mQueue[0] else mSong
|
||||
private fun genShuffle(keepSong: Boolean) {
|
||||
val lastSong = mSong
|
||||
|
||||
logD("Shuffling queue")
|
||||
|
||||
|
@ -464,11 +404,10 @@ class PlaybackStateManager private constructor() {
|
|||
/**
|
||||
* Reset the queue to its normal, ordered state.
|
||||
* @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 lastSong = if (useLastSong) mQueue[mIndex] else mSong
|
||||
val lastSong = mSong
|
||||
|
||||
mQueue = when (mPlaybackMode) {
|
||||
PlaybackMode.ALL_SONGS ->
|
||||
|
@ -585,11 +524,12 @@ class PlaybackStateManager private constructor() {
|
|||
database.writeState(
|
||||
PlaybackStateDatabase.SavedState(
|
||||
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(
|
||||
"Save finished in ${System.currentTimeMillis() - start}ms"
|
||||
|
@ -608,7 +548,7 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
val start: Long
|
||||
val playbackState: PlaybackStateDatabase.SavedState?
|
||||
val queue: PlaybackStateDatabase.SavedQueue
|
||||
val queue: MutableList<Song>
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
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
|
||||
|
||||
if (playbackState != null) {
|
||||
logD("Found playback state $playbackState with queue size ${queue.user.size + queue.queue.size}")
|
||||
logD("Found playback state $playbackState")
|
||||
|
||||
unpackFromPlaybackState(playbackState)
|
||||
unpackQueue(queue)
|
||||
|
@ -649,30 +589,21 @@ class PlaybackStateManager private constructor() {
|
|||
mSong = playbackState.song
|
||||
mLoopMode = playbackState.loopMode
|
||||
mIsShuffling = playbackState.isShuffling
|
||||
mIsInUserQueue = playbackState.isInUserQueue
|
||||
|
||||
seekTo(playbackState.position)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack a list of queue items into a queue & user queue.
|
||||
*/
|
||||
private fun unpackQueue(queue: PlaybackStateDatabase.SavedQueue) {
|
||||
mUserQueue = queue.user
|
||||
mQueue = queue.queue
|
||||
private fun unpackQueue(queue: MutableList<Song>) {
|
||||
mQueue = queue
|
||||
|
||||
// When done, get a more accurate index to prevent issues with queue songs that were saved
|
||||
// to the db but are now deleted when the restore occurred.
|
||||
// Not done if in user queue because that could result in a bad index being created.
|
||||
if (!mIsInUserQueue) {
|
||||
mSong?.let { song ->
|
||||
val index = mQueue.indexOf(song)
|
||||
mIndex = if (index != -1) index else mIndex
|
||||
// Sanity check: Ensure that the
|
||||
mSong?.let { song ->
|
||||
while (mQueue.getOrNull(mIndex) != song) {
|
||||
mIndex--
|
||||
}
|
||||
}
|
||||
|
||||
forceQueueUpdate()
|
||||
forceUserQueueUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -702,14 +633,12 @@ class PlaybackStateManager private constructor() {
|
|||
fun onParentUpdate(parent: MusicParent?) {}
|
||||
fun onPositionUpdate(position: Long) {}
|
||||
fun onQueueUpdate(queue: List<Song>) {}
|
||||
fun onUserQueueUpdate(userQueue: List<Song>) {}
|
||||
fun onModeUpdate(mode: PlaybackMode) {}
|
||||
fun onIndexUpdate(index: Int) {}
|
||||
fun onPlayingUpdate(isPlaying: Boolean) {}
|
||||
fun onShuffleUpdate(isShuffling: Boolean) {}
|
||||
fun onLoopUpdate(loopMode: LoopMode) {}
|
||||
fun onSeek(position: Long) {}
|
||||
fun onInUserQueueUpdate(isInUserQueue: Boolean) {}
|
||||
}
|
||||
|
||||
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 { _, _ ->
|
||||
Coil.imageLoader(requireContext()).apply {
|
||||
this.memoryCache?.clear()
|
||||
|
|
|
@ -144,15 +144,31 @@ class ActionMenu(
|
|||
}
|
||||
}
|
||||
|
||||
R.id.action_queue_add -> {
|
||||
R.id.action_play_next -> {
|
||||
when (data) {
|
||||
is Song -> {
|
||||
playbackModel.addToUserQueue(data)
|
||||
playbackModel.playNext(data)
|
||||
context.showToast(R.string.lbl_queue_added)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
android:id="@+id/action_shuffle"
|
||||
android:title="@string/lbl_shuffle" />
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add" />
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add"
|
||||
app:showAsAction="never" />
|
||||
android:title="@string/lbl_queue_add" />
|
||||
</menu>
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add" />
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
android:id="@+id/action_shuffle"
|
||||
android:title="@string/lbl_shuffle"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add"
|
||||
app:showAsAction="never" />
|
||||
android:title="@string/lbl_queue_add" />
|
||||
</menu>
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_queue_add"
|
||||
android:title="@string/lbl_queue_add" />
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_play_next"
|
||||
android:title="@string/lbl_play_next" />
|
||||
<item
|
||||
android:id="@+id/action_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_playback">"Nyní hraje"</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_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_album">"Jít na album"</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_change_loop">"Změnit režim opakování"</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_clear_search">"Vymazat vyhledávání"</string>
|
||||
<string name="desc_blacklist_delete">"Vymazat vyloučený adresář"</string>
|
||||
|
@ -136,7 +135,6 @@
|
|||
<string name="clr_grey">"Šedá"</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">"Další z: %s"</string>
|
||||
<string name="fmt_songs_loaded">"Načtené skladby: %d"</string>
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="one">"%d skladba"</item>
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
<string name="lbl_playback">Aktuelle Wiedergabe</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_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_album">Zum Album gehen</string>
|
||||
|
@ -106,7 +106,6 @@
|
|||
<string name="desc_change_loop">Art der Wiederholung ändern</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_album_cover">Albumcover für %s</string>
|
||||
|
@ -136,7 +135,6 @@
|
|||
<string name="clr_grey">Grau</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">Nächsten von: %s</string>
|
||||
<string name="fmt_songs_loaded">Geladene Lieder: %d</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<string name="lbl_playback">Παίζει τώρα</string>
|
||||
|
||||
<string name="lbl_queue">Ουρά αναπαραγωγής</string>
|
||||
<string name="lbl_play_next">Επόμενο</string>
|
||||
<string name="lbl_queue_add">Προσθήκη στην ουρά αναπ/γής</string>
|
||||
<string name="lbl_queue_added">Προστέθηκε ένας τίτλος στην ουρά αναπαραγωγής</string>
|
||||
<string name="lbl_next_user_queue">Επόμενο</string>
|
||||
|
||||
<string name="lbl_go_artist">Πήγαινε στον καλλιτέχνη</string>
|
||||
<string name="lbl_go_album">Πήγαινε στο άλμπουμ</string>
|
||||
|
@ -55,8 +55,6 @@
|
|||
|
||||
<string name="desc_play_pause">Αναπαραγωγή/Παύση</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Εκκαθάριση ουράς αναπαραγωγής</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">Αναζήτηση στη βιβλιοθήκη…</string>
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
<string name="lbl_playback">Reproducción actual</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_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_album">Ir al álbum</string>
|
||||
|
@ -108,7 +108,6 @@
|
|||
<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_clear_user_queue">Limpiar cola</string>
|
||||
<string name="desc_clear_search">Borrar historial de búsqueda</string>
|
||||
<string name="desc_blacklist_delete">Eliminar directorio excluido</string>
|
||||
|
||||
|
@ -140,7 +139,6 @@
|
|||
<string name="clr_grey">Gris</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">Siguiente de: %s</string>
|
||||
<string name="fmt_songs_loaded">Canciones encontradas: %d</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Lecture en cours</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_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_artist">Aller à l\'artiste</string>
|
||||
|
@ -62,8 +62,6 @@
|
|||
|
||||
<string name="desc_play_pause">Lecture/Pause</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Effacer la file d\'attente</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<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_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_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_album">Ugrás az albumhoz</string>
|
||||
|
@ -64,8 +64,6 @@
|
|||
|
||||
<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 -->
|
||||
<string name="clr_red">Piros</string>
|
||||
<string name="clr_pink">Rózsaszínű</string>
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Sedang Diputar</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_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_album">Pergi ke album</string>
|
||||
|
@ -64,8 +64,6 @@
|
|||
|
||||
<string name="desc_play_pause">Putar/Jeda</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Kosongkan antrean</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Schermata di riproduzione</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_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_album">Vai all\'album</string>
|
||||
|
@ -63,8 +63,6 @@
|
|||
|
||||
<string name="desc_play_pause">Play/Pausa</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Svuota coda</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">Cerca nella tua libreria…</string>
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">지금 재생 중</string>
|
||||
|
||||
<string name="lbl_queue">대기열</string>
|
||||
<string name="lbl_play_next">다음 곡으로 재생</string>
|
||||
<string name="lbl_queue_add">대기열에 추가</string>
|
||||
<string name="lbl_queue_added">가 대기열에 추가되었습니다</string>
|
||||
<string name="lbl_next_user_queue">다음 곡</string>
|
||||
|
||||
<string name="lbl_go_artist">아티스트로 가기</string>
|
||||
<string name="lbl_go_album">앨범으로 가기</string>
|
||||
|
@ -60,8 +60,6 @@
|
|||
|
||||
<string name="desc_play_pause">재생/일시 정지</string>
|
||||
|
||||
<string name="desc_clear_user_queue">재생 대기열 비우기</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">저장소 검색…</string>
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
<string name="lbl_playback">Afspeelscherm</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_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_album">Ga naar album</string>
|
||||
|
@ -106,7 +106,6 @@
|
|||
<string name="desc_skip_prev">Naar het laatste nummer gaan</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_blacklist_delete">Verwijder uitgesloten map</string>
|
||||
|
||||
|
@ -138,7 +137,6 @@
|
|||
<string name="clr_grey">Grijis</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">Volgende van: %s</string>
|
||||
<string name="fmt_songs_loaded">Nummers geladen: %d</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Obecnie Grane</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_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_album">Przejdź do albumu</string>
|
||||
|
@ -61,8 +61,6 @@
|
|||
|
||||
<string name="desc_play_pause">Odtwarzanie/Pauza</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Wyczyść kolejkę</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">Szukaj w bibliotece…</string>
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<string name="lbl_playback">Tocando agora</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_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_album">Ir para o álbum</string>
|
||||
|
@ -62,8 +62,6 @@
|
|||
|
||||
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Limpar fila</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">Procurar na biblioteca…</string>
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">A reproduzir</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_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_album">Ir para o álbum</string>
|
||||
|
@ -63,8 +63,6 @@
|
|||
|
||||
<string name="desc_play_pause">Reproduzir/Pausar</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Limpar fila</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">Procurar na biblioteca…</string>
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Redare Acum</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_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_album">Accesaţi albumul</string>
|
||||
|
@ -65,8 +65,6 @@
|
|||
|
||||
<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 -->
|
||||
<string name="clr_red">Roșu</string>
|
||||
<string name="clr_pink">Roz</string>
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
<string name="lbl_play_genre">Воспроизвести из жанра</string>
|
||||
|
||||
<string name="lbl_queue">Очередь</string>
|
||||
<string name="lbl_play_next">Воспроизвести далее</string>
|
||||
<string name="lbl_queue_add">Добавить в очередь</string>
|
||||
<string name="lbl_queue_added">Добавлено в очередь</string>
|
||||
<string name="lbl_next_user_queue">Далее в очереди</string>
|
||||
|
||||
<string name="lbl_go_artist">Перейти к исполнителю</string>
|
||||
<string name="lbl_go_album">Перейти к альбому</string>
|
||||
|
@ -121,7 +121,6 @@
|
|||
<string name="desc_shuffle">Включить или выключить перемешивание</string>
|
||||
<string name="desc_shuffle_all">Перемешать все композиции</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Очистить очередь</string>
|
||||
<string name="desc_clear_queue_item">Удалить композицию из очереди</string>
|
||||
<string name="desc_queue_handle">Переместить композицию в очереди</string>
|
||||
<string name="desc_tab_handle">Переместить вкладку</string>
|
||||
|
@ -161,7 +160,6 @@
|
|||
<string name="clr_grey">Серый</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">Далее из: %s</string>
|
||||
<string name="fmt_songs_loaded">Всего композиций: %d</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">Şuan çalınan</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_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_album">Albüme git</string>
|
||||
|
@ -63,8 +63,6 @@
|
|||
|
||||
<string name="desc_play_pause">Başlat/Durdur</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Temizle Kuyruk</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<string name="lbl_playback">Відтворюється</string>
|
||||
|
||||
<string name="lbl_queue">Черга</string>
|
||||
<string name="lbl_play_next">Відтворити наступною</string>
|
||||
<string name="lbl_queue_add">Додати в чергу</string>
|
||||
<string name="lbl_queue_added">Додана в чергу</string>
|
||||
<string name="lbl_next_user_queue">До наступного</string>
|
||||
|
||||
<string name="lbl_go_artist">Перейти до виконавця</string>
|
||||
<string name="lbl_go_album">Перейти до альбому</string>
|
||||
|
@ -59,8 +59,6 @@
|
|||
|
||||
<string name="desc_play_pause">Відтворити/Зупинити</string>
|
||||
|
||||
<string name="desc_clear_user_queue">Очистити черга</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_songs_loaded">Пісні завантажено: %d</string>
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">正在播放界面</string>
|
||||
|
||||
<string name="lbl_queue">播放队列</string>
|
||||
<string name="lbl_play_next">作为下一首播放</string>
|
||||
<string name="lbl_queue_add">加入播放队列</string>
|
||||
<string name="lbl_queue_added">此歌曲已加入播放队列</string>
|
||||
<string name="lbl_next_user_queue">即将播放</string>
|
||||
|
||||
<string name="lbl_go_artist">查看艺术家</string>
|
||||
<string name="lbl_go_album">查看专辑</string>
|
||||
|
@ -56,8 +56,6 @@
|
|||
|
||||
<string name="desc_play_pause">播放/暂停</string>
|
||||
|
||||
<string name="desc_clear_user_queue">清空播放队列</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<plurals name="fmt_song_count">
|
||||
<item quantity="other">%d 歌曲</item>
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
<string name="lbl_playback">播放面板</string>
|
||||
|
||||
<string name="lbl_queue">隊列</string>
|
||||
<string name="lbl_play_next">下一首播放</string>
|
||||
<string name="lbl_queue_add">添加到隊列</string>
|
||||
<string name="lbl_queue_added">已加入隊列</string>
|
||||
<string name="lbl_next_user_queue">播放佇列:即將播放</string>
|
||||
|
||||
<string name="lbl_go_artist">前往該歌手頁面</string>
|
||||
<string name="lbl_go_album">專輯</string>
|
||||
|
@ -61,8 +61,6 @@
|
|||
|
||||
<string name="desc_play_pause">播放/暫停</string>
|
||||
|
||||
<string name="desc_clear_user_queue">清空播放佇列</string>
|
||||
|
||||
<!-- Hint Namespace | EditText Hints -->
|
||||
<string name="hint_search_library">搜尋音樂庫…</string>
|
||||
|
||||
|
|
|
@ -6,26 +6,6 @@
|
|||
<attr name="entryValues" format="reference" />
|
||||
</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">
|
||||
<item>@string/set_theme_auto</item>
|
||||
<item>@string/set_theme_day</item>
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
<string name="lbl_play_genre">Play from genre</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_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_album">Go to album</string>
|
||||
|
@ -122,7 +122,6 @@
|
|||
<string name="desc_shuffle">Turn shuffle on or off</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_queue_handle">Move this queue song</string>
|
||||
<string name="desc_tab_handle">Move this tab</string>
|
||||
|
@ -162,7 +161,6 @@
|
|||
<string name="clr_grey">Grey</string>
|
||||
|
||||
<!-- Format Namespace | Value formatting/plurals -->
|
||||
<string name="fmt_next_from">Next From: %s</string>
|
||||
<string name="fmt_songs_loaded">Songs loaded: %d</string>
|
||||
|
||||
<plurals name="fmt_song_count">
|
||||
|
|
|
@ -171,11 +171,6 @@
|
|||
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.Auxio.FloatingActionButton.PlayPause</item>
|
||||
</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="">
|
||||
<item name="cornerSize">50%</item>
|
||||
</style>
|
||||
|
|
|
@ -27,4 +27,3 @@ Feel free to fork Auxio to add your own feature set however.
|
|||
- Recently added list [#18]
|
||||
- Lyrics [#19]
|
||||
- Tag editing [#33]
|
||||
- Specialized queue adding (ex. Play Next) [#44]
|
Loading…
Reference in a new issue