Skip to content

Commit

Permalink
Refactor LibraryClient to PlaylistEditClient
Browse files Browse the repository at this point in the history
  • Loading branch information
brahmkshatriya committed Sep 17, 2024
1 parent f2a7f86 commit 4072cec
Show file tree
Hide file tree
Showing 22 changed files with 242 additions and 187 deletions.
3 changes: 0 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ dependencies {

implementation("com.github.Kyant0:taglib:1.0.0-alpha22")

//TODO : use fetch instead of download manager
// implementation("com.github.tonyofrancis.Fetch:xfetch2:3.1.6")

testImplementation("org.jetbrains.kotlin:kotlin-reflect:1.9.24")
testImplementation("junit:junit:4.13.2")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class Downloader(
val folder = "Echo${parent?.title?.let { "/$it" } ?: ""}"

val id = when (val audio = media.audio) {
is Streamable.Audio.ByteStream -> TODO()
is Streamable.Audio.ByteStream -> throw Exception("Not Supported")
is Streamable.Audio.Channel -> TODO()
is Streamable.Audio.Http -> {
val request = audio.request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import androidx.media3.common.MediaItem
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.common.clients.AlbumClient
import dev.brahmkshatriya.echo.common.clients.ArtistClient
import dev.brahmkshatriya.echo.common.clients.EditPlayerListenerClient
import dev.brahmkshatriya.echo.common.clients.ExtensionClient
import dev.brahmkshatriya.echo.common.clients.HomeFeedClient
import dev.brahmkshatriya.echo.common.clients.LibraryClient
import dev.brahmkshatriya.echo.common.clients.PlaylistClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditorListenerClient
import dev.brahmkshatriya.echo.common.clients.RadioClient
import dev.brahmkshatriya.echo.common.clients.SearchClient
import dev.brahmkshatriya.echo.common.clients.TrackClient
Expand Down Expand Up @@ -47,7 +47,7 @@ import dev.brahmkshatriya.echo.utils.toJson

class OfflineExtension(val context: Context) : ExtensionClient, HomeFeedClient, TrackClient,
AlbumClient, ArtistClient, PlaylistClient, RadioClient, SearchClient, LibraryClient,
EditPlayerListenerClient {
PlaylistEditorListenerClient {

companion object {
val metadata = ExtensionMetadata(
Expand Down
69 changes: 0 additions & 69 deletions app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,77 +136,8 @@ class TestExtension : ExtensionClient, LoginClient.UsernamePassword, TrackClient
return PagedData.Single { emptyList() }
}

override suspend fun listEditablePlaylists(): List<Playlist> {
return listOf()
}

override suspend fun likeTrack(track: Track, liked: Boolean): Boolean {
println("likeTrack: ${track.title}, $liked")
return liked
}

override suspend fun createPlaylist(title: String, description: String?): Playlist {
return Playlist("", title, true)
}

override suspend fun deletePlaylist(playlist: Playlist) {}

override suspend fun editPlaylistMetadata(
playlist: Playlist,
title: String,
description: String?
) {
}

override suspend fun addTracksToPlaylist(
playlist: Playlist,
tracks: List<Track>,
index: Int,
new: List<Track>
) {
}

override suspend fun removeTracksFromPlaylist(
playlist: Playlist,
tracks: List<Track>,
indexes: List<Int>
) {
}

override suspend fun moveTrackInPlaylist(
playlist: Playlist,
tracks: List<Track>,
fromIndex: Int,
toIndex: Int
) {
}

override suspend fun loadPlaylist(playlist: Playlist) = playlist
override fun loadTracks(playlist: Playlist): PagedData<Track> = PagedData.Single {
listOf(
Track(
"track",
"Track",
emptyList(),
null,
null,
streamables = listOf(Streamable.audioVideo(video, 0))
)
)
}

override fun loadTracks(album: Album): PagedData<Track> {
return PagedData.Single { emptyList() }
}

override fun getMediaItems(playlist: Playlist): PagedData<MediaItemsContainer> =
PagedData.Single { listOf() }

override fun getMediaItems(album: Album): PagedData<MediaItemsContainer> {
return PagedData.Single { emptyList() }
}

override suspend fun loadAlbum(album: Album): Album {
return album
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModel
import androidx.paging.LoadState
import androidx.paging.PagingData
Expand Down Expand Up @@ -80,6 +82,10 @@ class MediaContainerAdapter(
}
}

suspend fun submit(pagingData: PagingData<MediaItemsContainer>?) {
saveState()
submitData(pagingData ?: PagingData.empty())
}

//Nested RecyclerView State Management

Expand All @@ -101,6 +107,13 @@ class MediaContainerAdapter(
stateViewModel.visibleScrollableViews.clear()
}

private fun saveScrollState(holder: Category, block: ((Category) -> Unit)? = null) {
val layoutManagerStates = stateViewModel.layoutManagerStates
layoutManagerStates[holder.bindingAdapterPosition] =
holder.layoutManager?.onSaveInstanceState()
block?.invoke(holder)
}

private fun saveState() {
stateViewModel.visibleScrollableViews.values.forEach { item ->
item.get()?.let { saveScrollState(it) }
Expand All @@ -110,24 +123,22 @@ class MediaContainerAdapter(

override fun onViewRecycled(holder: MediaContainerViewHolder) {
super.onViewRecycled(holder)
destroyLifeCycle(holder)
if (holder is Category) saveScrollState(holder) {
stateViewModel.visibleScrollableViews.remove(holder.bindingAdapterPosition)
}
}

private fun saveScrollState(holder: Category, block: ((Category) -> Unit)? = null) {
val layoutManagerStates = stateViewModel.layoutManagerStates
layoutManagerStates[holder.bindingAdapterPosition] =
holder.layoutManager?.onSaveInstanceState()
block?.invoke(holder)
}

suspend fun submit(pagingData: PagingData<MediaItemsContainer>?) {
saveState()
submitData(pagingData ?: PagingData.empty())
private fun destroyLifeCycle(holder: MediaContainerViewHolder) {
if (holder.isInitialized && holder.lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED))
holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}

override fun onBindViewHolder(holder: MediaContainerViewHolder, position: Int) {
destroyLifeCycle(holder)
holder.lifecycleRegistry = LifecycleRegistry(holder)
holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)

val items = getItem(position) ?: return
holder.transitionView.transitionName = (transition + items.id).hashCode().toString()
holder.bind(items)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope
import androidx.paging.PagingData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand All @@ -18,18 +21,20 @@ import dev.brahmkshatriya.echo.databinding.NewItemMediaBinding
import dev.brahmkshatriya.echo.databinding.NewItemTracksBinding
import dev.brahmkshatriya.echo.ui.adapter.MediaItemViewHolder.Companion.bind
import dev.brahmkshatriya.echo.ui.item.TrackAdapter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference

sealed class MediaContainerViewHolder(
itemView: View,
) : RecyclerView.ViewHolder(itemView) {
) : RecyclerView.ViewHolder(itemView), LifecycleOwner {
abstract fun bind(container: MediaItemsContainer)
open val clickView = itemView
abstract val transitionView: View

lateinit var lifecycleRegistry : LifecycleRegistry
val isInitialized get() = this::lifecycleRegistry.isInitialized
override val lifecycle get() = lifecycleRegistry

class Category(
val binding: NewItemCategoryBinding,
val viewModel: MediaContainerAdapter.StateViewModel,
Expand Down Expand Up @@ -159,10 +164,8 @@ sealed class MediaContainerViewHolder(
val binding: NewItemTracksBinding,
private val sharedPool: RecyclerView.RecycledViewPool,
private val clientId: String?,
val listener: MediaItemAdapter.Listener,
val scope: CoroutineScope,
) :
MediaContainerViewHolder(binding.root) {
val listener: MediaItemAdapter.Listener
) : MediaContainerViewHolder(binding.root) {

private val trackListener = object : TrackAdapter.Listener {
override fun onClick(list: List<Track>, position: Int, view: View) {
Expand All @@ -186,7 +189,7 @@ sealed class MediaContainerViewHolder(
binding.recyclerView.adapter = adapter
binding.recyclerView.setRecycledViewPool(sharedPool)
binding.more.isVisible = tracks.more != null
scope.launch { adapter.submit(PagingData.from(tracks.list.take(6))) }
lifecycleScope.launch { adapter.submit(PagingData.from(tracks.list.take(6))) }
}

val layoutManager get() = binding.recyclerView.layoutManager
Expand All @@ -201,14 +204,11 @@ sealed class MediaContainerViewHolder(
listener: MediaItemAdapter.Listener,
): MediaContainerViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
//TODO
val scope = CoroutineScope(Dispatchers.Main)
return MediaTracks(
NewItemTracksBinding.inflate(layoutInflater, parent, false),
sharedPool,
clientId,
listener,
scope
listener
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package dev.brahmkshatriya.echo.ui.editplaylist
import android.app.Application
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.brahmkshatriya.echo.common.clients.LibraryClient
import dev.brahmkshatriya.echo.common.clients.AlbumClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditClient
import dev.brahmkshatriya.echo.common.models.EchoMediaItem
import dev.brahmkshatriya.echo.common.models.Playlist
import dev.brahmkshatriya.echo.common.models.Track
Expand Down Expand Up @@ -33,12 +34,14 @@ class AddToPlaylistViewModel @Inject constructor(
override fun onInitialize() {
val extension = extensionListFlow.getExtension(clientId) ?: return
val client = extension.client
if (client !is LibraryClient) return
if (client !is PlaylistEditClient) return
viewModelScope.launch(Dispatchers.IO) {
tryWith(extension.info) {
when (val mediaItem = item) {
is EchoMediaItem.Lists.AlbumItem ->
is EchoMediaItem.Lists.AlbumItem -> {
if(client !is AlbumClient) return@tryWith
tracks.addAll(client.loadTracks(mediaItem.album).loadAll())
}
is EchoMediaItem.Lists.PlaylistItem ->
tracks.addAll(client.loadTracks(mediaItem.playlist).loadAll())
is EchoMediaItem.TrackItem -> tracks.add(mediaItem.track)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.common.clients.EditPlaylistCoverClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditCoverClient
import dev.brahmkshatriya.echo.common.models.Playlist
import dev.brahmkshatriya.echo.common.models.Track
import dev.brahmkshatriya.echo.databinding.FragmentEditPlaylistBinding
Expand Down Expand Up @@ -165,7 +165,7 @@ class EditPlaylistFragment : Fragment() {
observe(viewModel.extensionListFlow) {
viewModel.load(clientId, playlist)
val client = viewModel.extensionListFlow.getExtension(clientId)?.client
header.showCover(client is EditPlaylistCoverClient)
header.showCover(client is PlaylistEditCoverClient)
}

binding.toolbar.setOnMenuItemClickListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import android.content.Context
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.brahmkshatriya.echo.R
import dev.brahmkshatriya.echo.common.clients.EditPlayerListenerClient
import dev.brahmkshatriya.echo.common.clients.EditPlaylistCoverClient
import dev.brahmkshatriya.echo.common.clients.LibraryClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditCoverClient
import dev.brahmkshatriya.echo.common.clients.PlaylistEditorListenerClient
import dev.brahmkshatriya.echo.common.models.Playlist
import dev.brahmkshatriya.echo.common.models.Track
import dev.brahmkshatriya.echo.plugger.MusicExtension
Expand Down Expand Up @@ -39,7 +39,7 @@ class EditPlaylistViewModel @Inject constructor(
loadingFlow.emit(true)
val extension = extensionListFlow.getExtension(clientId)
val client = extension?.client
if (client !is LibraryClient) return@launch
if (client !is PlaylistEditClient) return@launch
withContext(Dispatchers.IO) {
originalList =
tryWith(extension.info) { client.loadTracks(playlist).loadAll() } ?: emptyList()
Expand All @@ -50,8 +50,8 @@ class EditPlaylistViewModel @Inject constructor(
}
}

private suspend fun libraryClient(
clientId: String, block: suspend (client: LibraryClient) -> Unit
private suspend fun playlistEditClient(
clientId: String, block: suspend (client: PlaylistEditClient) -> Unit
) {
client(clientId, block)
}
Expand All @@ -68,15 +68,15 @@ class EditPlaylistViewModel @Inject constructor(

fun changeMetadata(clientId: String, playlist: Playlist, title: String, description: String?) {
viewModelScope.launch {
libraryClient(clientId) {
playlistEditClient(clientId) {
it.editPlaylistMetadata(playlist, title, description)
}
}
}

fun changeCover(clientId: String, playlist: Playlist, file: File?) {
viewModelScope.launch {
client<EditPlaylistCoverClient>(clientId) {
client<PlaylistEditCoverClient>(clientId) {
it.editPlaylistCover(playlist, file)
}
}
Expand Down Expand Up @@ -133,10 +133,10 @@ class EditPlaylistViewModel @Inject constructor(
val tracks = originalList.toMutableList()
performedActions.emit(tracks to null)
println("Actions Size : ${newActions.size}")
client<EditPlayerListenerClient>(clientId) {
client<PlaylistEditorListenerClient>(clientId) {
it.onEnterPlaylistEditor(playlist, tracks)
}
libraryClient(clientId) { client ->
playlistEditClient(clientId) { client ->
newActions.forEach { action ->
println("new action : $action")
performedActions.emit(tracks to action)
Expand All @@ -161,7 +161,7 @@ class EditPlaylistViewModel @Inject constructor(
}
}
performedActions.emit(tracks to null)
client<EditPlayerListenerClient>(clientId) {
client<PlaylistEditorListenerClient>(clientId) {
it.onExitPlaylistEditor(playlist, tracks)
}
}
Expand Down Expand Up @@ -193,7 +193,7 @@ class EditPlaylistViewModel @Inject constructor(
) {
val extension = extensionListFlow.getExtension(clientId) ?: return
val client = extension.client
if (client !is LibraryClient) return
if (client !is PlaylistEditClient) return
viewModelScope.launch(Dispatchers.IO) {
tryWith(extension.info) {
println("deleting playlist : ${playlist.title}")
Expand All @@ -214,8 +214,8 @@ class EditPlaylistViewModel @Inject constructor(
) = run {
val extension = extensionListFlow.getExtension(clientId) ?: return
val client = extension.client
if (client !is LibraryClient) return
val listener = client as? EditPlayerListenerClient
if (client !is PlaylistEditClient) return
val listener = client as? PlaylistEditorListenerClient
withContext(Dispatchers.IO) {
playlists.forEach { playlist ->
tryWith(extension.info) {
Expand Down
Loading

0 comments on commit 4072cec

Please sign in to comment.