Skip to content

Commit

Permalink
- Next/Previous button work now
Browse files Browse the repository at this point in the history
- Refactor PlayerListener a bit
- Create TrackWithStream
- Refactor Loading with the Paging Adapter
  • Loading branch information
brahmkshatriya committed Feb 8, 2024
1 parent 2a30f39 commit b930620
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package dev.brahmkshatriya.echo.data.models

import android.net.Uri
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow

sealed class Playlist{
sealed class Playlist {

open class Small(
open val uri: Uri,
Expand All @@ -22,7 +20,7 @@ sealed class Playlist{
override val title: String,
override val cover: ImageHolder?,
val author: User?,
val tracks: Flow<PagingData<Track>>,
val tracks: List<Track>,
val creationDate: String?,
val duration: Long?,
val description: String?,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dev.brahmkshatriya.echo.ui.adapters

interface ClickListener<T> {
fun onClick(item: T)
fun onLongClick(item: T)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import dev.brahmkshatriya.echo.databinding.ItemMediaBinding
import dev.brahmkshatriya.echo.ui.utils.loadInto

class MediaItemAdapter(
private val play : (Track) -> Unit
private val listener: ClickListener<Track>
) :
PagingDataAdapter<MediaItem, MediaItemAdapter.MediaItemHolder>(
MediaItemComparator
Expand All @@ -31,7 +31,11 @@ class MediaItemAdapter(
binding.title.text = item.track.title
item.track.cover?.loadInto(binding.imageView)
binding.root.setOnClickListener {
play(item.track)
listener.onClick(item.track)
}
binding.root.setOnLongClickListener {
listener.onLongClick(item.track)
true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.paging.LoadState
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL
Expand All @@ -18,10 +20,24 @@ import dev.brahmkshatriya.echo.ui.utils.loadInto

class MediaItemsContainerAdapter(
private val lifecycle: Lifecycle,
private val play: (Track) -> Unit,
private val listener: ClickListener<Track>,
) : PagingDataAdapter<MediaItemsContainer, MediaItemsContainerAdapter.MediaItemsContainerHolder>(
MediaItemsContainerComparator
) {

fun withLoadingFooter(): ConcatAdapter {
val footer = ContainerLoadingAdapter {
retry()
}
addLoadStateListener { loadStates ->
footer.loadState = when (loadStates.refresh) {
is LoadState.NotLoading -> loadStates.append
else -> loadStates.refresh
}
}
return ConcatAdapter(this, footer)
}

override fun getItemViewType(position: Int): Int {
return getItem(position)?.let {
when (it) {
Expand Down Expand Up @@ -57,15 +73,19 @@ class MediaItemsContainerAdapter(
binding.textView.text = category.title
binding.recyclerView.layoutManager =
LinearLayoutManager(binding.root.context, HORIZONTAL, false)
val adapter = MediaItemAdapter(play)
val adapter = MediaItemAdapter(listener)
binding.recyclerView.adapter = adapter
adapter.submitData(lifecycle, category.list)
}

is MediaItemsContainerBinding.Track -> {
val binding = holder.container.binding
val track = (item as MediaItemsContainer.TrackItem).track
binding.root.setOnClickListener { play(track) }
binding.root.setOnClickListener { listener.onClick(track) }
binding.root.setOnLongClickListener {
listener.onLongClick(track)
true
}

binding.title.text = track.title

Expand Down
28 changes: 19 additions & 9 deletions app/src/main/java/dev/brahmkshatriya/echo/ui/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.paging.LoadState
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.data.models.Track
import dev.brahmkshatriya.echo.databinding.FragmentRecyclerBinding
import dev.brahmkshatriya.echo.ui.adapters.ContainerLoadingAdapter
import dev.brahmkshatriya.echo.ui.adapters.ClickListener
import dev.brahmkshatriya.echo.ui.adapters.HeaderAdapter
import dev.brahmkshatriya.echo.ui.adapters.MediaItemsContainerAdapter
import dev.brahmkshatriya.echo.ui.player.PlayerBackButtonHelper
Expand Down Expand Up @@ -42,25 +44,33 @@ class HomeFragment : Fragment() {

val headerAdapter = HeaderAdapter(R.string.home)
val mediaItemsContainerAdapter =
MediaItemsContainerAdapter(viewLifecycleOwner.lifecycle, playerViewModel::play)
MediaItemsContainerAdapter(viewLifecycleOwner.lifecycle, object : ClickListener<Track> {
override fun onClick(item: Track) {
playerViewModel.play(item)
}

mediaItemsContainerAdapter.withLoadStateFooter(ContainerLoadingAdapter {
homeViewModel.loadFeed(homeViewModel.genre)
})
override fun onLongClick(item: Track) {
playerViewModel.addToQueue(item)
}
})

binding.recyclerView.adapter = ConcatAdapter(headerAdapter, mediaItemsContainerAdapter)
val concat = mediaItemsContainerAdapter.withLoadingFooter()

binding.recyclerView.adapter = ConcatAdapter(headerAdapter, concat)
binding.recyclerView.layoutManager = LinearLayoutManager(context)

binding.swipeRefresh.setOnRefreshListener {
homeViewModel.loadFeed(homeViewModel.genre)
mediaItemsContainerAdapter.refresh()
}

mediaItemsContainerAdapter.addLoadStateListener {
binding.swipeRefresh.isRefreshing = it.refresh is LoadState.Loading
}

homeViewModel.feed.observeFlow(viewLifecycleOwner) {
binding.swipeRefresh.isRefreshing = false
if (it == null) return@observeFlow
mediaItemsContainerAdapter.submitData(it)
}

homeViewModel.loadFeed(homeViewModel.genre)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ class HomeViewModel @Inject constructor(
private val homeClient: HomeFeedClient
) : ViewModel() {

init {
viewModelScope.launch {
loadFeed()
}
}

private val _feed: MutableStateFlow<PagingData<MediaItemsContainer>?> = MutableStateFlow(null)
val feed = _feed.asStateFlow()

val genre: String? = null

fun loadFeed(genre: String?) {
fun loadFeed() {
viewModelScope.launch(Dispatchers.IO) {
println("Loading Data")
homeClient.getHomeFeed(genre).cachedIn(viewModelScope).collectLatest {
Expand Down
47 changes: 20 additions & 27 deletions app/src/main/java/dev/brahmkshatriya/echo/ui/player/Player.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import com.google.android.material.checkbox.MaterialCheckBox.STATE_CHECKED
import com.google.android.material.checkbox.MaterialCheckBox.STATE_UNCHECKED
import dev.brahmkshatriya.echo.MainActivity
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.data.models.StreamableAudio
import dev.brahmkshatriya.echo.databinding.BottomPlayerBinding
import dev.brahmkshatriya.echo.ui.player.PlayerHelper.Companion.toMetaData
import dev.brahmkshatriya.echo.ui.player.PlayerHelper.Companion.toTimeString
import dev.brahmkshatriya.echo.ui.utils.dpToPx
import dev.brahmkshatriya.echo.ui.utils.updatePaddingWithSystemInsets
Expand Down Expand Up @@ -89,11 +87,11 @@ class Player(
binding.collapsedTrackPlayPause.addOnCheckedStateChangedListener(playPauseListener)

binding.trackNext.setOnClickListener {
player.seekToNextMediaItem()
player.seekToNext()
}

binding.trackPrevious.setOnClickListener {
player.seekToPreviousMediaItem()
player.seekToPrevious()
}


Expand All @@ -114,31 +112,26 @@ class Player(
val listener = PlayerListener(player, binding, playPauseListener)
player.addListener(listener)

fun addToQueue(it: TrackWithStream?): MediaItem? {
it ?: return null
val item = it.mediaItemBuilder()
listener.map[item.mediaMetadata] = it.track
player.addMediaItem(item)
player.prepare()
player.play()
return item
}

activity.lifecycleScope.launch {
viewModel.audioFlow.collectLatest { pair ->
pair?.run {
val track = this.first
val metadata = track.toMetaData()
listener.map(metadata, track)

val builder = MediaItem.Builder()
.setMediaMetadata(metadata)
val item = when (val audio = this.second) {
is StreamableAudio.StreamableFile -> {
builder.setUri(audio.uri)
}

is StreamableAudio.StreamableUrl -> {
builder.setUri(audio.url.url)
}

is StreamableAudio.ByteStreamAudio -> TODO()
}
player.setMediaItem(item.build())
player.prepare()
player.play()
launch {
viewModel.audioFlow.collectLatest {
// val item = addToQueue(it) ?: return@collectLatest
}
}
launch {
viewModel.audioQueueFlow.collectLatest {
addToQueue(it)
}

}
}
}
Expand Down
Loading

0 comments on commit b930620

Please sign in to comment.