Skip to content

Commit

Permalink
made play button in album playable, shuffle is uhhh
Browse files Browse the repository at this point in the history
  • Loading branch information
brahmkshatriya committed Mar 1, 2024
1 parent 8fac817 commit 554f3c6
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 96 deletions.
6 changes: 3 additions & 3 deletions app/src/main/java/dev/brahmkshatriya/echo/player/Global.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ object Global {
private val _clearQueue = MutableSharedFlow<Unit>()
val clearQueueFlow = _clearQueue.asSharedFlow()
fun clearQueue(scope: CoroutineScope) {
_queue.clear()
scope.launch {
_clearQueue.emit(Unit)
}
_queue.clear()
}

private val _removeTrack = MutableSharedFlow<Int>()
Expand All @@ -33,7 +33,7 @@ object Global {
}
}

private val _addTrack = MutableSharedFlow<Pair<Int, MediaItem>>()
private val _addTrack = MutableSharedFlow<Triple<Int, MediaItem, Track>>()
val addTrackFlow = _addTrack.asSharedFlow()
fun addTrack(
scope: CoroutineScope, track: Track, stream: StreamableAudio, positionOffset: Int = 0
Expand All @@ -44,7 +44,7 @@ object Global {

_queue.add(index, mediaId to track)
scope.launch {
_addTrack.emit(index to item)
_addTrack.emit(Triple(index, item, track))
}
return index to item
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class PlayerViewModel @Inject constructor(
val seekToPrevious: MutableSharedFlow<Unit> = MutableSharedFlow()
val seekToNext: MutableSharedFlow<Unit> = MutableSharedFlow()
val repeat: MutableSharedFlow<Int> = MutableSharedFlow()
val shuffle: MutableSharedFlow<List<Pair<Int, Int>>> = MutableSharedFlow()

private suspend fun loadStreamable(track: Track): StreamableAudio? {
return trackClient?.getStreamable(track) ?: return null
Expand All @@ -54,11 +55,28 @@ class PlayerViewModel @Inject constructor(
}

fun play(tracks: List<Track>) {
clearQueue()
viewModelScope.launch(Dispatchers.IO) {
tracks.forEach {
loadAndAddToQueue(it)
tracks.forEachIndexed { index, track ->
if (index == 0) audioIndexFlow.emit(loadAndAddToQueue(track))
else loadAndAddToQueue(track)
}
audioIndexFlow.emit(0)
}
}

private var oldList: List<Pair<Int, Int>>? = null
fun shuffle(shuffled: Boolean) {
println("Shuffling: $shuffled")
val list = if (shuffled) {
(0..<Global.queue.size).shuffled().mapIndexed { i, j -> i to j }
.also { oldList = it.asReversed() }
} else oldList ?: return
println(list)
viewModelScope.launch(Dispatchers.IO) {
shuffle.emit(list)
}
list.forEach { (i, j) ->
moveQueueItems(i, j)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import dev.brahmkshatriya.echo.player.Global
import dev.brahmkshatriya.echo.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.player.PlayerViewModel
import dev.brahmkshatriya.echo.ui.adapters.PlaylistAdapter
import dev.brahmkshatriya.echo.utils.dpToPx
import dev.brahmkshatriya.echo.utils.emit
import dev.brahmkshatriya.echo.utils.loadInto
import dev.brahmkshatriya.echo.utils.observe
Expand Down Expand Up @@ -222,14 +223,14 @@ fun createPlayerUI(
val new = viewHolder.bindingAdapterPosition
val old = target.bindingAdapterPosition
playerViewModel.moveQueueItems(new, old)
adapter?.notifyItemMoved(new, old)
adapter?.moveItems(old, new)
return true
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val pos = viewHolder.bindingAdapterPosition
playerViewModel.removeQueueItem(pos)
adapter?.notifyItemRemoved(pos)
adapter?.removeItem(pos)
}
}
val touchHelper = ItemTouchHelper(callback)
Expand All @@ -244,9 +245,10 @@ fun createPlayerUI(

override fun onItemClosedClicked(position: Int) {
playerViewModel.removeQueueItem(position)
adapter?.notifyItemRemoved(position)
adapter?.removeItem(position)
}
})
adapter.submitList(Global.queue.map { it.second })

playlistBinding.playlistRecycler.apply {
layoutManager = linearLayoutManager
Expand All @@ -258,6 +260,17 @@ fun createPlayerUI(
playerViewModel.clearQueue()
}

playlistBinding.playlistShuffle.apply {
val stroke = 1.dpToPx()
strokeWidth = if(uiViewModel.shuffled.value) stroke else 0
setOnClickListener {
uiViewModel.shuffled.value = !uiViewModel.shuffled.value
val shuffled = uiViewModel.shuffled.value
playerViewModel.shuffle(shuffled)
strokeWidth = if(shuffled) stroke else 0
}
}

uiViewModel.view = WeakReference(playerBinding.collapsedTrackCover)

activity.apply {
Expand Down Expand Up @@ -373,15 +386,15 @@ fun createPlayerUI(
}
}
observe(uiViewModel.playlist) {
adapter.setCurrent(Global.queue, it)
adapter.setCurrent(it)
}

observe(Global.addTrackFlow) { (index, _) ->
adapter.addItem(Global.queue, index)
observe(Global.addTrackFlow) { (index, _, item) ->
adapter.addItem(index, item)
}

observe(Global.clearQueueFlow) {
adapter.removeItems(Global.queue)
adapter.emptyItems()
PlayerBackButtonHelper.playlistState.value = STATE_COLLAPSED
PlayerBackButtonHelper.playerSheetState.value = STATE_HIDDEN
container.post {
Expand All @@ -391,5 +404,11 @@ fun createPlayerUI(
}
}
}

observe(playerViewModel.shuffle) {
it.forEach { (i, j) ->
adapter.moveItems(i, j)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class PlayerUIViewModel : ViewModel() {
val isPlaying = MutableStateFlow(false)
val nextEnabled = MutableStateFlow(false)
val previousEnabled = MutableStateFlow(false)
val shuffled = MutableStateFlow(false)

var view: WeakReference<View> = WeakReference(null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ import android.view.LayoutInflater
import android.view.MotionEvent.ACTION_DOWN
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.common.models.Track
import dev.brahmkshatriya.echo.databinding.ItemPlaylistItemBinding
import dev.brahmkshatriya.echo.player.Global
import dev.brahmkshatriya.echo.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.utils.loadInto

class PlaylistAdapter(
val callback: Callback,
var list: List<Pair<String, Track>> = Global.queue,
val callback: Callback
) : RecyclerView.Adapter<PlaylistAdapter.ViewHolder>() {

open class Callback {
Expand All @@ -42,6 +41,12 @@ class PlaylistAdapter(
}
}

class DiffCallback : DiffUtil.ItemCallback<Track>() {
override fun areItemsTheSame(oldItem: Track, newItem: Track) = oldItem.uri == newItem.uri
override fun areContentsTheSame(oldItem: Track, newItem: Track) = oldItem == newItem

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemPlaylistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
Expand All @@ -50,7 +55,7 @@ class PlaylistAdapter(

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
val track = list[position].second
val track = list[position]
binding.playlistItemTitle.text = track.title
track.cover.loadInto(binding.playlistItemImageView, R.drawable.art_music)
binding.playlistCurrentItem.isVisible = position == currentPosition
Expand All @@ -65,27 +70,42 @@ class PlaylistAdapter(
binding.playlistItemAuthor.text = subtitle
}

private fun submitList(list: List<Pair<String, Track>>) {
this.list = list
}

private var currentPosition: Int? = null
fun setCurrent(list: List<Pair<String, Track>>, position: Int?) {
submitList(list)
fun setCurrent(position: Int?) {
val old = currentPosition
currentPosition = position
old?.let { notifyItemChanged(it) }
currentPosition?.let { notifyItemChanged(it) }
}

fun addItem(queue: List<Pair<String, Track>>, index: Int) {
submitList(queue)
val list = mutableListOf<Track>()

fun addItem(index: Int, track: Track) {
list.add(index, track)
notifyItemInserted(index)
}

@SuppressLint("NotifyDataSetChanged")
fun removeItems(queue: List<Pair<String, Track>>) {
submitList(queue)
notifyDataSetChanged()
fun removeItem(index: Int) {
list.removeAt(index)
notifyItemRemoved(index)
}

fun moveItems(fromIndex: Int, toIndex: Int) {
val item = list.removeAt(fromIndex)
list.add(toIndex, item)
notifyItemMoved(fromIndex, toIndex)
}

fun emptyItems() {
val oldSize = list.size
list.clear()
notifyItemRangeRemoved(0, oldSize)
}

fun submitList(tracks: List<Track>) {
emptyItems()
list.addAll(tracks)
notifyItemRangeInserted(0, tracks.size)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package dev.brahmkshatriya.echo.ui.adapters

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem
import dev.brahmkshatriya.echo.common.models.Track
import dev.brahmkshatriya.echo.databinding.ItemTrackSmallBinding
import dev.brahmkshatriya.echo.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.ui.MediaItemClickListener
import dev.brahmkshatriya.echo.utils.loadInto

class TrackAdapter(
private val callback: MediaItemClickListener,
private val albumVisible: Boolean = true,
) : RecyclerView.Adapter<TrackAdapter.ViewHolder>() {

var list: List<Track>? = null

inner class ViewHolder(val binding: ItemTrackSmallBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val track = list?.get(bindingAdapterPosition) ?: return@setOnClickListener
callback.onClick(binding.imageView to track.toMediaItem())
}
}
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemTrackSmallBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)

override fun getItemCount() = list?.count() ?: 0

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
val track = list?.get(position) ?: return
binding.itemNumber.text =
binding.root.context.getString(R.string.number_dot, (position + 1))
binding.itemTitle.text = track.title
track.cover.loadInto(binding.imageView, R.drawable.art_music)
var subtitle = ""
track.duration?.toTimeString()?.let {
subtitle += it
}
track.artists.joinToString(", ") { it.name }.let {
if (it.isNotBlank()) subtitle += if (subtitle.isNotBlank()) "$it" else it
}
binding.itemSubtitle.isVisible = subtitle.isNotEmpty()
binding.itemSubtitle.text = subtitle
}

@SuppressLint("NotifyDataSetChanged")
fun submitList(tracks: List<Track>) {
list = tracks
notifyDataSetChanged()
}
}
Loading

0 comments on commit 554f3c6

Please sign in to comment.