#15: Enable reordering of playlist tracks. Fixed an algorithmic issue with reordering of queue items.
This commit is contained in:
parent
d2a981c368
commit
ab654a08c4
|
@ -16,12 +16,13 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
|
||||||
import kotlinx.android.synthetic.main.row_track.view.*
|
import kotlinx.android.synthetic.main.row_track.view.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class PlaylistTracksAdapter(private val context: Context?, private val favoriteListener: OnFavoriteListener? = null, private val playlistListener: OnPlaylistListener? = null, val fromQueue: Boolean = false) : OtterAdapter<PlaylistTrack, PlaylistTracksAdapter.ViewHolder>() {
|
class PlaylistTracksAdapter(private val context: Context?, private val favoriteListener: OnFavoriteListener? = null, private val playlistListener: OnPlaylistListener? = null) : OtterAdapter<PlaylistTrack, PlaylistTracksAdapter.ViewHolder>() {
|
||||||
interface OnFavoriteListener {
|
interface OnFavoriteListener {
|
||||||
fun onToggleFavorite(id: Int, state: Boolean)
|
fun onToggleFavorite(id: Int, state: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnPlaylistListener {
|
interface OnPlaylistListener {
|
||||||
|
fun onMoveTrack(from: Int, to: Int)
|
||||||
fun onRemoveTrackFromPlaylist(track: Track, index: Int)
|
fun onRemoveTrackFromPlaylist(track: Track, index: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +39,8 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||||
super.onAttachedToRecyclerView(recyclerView)
|
super.onAttachedToRecyclerView(recyclerView)
|
||||||
|
|
||||||
if (fromQueue) {
|
touchHelper = ItemTouchHelper(TouchHelperCallback()).also {
|
||||||
touchHelper = ItemTouchHelper(TouchHelperCallback()).also {
|
it.attachToRecyclerView(recyclerView)
|
||||||
it.attachToRecyclerView(recyclerView)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +95,7 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
holder.actions.setOnClickListener {
|
holder.actions.setOnClickListener {
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
PopupMenu(context, holder.actions, Gravity.START, R.attr.actionOverflowMenuStyle, 0).apply {
|
PopupMenu(context, holder.actions, Gravity.START, R.attr.actionOverflowMenuStyle, 0).apply {
|
||||||
inflate(if (fromQueue) R.menu.row_queue else R.menu.row_track)
|
inflate(R.menu.row_track)
|
||||||
|
|
||||||
menu.findItem(R.id.track_remove_from_playlist).isVisible = true
|
menu.findItem(R.id.track_remove_from_playlist).isVisible = true
|
||||||
|
|
||||||
|
@ -116,16 +115,14 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromQueue) {
|
holder.handle.visibility = View.VISIBLE
|
||||||
holder.handle.visibility = View.VISIBLE
|
|
||||||
|
|
||||||
holder.handle.setOnTouchListener { _, event ->
|
holder.handle.setOnTouchListener { _, event ->
|
||||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||||
touchHelper.startDrag(holder)
|
touchHelper.startDrag(holder)
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,13 +132,12 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
Collections.swap(data, i, i + 1)
|
Collections.swap(data, i, i + 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i in newPosition.downTo(oldPosition)) {
|
for (i in oldPosition.downTo(newPosition + 1)) {
|
||||||
Collections.swap(data, i, i - 1)
|
Collections.swap(data, i, i - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyItemMoved(oldPosition, newPosition)
|
notifyItemMoved(oldPosition, newPosition)
|
||||||
CommandBus.send(Command.MoveFromQueue(oldPosition, newPosition))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(view: View, val context: Context?) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
inner class ViewHolder(view: View, val context: Context?) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
|
@ -154,20 +150,18 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
val actions = view.actions
|
val actions = view.actions
|
||||||
|
|
||||||
override fun onClick(view: View?) {
|
override fun onClick(view: View?) {
|
||||||
when (fromQueue) {
|
data.subList(layoutPosition, data.size).plus(data.subList(0, layoutPosition)).apply {
|
||||||
true -> CommandBus.send(Command.PlayTrack(layoutPosition))
|
CommandBus.send(Command.ReplaceQueue(this.map { it.track }))
|
||||||
false -> {
|
|
||||||
data.subList(layoutPosition, data.size).plus(data.subList(0, layoutPosition)).apply {
|
|
||||||
CommandBus.send(Command.ReplaceQueue(this.map { it.track }))
|
|
||||||
|
|
||||||
context.toast("All tracks were added to your queue")
|
context.toast("All tracks were added to your queue")
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class TouchHelperCallback : ItemTouchHelper.Callback() {
|
inner class TouchHelperCallback : ItemTouchHelper.Callback() {
|
||||||
|
var from = -1
|
||||||
|
var to = -1
|
||||||
|
|
||||||
override fun isLongPressDragEnabled() = false
|
override fun isLongPressDragEnabled() = false
|
||||||
|
|
||||||
override fun isItemViewSwipeEnabled() = false
|
override fun isItemViewSwipeEnabled() = false
|
||||||
|
@ -176,6 +170,9 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
|
makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
|
||||||
|
|
||||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||||
|
if (from == -1) from = viewHolder.adapterPosition
|
||||||
|
to = target.adapterPosition
|
||||||
|
|
||||||
onItemMove(viewHolder.adapterPosition, target.adapterPosition)
|
onItemMove(viewHolder.adapterPosition, target.adapterPosition)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -194,6 +191,13 @@ class PlaylistTracksAdapter(private val context: Context?, private val favoriteL
|
||||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||||
viewHolder.itemView.background = ColorDrawable(Color.TRANSPARENT)
|
viewHolder.itemView.background = ColorDrawable(Color.TRANSPARENT)
|
||||||
|
|
||||||
|
if (from != -1 && to != -1 && from != to) {
|
||||||
|
playlistListener?.onMoveTrack(from, to)
|
||||||
|
|
||||||
|
from = -1
|
||||||
|
to = -1
|
||||||
|
}
|
||||||
|
|
||||||
super.clearView(recyclerView, viewHolder)
|
super.clearView(recyclerView, viewHolder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,7 +146,7 @@ class TracksAdapter(private val context: Context?, private val favoriteListener:
|
||||||
Collections.swap(data, i, i + 1)
|
Collections.swap(data, i, i + 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i in newPosition.downTo(oldPosition)) {
|
for (i in oldPosition.downTo(newPosition + 1)) {
|
||||||
Collections.swap(data, i, i - 1)
|
Collections.swap(data, i, i - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,6 +187,10 @@ class PlaylistTracksFragment : OtterFragment<PlaylistTrack, PlaylistTracksAdapte
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class PlaylistListener : PlaylistTracksAdapter.OnPlaylistListener {
|
inner class PlaylistListener : PlaylistTracksAdapter.OnPlaylistListener {
|
||||||
|
override fun onMoveTrack(from: Int, to: Int) {
|
||||||
|
playlistsRepository.move(albumId, from, to)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRemoveTrackFromPlaylist(track: Track, index: Int) {
|
override fun onRemoveTrackFromPlaylist(track: Track, index: Int) {
|
||||||
lifecycleScope.launch(Main) {
|
lifecycleScope.launch(Main) {
|
||||||
playlistsRepository.remove(albumId, track, index)
|
playlistsRepository.remove(albumId, track, index)
|
||||||
|
|
|
@ -79,4 +79,21 @@ class ManagementPlaylistsRepository(override val context: Context?) : Repository
|
||||||
.body(Gson().toJson(body))
|
.body(Gson().toJson(body))
|
||||||
.awaitByteArrayResponseResult()
|
.awaitByteArrayResponseResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun move(id: Int, from: Int, to: Int) {
|
||||||
|
val body = mapOf("from" to from, "to" to to)
|
||||||
|
|
||||||
|
val request = Fuel.post(mustNormalizeUrl("/api/v1/playlists/${id}/move/")).apply {
|
||||||
|
if (!Settings.isAnonymous()) {
|
||||||
|
header("Authorization", "Bearer ${Settings.getAccessToken()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
request
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(Gson().toJson(body))
|
||||||
|
.awaitByteArrayResponseResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue