diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 31f9fc84..bb36a5a7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -71,6 +71,7 @@ dependencies { implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.palette:palette-ktx:1.0.0") + implementation("com.google.android.flexbox:flexbox:3.0.0") implementation("com.google.android.material:material:1.12.0") implementation("com.google.dagger:hilt-android:2.48.1") diff --git a/app/src/main/java/dev/brahmkshatriya/echo/offline/OfflineExtension.kt b/app/src/main/java/dev/brahmkshatriya/echo/offline/OfflineExtension.kt index 0895c397..9bb07bcd 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/offline/OfflineExtension.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/offline/OfflineExtension.kt @@ -13,6 +13,7 @@ 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 +import dev.brahmkshatriya.echo.common.clients.TrackLikeClient import dev.brahmkshatriya.echo.common.helpers.PagedData import dev.brahmkshatriya.echo.common.models.Album import dev.brahmkshatriya.echo.common.models.Artist @@ -47,7 +48,7 @@ import dev.brahmkshatriya.echo.utils.toJson class OfflineExtension(val context: Context) : ExtensionClient, HomeFeedClient, TrackClient, AlbumClient, ArtistClient, PlaylistClient, RadioClient, SearchClient, LibraryClient, - PlaylistEditorListenerClient { + TrackLikeClient, PlaylistEditorListenerClient { companion object { val metadata = ExtensionMetadata( @@ -355,15 +356,14 @@ class OfflineExtension(val context: Context) : ExtensionClient, HomeFeedClient, override suspend fun listEditablePlaylists() = library.playlistList.map { it.toPlaylist() } - override suspend fun likeTrack(track: Track, liked: Boolean): Boolean { + override suspend fun likeTrack(track: Track, isLiked: Boolean) { val playlist = library.likedPlaylist.id - if (liked) context.addSongToPlaylist(playlist, track.id.toLong(), 0) + if (isLiked) context.addSongToPlaylist(playlist, track.id.toLong(), 0) else { val index = library.likedPlaylist.songList.indexOfFirst { it.mediaId == track.id } context.removeSongFromPlaylist(playlist, index) } library = MediaStoreUtils.getAllSongs(context) - return liked } override suspend fun createPlaylist(title: String, description: String?): Playlist { diff --git a/app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt b/app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt index 8c03dc55..110cab4c 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt @@ -2,7 +2,6 @@ package dev.brahmkshatriya.echo.offline 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.LoginClient import dev.brahmkshatriya.echo.common.clients.RadioClient import dev.brahmkshatriya.echo.common.clients.TrackClient @@ -11,9 +10,9 @@ import dev.brahmkshatriya.echo.common.models.Album import dev.brahmkshatriya.echo.common.models.Artist import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem -import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.common.models.Playlist import dev.brahmkshatriya.echo.common.models.Radio +import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.common.models.Streamable import dev.brahmkshatriya.echo.common.models.Streamable.Audio.Companion.toAudio import dev.brahmkshatriya.echo.common.models.Streamable.Media.Companion.toAudioVideoMedia @@ -29,7 +28,7 @@ import dev.brahmkshatriya.echo.plugger.ExtensionMetadata import dev.brahmkshatriya.echo.plugger.ImportType class TestExtension : ExtensionClient, LoginClient.UsernamePassword, TrackClient, HomeFeedClient, - RadioClient, LibraryClient { + RadioClient { companion object { val metadata = ExtensionMetadata( @@ -129,15 +128,4 @@ class TestExtension : ExtensionClient, LoginClient.UsernamePassword, TrackClient override suspend fun radio(artist: Artist) = radio override suspend fun radio(user: User) = radio override suspend fun radio(playlist: Playlist) = radio - - override suspend fun getLibraryTabs() = emptyList() - - override fun getLibraryFeed(tab: Tab?): PagedData { - return PagedData.Single { emptyList() } - } - - override suspend fun likeTrack(track: Track, liked: Boolean): Boolean { - println("likeTrack: ${track.title}, $liked") - return liked - } } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt b/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt index 538e01f6..2ca24d3f 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/playback/PlayerSessionCallback.kt @@ -22,9 +22,9 @@ import androidx.media3.session.SessionResult.RESULT_SUCCESS import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import dev.brahmkshatriya.echo.R -import dev.brahmkshatriya.echo.common.clients.LibraryClient import dev.brahmkshatriya.echo.common.clients.SearchClient import dev.brahmkshatriya.echo.common.clients.TrackClient +import dev.brahmkshatriya.echo.common.clients.TrackLikeClient import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem import dev.brahmkshatriya.echo.common.models.Shelf @@ -125,16 +125,19 @@ class PlayerSessionCallback( val errorIO = SessionResult(SessionError.ERROR_IO) val item = session.player.currentMediaItem ?: return@future errorIO val client = extensionList.getExtension(item.clientId)?.client ?: return@future errorIO - if (client !is LibraryClient) return@future errorIO + if (client !is TrackLikeClient) return@future errorIO val track = item.track - val liked = withContext(Dispatchers.IO) { - runCatching { client.likeTrack(track, rating.isThumbsUp) } + withContext(Dispatchers.IO) { + runCatching { + client.likeTrack(track, rating.isThumbsUp) + } }.getOrElse { return@future SessionResult( SessionError.ERROR_UNKNOWN, Bundle().apply { putSerialized("error", it.toExceptionDetails(context)) } ) } + val liked = rating.isThumbsUp val newItem = item.run { buildUpon().setMediaMetadata( mediaMetadata.buildUpon().setUserRating(ThumbRating(liked)).build() diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryAdapter.kt deleted file mode 100644 index 49dc64f6..00000000 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryAdapter.kt +++ /dev/null @@ -1,45 +0,0 @@ -package dev.brahmkshatriya.echo.ui.adapter - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams -import androidx.recyclerview.widget.RecyclerView -import dev.brahmkshatriya.echo.common.models.Shelf -import dev.brahmkshatriya.echo.databinding.ItemShelfCategoryBinding - -class CategoryAdapter( - private val clientId: String, - private val transition: String, - private val listener: ShelfAdapter.Listener, - private val list: List -) : RecyclerView.Adapter() { - - inner class ViewHolder(val binding: ItemShelfCategoryBinding) : - RecyclerView.ViewHolder(binding.root) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(parent.context) - return ViewHolder(ItemShelfCategoryBinding.inflate(inflater, parent, false)) - } - - override fun getItemCount() = list.size - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = list[position] - val binding = holder.binding - val root = binding.root - root.transitionName = (transition + item.id).hashCode().toString() - root.updateLayoutParams { width = ViewGroup.LayoutParams.WRAP_CONTENT } - binding.title.text = item.title - binding.subtitle.text = item.subtitle - binding.subtitle.isVisible = item.subtitle.isNullOrBlank().not() - - holder.itemView.setOnClickListener { - listener.onClick(clientId, item, root) - } - holder.itemView.setOnLongClickListener { - listener.onLongClick(clientId, item, root) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryViewHolder.kt new file mode 100644 index 00000000..0f22f740 --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/CategoryViewHolder.kt @@ -0,0 +1,51 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import com.google.android.material.R +import com.google.android.material.color.MaterialColors +import dev.brahmkshatriya.echo.common.models.Shelf +import dev.brahmkshatriya.echo.databinding.ItemShelfCategoryBinding + +class CategoryViewHolder( + val listener: ShelfAdapter.Listener, + val clientId: String, + val binding: ItemShelfCategoryBinding +) : ShelfListItemViewHolder(binding.root) { + + companion object { + fun create( + parent: ViewGroup, + listener: ShelfAdapter.Listener, + clientId: String + ): CategoryViewHolder { + val inflater = LayoutInflater.from(parent.context) + return CategoryViewHolder( + listener, + clientId, + ItemShelfCategoryBinding.inflate(inflater, parent, false) + ) + } + } + + override fun bind(item:Any) { + if (item !is Shelf.Category) return + val root = binding.root + root.setCardBackgroundColor(MaterialColors.getColor(root, R.attr.colorSurfaceVariant)) + root.updateLayoutParams { width = ViewGroup.LayoutParams.WRAP_CONTENT } + binding.title.text = item.title + binding.subtitle.text = item.subtitle + binding.subtitle.isVisible = item.subtitle.isNullOrBlank().not() + + root.setOnClickListener { + listener.onClick(clientId, item, root) + } + root.setOnLongClickListener { + listener.onLongClick(clientId, item, root) + } + } + + override val transitionView = binding.root +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/GridViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/GridViewHolder.kt new file mode 100644 index 00000000..10cbd3bb --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/GridViewHolder.kt @@ -0,0 +1,86 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import dev.brahmkshatriya.echo.common.models.EchoMediaItem +import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem +import dev.brahmkshatriya.echo.common.models.Shelf +import dev.brahmkshatriya.echo.common.models.Track +import dev.brahmkshatriya.echo.databinding.ItemShelfMediaGridBinding +import dev.brahmkshatriya.echo.databinding.NewItemMediaTitleBinding +import dev.brahmkshatriya.echo.ui.adapter.MediaItemViewHolder.Companion.bind +import dev.brahmkshatriya.echo.ui.adapter.MediaItemViewHolder.Companion.icon +import dev.brahmkshatriya.echo.ui.adapter.MediaItemViewHolder.Companion.placeHolder +import dev.brahmkshatriya.echo.utils.loadInto + +class GridViewHolder( + val listener: ShelfAdapter.Listener, + val clientId: String, + val binding: ItemShelfMediaGridBinding +) : ShelfListItemViewHolder(binding.root) { + + @SuppressLint("SetTextI18n") + override fun bind(item:Any) { + val media = when (item) { + is EchoMediaItem -> { + binding.iconContainer.isVisible = true + binding.icon.setImageResource(item.icon()) + binding.icon.isVisible = true + binding.count.isVisible = false + binding.root.setOnClickListener { + listener.onClick(clientId, item, it) + } + binding.root.setOnLongClickListener { + listener.onLongClick(clientId, item, it) + } + item + } + + is Track -> { + val pos = bindingAdapterPosition + val shelf = shelf as Shelf.Lists.Tracks + val isNumbered = shelf.isNumbered + val tracks = shelf.list + val media = item.toMediaItem() + binding.iconContainer.isVisible = isNumbered + binding.icon.isVisible = false + binding.count.isVisible = isNumbered + binding.count.text = (pos + 1).toString() + binding.root.setOnClickListener { + if (isNumbered) + listener.onClick(clientId, null, tracks, pos, it) + else listener.onClick(clientId, media, it) + } + binding.root.setOnLongClickListener { + if (isNumbered) listener.onLongClick(clientId, null, tracks, pos, it) + else listener.onLongClick(clientId, media, it) + } + media + } + + else -> return + } + val titleBinding = NewItemMediaTitleBinding.bind(binding.root) + titleBinding.bind(media) + media.cover.loadInto(binding.imageView, media.placeHolder()) + } + + override val transitionView = binding.root + + companion object { + fun create( + parent: ViewGroup, + listener: ShelfAdapter.Listener, + clientId: String + ): GridViewHolder { + val inflater = LayoutInflater.from(parent.context) + return GridViewHolder( + listener, + clientId, + ItemShelfMediaGridBinding.inflate(inflater, parent, false) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/LifeCycleListAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/LifeCycleListAdapter.kt new file mode 100644 index 00000000..3d6cf875 --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/LifeCycleListAdapter.kt @@ -0,0 +1,54 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.view.View +import android.view.ViewGroup +import androidx.annotation.CallSuper +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView + +abstract class LifeCycleListAdapter>( + diffCallback: DiffUtil.ItemCallback +) : ListAdapter(diffCallback) { + + abstract fun createHolder(parent: ViewGroup, viewType: Int): Holder + + @CallSuper + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): Holder { + val holder = createHolder(parent, viewType) + holder.lifecycleRegistry = LifecycleRegistry(holder) + return holder + } + + @CallSuper + override fun onBindViewHolder(holder: Holder, position: Int) { + destroyLifeCycle(holder) + holder.lifecycleRegistry = LifecycleRegistry(holder) + holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) + holder.bind(getItem(position)) + } + + private fun destroyLifeCycle(holder: Holder) { + if (holder.lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED)) + holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) + } + + @CallSuper + override fun onViewRecycled(holder: Holder) { + destroyLifeCycle(holder) + } + + abstract class Holder(itemView: View) : RecyclerView.ViewHolder(itemView), LifecycleOwner { + abstract fun bind(item: T) + lateinit var lifecycleRegistry: LifecycleRegistry + override val lifecycle get() = lifecycleRegistry + } +} + + diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemAdapter.kt deleted file mode 100644 index 06a92e7c..00000000 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemAdapter.kt +++ /dev/null @@ -1,49 +0,0 @@ -package dev.brahmkshatriya.echo.ui.adapter - -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import dev.brahmkshatriya.echo.common.models.EchoMediaItem - -class MediaItemAdapter( - private val clientId: String, - private val transition: String, - private val listener: Listener, - private val list: List -) : RecyclerView.Adapter() { - - interface Listener { - fun onClick(clientId: String, item: EchoMediaItem, transitionView: View?) - fun onLongClick(clientId: String, item: EchoMediaItem, transitionView: View?): Boolean - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaItemViewHolder { - return when (viewType) { - 0 -> MediaItemViewHolder.Lists.create(parent) - 1 -> MediaItemViewHolder.Profile.create(parent) - 2 -> MediaItemViewHolder.Track.create(parent) - else -> throw IllegalArgumentException("Invalid view type") - } - } - - override fun getItemViewType(position: Int) = when (list[position]) { - is EchoMediaItem.Lists.RadioItem -> 2 - is EchoMediaItem.Lists -> 0 - is EchoMediaItem.Profile -> 1 - is EchoMediaItem.TrackItem -> 2 - } - - override fun getItemCount() = list.size - - override fun onBindViewHolder(holder: MediaItemViewHolder, position: Int) { - val item = list[position] - holder.transitionView.transitionName = (transition + item.id).hashCode().toString() - holder.bind(item) - holder.itemView.setOnClickListener { - listener.onClick(clientId, item, holder.transitionView) - } - holder.itemView.setOnLongClickListener { - listener.onLongClick(clientId, item, holder.transitionView) - } - } -} diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemSelectableAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemSelectableAdapter.kt index 1e949e30..2486e676 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemSelectableAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemSelectableAdapter.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.doOnLayout import androidx.core.view.isVisible -import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.DiffUtil import dev.brahmkshatriya.echo.R import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.databinding.ItemMediaSelectableBinding @@ -16,43 +16,50 @@ import kotlin.math.roundToInt class MediaItemSelectableAdapter( val listener: Listener -) : RecyclerView.Adapter() { +) : LifeCycleListAdapter, MediaItemSelectableAdapter.ViewHolder>( + DiffCallback +) { + + object DiffCallback : DiffUtil.ItemCallback>() { + + override fun areItemsTheSame( + oldItem: Pair, newItem: Pair + ) = oldItem.first.id == newItem.first.id + + + override fun areContentsTheSame( + oldItem: Pair, newItem: Pair + ) = oldItem == newItem + + } fun interface Listener { fun onItemSelected(selected: Boolean, item: EchoMediaItem) } - class ViewHolder(val binding: ItemMediaSelectableBinding) : - RecyclerView.ViewHolder(binding.root) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + override fun createHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) - val binding = ItemMediaSelectableBinding.inflate(inflater, parent, false) - return ViewHolder(binding) + return ViewHolder(ItemMediaSelectableBinding.inflate(inflater, parent, false)) } - private val items = mutableListOf() - private val selectedItems = mutableListOf() - override fun getItemCount() = items.size - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = items[position] - val binding = holder.binding - binding.cover.bind(item) - binding.title.text = item.title - binding.selected.isVisible = selectedItems.contains(item) - binding.root.setOnClickListener { - val selected = !binding.selected.isVisible - binding.selected.isVisible = selected - listener.onItemSelected(selected, item) + inner class ViewHolder(val binding: ItemMediaSelectableBinding) : + Holder>(binding.root) { + override fun bind(item: Pair) { + val mediaItem = item.first + binding.cover.bind(mediaItem) + binding.title.text = mediaItem.title + binding.selected.isVisible = item.second + binding.root.setOnClickListener { + val selected = !binding.selected.isVisible + binding.selected.isVisible = selected + listener.onItemSelected(selected, mediaItem) + } } } @SuppressLint("NotifyDataSetChanged") fun setItems(items: List, selectedItems: List) { - this.items.clear() - this.items.addAll(items) - this.selectedItems.clear() - this.selectedItems.addAll(selectedItems) + submitList(items.map { it to selectedItems.contains(it) }) notifyDataSetChanged() } @@ -68,4 +75,5 @@ class MediaItemSelectableAdapter( requestLayout() } } + } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemViewHolder.kt index d9c25a2c..a22499a9 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemViewHolder.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/MediaItemViewHolder.kt @@ -4,7 +4,6 @@ import android.view.LayoutInflater import android.view.View 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 import dev.brahmkshatriya.echo.databinding.ItemListsCoverBinding @@ -17,12 +16,31 @@ import dev.brahmkshatriya.echo.databinding.NewItemMediaTrackBinding import dev.brahmkshatriya.echo.utils.loadInto import dev.brahmkshatriya.echo.utils.loadWith -sealed class MediaItemViewHolder(itemView: View) : - RecyclerView.ViewHolder(itemView) { +sealed class MediaItemViewHolder( + val listener: ShelfAdapter.Listener, + val clientId: String, + itemView: View +) : ShelfListItemViewHolder(itemView) { abstract fun bind(item: EchoMediaItem) - abstract val transitionView: View - class Lists(val binding: NewItemMediaListsBinding) : MediaItemViewHolder(binding.root) { + override fun bind(item: Any) { + if (item !is EchoMediaItem) return + bind(item) + itemView.setOnClickListener { + listener.onClick(clientId, item, transitionView) + } + itemView.setOnLongClickListener { + listener.onLongClick(clientId, item, transitionView) + } + } + + abstract override val transitionView: View + + class Lists( + listener: ShelfAdapter.Listener, + clientId: String, + val binding: NewItemMediaListsBinding + ) : MediaItemViewHolder(listener, clientId, binding.root) { private val titleBinding = NewItemMediaTitleBinding.bind(binding.root) override val transitionView: View @@ -36,17 +54,24 @@ sealed class MediaItemViewHolder(itemView: View) : companion object { fun create( + listener: ShelfAdapter.Listener, + clientId: String, parent: ViewGroup ): MediaItemViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return Lists( + listener, clientId, NewItemMediaListsBinding.inflate(layoutInflater, parent, false) ) } } } - class Track(val binding: NewItemMediaTrackBinding) : MediaItemViewHolder(binding.root) { + class Track( + listener: ShelfAdapter.Listener, + clientId: String, + val binding: NewItemMediaTrackBinding + ) : MediaItemViewHolder(listener, clientId, binding.root) { private val titleBinding = NewItemMediaTitleBinding.bind(binding.root) override val transitionView: View @@ -59,17 +84,24 @@ sealed class MediaItemViewHolder(itemView: View) : companion object { fun create( + listener: ShelfAdapter.Listener, + clientId: String, parent: ViewGroup ): MediaItemViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return Track( + listener, clientId, NewItemMediaTrackBinding.inflate(layoutInflater, parent, false) ) } } } - class Profile(val binding: NewItemMediaProfileBinding) : MediaItemViewHolder(binding.root) { + class Profile( + listener: ShelfAdapter.Listener, + clientId: String, + val binding: NewItemMediaProfileBinding + ) : MediaItemViewHolder(listener, clientId, binding.root) { override val transitionView: View get() = binding.cover.root @@ -83,10 +115,13 @@ sealed class MediaItemViewHolder(itemView: View) : companion object { fun create( + listener: ShelfAdapter.Listener, + clientId: String, parent: ViewGroup ): MediaItemViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return Profile( + listener, clientId, NewItemMediaProfileBinding.inflate(layoutInflater, parent, false) ) } @@ -144,5 +179,25 @@ sealed class MediaItemViewHolder(itemView: View) : view1.isVisible = tracks > 1 view2.isVisible = tracks > 2 } + + fun create( + viewType: Int, parent: ViewGroup, + listener: ShelfAdapter.Listener, + clientId: String, + ): ShelfListItemViewHolder { + return when (viewType) { + 0 -> Lists.create(listener, clientId, parent) + 1 -> Profile.create(listener, clientId, parent) + 2 -> Track.create(listener, clientId, parent) + else -> throw IllegalArgumentException("Invalid view type") + } + } + + fun getViewType(item: EchoMediaItem) = when (item) { + is EchoMediaItem.Lists.RadioItem -> 2 + is EchoMediaItem.Lists -> 0 + is EchoMediaItem.Profile -> 1 + is EchoMediaItem.TrackItem -> 2 + } } } diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlaylistAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/PlaylistAdapter.kt similarity index 85% rename from app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlaylistAdapter.kt rename to app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/PlaylistAdapter.kt index 25892de6..560f8410 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlaylistAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/PlaylistAdapter.kt @@ -1,4 +1,4 @@ -package dev.brahmkshatriya.echo.ui.player +package dev.brahmkshatriya.echo.ui.adapter import android.annotation.SuppressLint import android.view.LayoutInflater @@ -19,7 +19,7 @@ import dev.brahmkshatriya.echo.utils.toTimeString class PlaylistAdapter( private val callback: Callback, private val inactive: Boolean = false -) : LifeCycleListAdapter, ItemPlaylistItemBinding>(DiffCallback) { +) : LifeCycleListAdapter, PlaylistAdapter.ViewHolder>(DiffCallback) { object DiffCallback : DiffUtil.ItemCallback>() { override fun areItemsTheSame( @@ -40,13 +40,20 @@ class PlaylistAdapter( open fun onDragHandleTouched(viewHolder: RecyclerView.ViewHolder) {} } - override fun inflateCallback( - inflater: LayoutInflater, - container: ViewGroup? - ) = ItemPlaylistItemBinding.inflate(inflater, container, false) + inner class ViewHolder(val binding: ItemPlaylistItemBinding) : + Holder>(binding.root) { + override fun bind(item: Pair) { + onBind(bindingAdapterPosition) + } + } + + override fun createHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return ViewHolder(ItemPlaylistItemBinding.inflate(inflater, parent, false)) + } @SuppressLint("ClickableViewAccessibility") - override fun Holder, ItemPlaylistItemBinding>.onBind(position: Int) { + fun ViewHolder.onBind(position: Int) { val (isCurrent, item) = getItem(position) val track = item.track diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfAdapter.kt index cdb77078..fb13b18f 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfAdapter.kt @@ -14,6 +14,7 @@ import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView.RecycledViewPool +import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.plugger.ExtensionInfo import dev.brahmkshatriya.echo.ui.adapter.ShelfViewHolder.Category @@ -31,9 +32,12 @@ class ShelfAdapter( val listener: Listener = getListener(fragment) ) : PagingDataAdapter(DiffCallback) { - interface Listener : MediaItemAdapter.Listener, TrackAdapter.Listener { + interface Listener : TrackAdapter.Listener { fun onClick(clientId: String, shelf: Shelf, transitionView: View) fun onLongClick(clientId: String, shelf: Shelf, transitionView: View): Boolean + fun onClick(clientId: String, item: EchoMediaItem, transitionView: View?) + fun onLongClick(clientId: String, item: EchoMediaItem, transitionView: View?): Boolean + fun onShuffleClick(clientId: String, shelf: Shelf.Lists.Tracks) } companion object { @@ -120,7 +124,6 @@ class ShelfAdapter( } override fun onViewRecycled(holder: ShelfViewHolder) { - super.onViewRecycled(holder) destroyLifeCycle(holder) if (holder is Lists) saveScrollState(holder) { stateViewModel.visibleScrollableViews.remove(holder.bindingAdapterPosition) @@ -128,7 +131,7 @@ class ShelfAdapter( } private fun destroyLifeCycle(holder: ShelfViewHolder) { - if (holder.isInitialized && holder.lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED)) + if (holder.lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED)) holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) } @@ -160,11 +163,15 @@ class ShelfAdapter( } private val sharedPool = RecycledViewPool() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { - 0 -> Lists.create(parent, stateViewModel, sharedPool, extensionInfo.id, listener) - 1 -> Media.create(parent, extensionInfo.id, listener) - 2 -> Category.create(parent) - else -> throw IllegalArgumentException("Unknown viewType: $viewType") + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShelfViewHolder { + val holder = when (viewType) { + 0 -> Lists.create(parent, stateViewModel, sharedPool, extensionInfo.id, listener) + 1 -> Media.create(parent, extensionInfo.id, listener) + 2 -> Category.create(parent) + else -> throw IllegalArgumentException("Unknown viewType: $viewType") + } + holder.lifecycleRegistry = LifecycleRegistry(holder) + return holder } } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfClickListener.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfClickListener.kt index d5aab6b6..eae58cf5 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfClickListener.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfClickListener.kt @@ -112,6 +112,13 @@ open class ShelfClickListener( return onLongClick(clientId, track.toMediaItem(), view) } + override fun onShuffleClick(clientId: String, shelf: Shelf.Lists.Tracks) { + withClient(clientId) { + it.play(clientId, null, shelf.list) + it.withBrowser { browser -> browser.shuffleModeEnabled = true } + } + } + override fun onLongClick( clientId: String, item: EchoMediaItem, transitionView: View? ): Boolean { diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewAdapter.kt new file mode 100644 index 00000000..c471fb8a --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewAdapter.kt @@ -0,0 +1,67 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.annotation.SuppressLint +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import dev.brahmkshatriya.echo.common.models.EchoMediaItem +import dev.brahmkshatriya.echo.common.models.Shelf +import dev.brahmkshatriya.echo.common.models.Track + +class ShelfListItemViewAdapter( + private val clientId: String, + private val transition: String, + private val listener: ShelfAdapter.Listener, + val shelf: Shelf.Lists<*> +) : LifeCycleListAdapter(DiffCallback) { + + init { + if (shelf !is Shelf.Lists.Tracks) submitList(shelf.list) + else ShowButtonViewHolder.initialize(this, shelf) + } + + object DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean { + return when { + oldItem is EchoMediaItem && newItem is EchoMediaItem -> oldItem.sameAs(newItem) + oldItem is Shelf.Category && newItem is Shelf.Category -> oldItem.sameAs(newItem) + oldItem is Track && newItem is Track -> oldItem.id == newItem.id + else -> false + } + } + + @SuppressLint("DiffUtilEquals") + override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean { + return oldItem == newItem + } + + } + + override fun createHolder(parent: ViewGroup, viewType: Int): ShelfListItemViewHolder { + return when (viewType) { + -1 -> GridViewHolder.create(parent, listener, clientId) + 3 -> CategoryViewHolder.create(parent, listener, clientId) + 4 -> TrackViewHolder.create(parent, listener, clientId, null) + 5 -> ShowButtonViewHolder.create(parent) + else -> MediaItemViewHolder.create(viewType, parent, listener, clientId) + } + } + + override fun getItemViewType(position: Int): Int { + val grid = shelf.type == Shelf.Lists.Type.Grid + return when (val item = getItem(position)) { + is EchoMediaItem -> if (!grid) MediaItemViewHolder.getViewType(item) else -1 + is Shelf.Category -> 3 + is Track -> if (!grid) 4 else -1 + is Boolean -> 5 + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: ShelfListItemViewHolder, position: Int) { + val item = getItem(position) ?: return + holder.transitionView.transitionName = (transition + item.hashCode()).hashCode().toString() + holder.adapter = this + holder.shelf = shelf + super.onBindViewHolder(holder, position) + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewHolder.kt new file mode 100644 index 00000000..74edce1b --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfListItemViewHolder.kt @@ -0,0 +1,11 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.view.View +import dev.brahmkshatriya.echo.common.models.Shelf + +abstract class ShelfListItemViewHolder(itemView: View) : + LifeCycleListAdapter.Holder(itemView) { + abstract val transitionView: View + lateinit var adapter: ShelfListItemViewAdapter + lateinit var shelf: Shelf.Lists<*> +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfViewHolder.kt index ea715c40..8fe44b98 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfViewHolder.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShelfViewHolder.kt @@ -5,73 +5,81 @@ 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.core.view.updatePaddingRelative +import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.flexbox.FlexDirection +import com.google.android.flexbox.FlexboxLayoutManager import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.databinding.ItemShelfCategoryBinding import dev.brahmkshatriya.echo.databinding.ItemShelfListsBinding import dev.brahmkshatriya.echo.databinding.ItemShelfMediaBinding import dev.brahmkshatriya.echo.ui.adapter.MediaItemViewHolder.Companion.bind -import dev.brahmkshatriya.echo.ui.item.TrackAdapter -import kotlinx.coroutines.launch +import dev.brahmkshatriya.echo.utils.dpToPx import java.lang.ref.WeakReference sealed class ShelfViewHolder( itemView: View, -) : RecyclerView.ViewHolder(itemView), LifecycleOwner { - abstract fun bind(container: Shelf) +) : LifeCycleListAdapter.Holder(itemView) { + open val clickView = itemView abstract val transitionView: View - lateinit var lifecycleRegistry: LifecycleRegistry - val isInitialized get() = this::lifecycleRegistry.isInitialized - override val lifecycle get() = lifecycleRegistry - class Lists( val binding: ItemShelfListsBinding, val viewModel: ShelfAdapter.StateViewModel, private val sharedPool: RecyclerView.RecycledViewPool, private val clientId: String, val listener: ShelfAdapter.Listener, - ) : - ShelfViewHolder(binding.root) { - override fun bind(container: Shelf) { - val lists = container as Shelf.Lists<*> - binding.title.text = lists.title - binding.subtitle.text = lists.subtitle - binding.subtitle.isVisible = lists.subtitle.isNullOrBlank().not() - binding.more.isVisible = lists.more != null + ) : ShelfViewHolder(binding.root) { + + override fun bind(item: Shelf) { + item as Shelf.Lists<*> + + binding.title.text = item.title + binding.subtitle.text = item.subtitle + binding.subtitle.isVisible = item.subtitle.isNullOrBlank().not() + binding.more.isVisible = item.more != null + binding.shuffle.isVisible = item is Shelf.Lists.Tracks binding.recyclerView.setRecycledViewPool(sharedPool) val position = bindingAdapterPosition - val transition = transitionView.transitionName + lists.id - binding.recyclerView.adapter = when (lists) { - is Shelf.Lists.Categories -> - CategoryAdapter(clientId, transition, listener, lists.list) - - is Shelf.Lists.Items -> - MediaItemAdapter(clientId, transition, listener, lists.list) - - is Shelf.Lists.Tracks -> { - val adapter = TrackAdapter(clientId, transition, listener) - lifecycleScope.launch { adapter.submit(PagingData.from(lists.list)) } - adapter + val transition = transitionView.transitionName + item.id + val context = binding.root.context + val layoutManager = when (item.type) { + Shelf.Lists.Type.Grid -> { + FlexboxLayoutManager(context).apply { + flexDirection = FlexDirection.COLUMN + } } - } + Shelf.Lists.Type.Linear -> when (item) { + is Shelf.Lists.Tracks -> { + binding.recyclerView.updatePaddingRelative(start = 0, end = 0) + binding.shuffle.setOnClickListener { + listener.onShuffleClick(clientId, item) + } + LinearLayoutManager(context, RecyclerView.VERTICAL, false) + } + + else -> { + val padding = 16.dpToPx(context) + binding.recyclerView.updatePaddingRelative(start = padding, end = padding) + LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) + } + } + } - val layoutManager = binding.recyclerView.layoutManager as RecyclerView.LayoutManager layoutManager.apply { val state: Parcelable? = viewModel.layoutManagerStates[position] if (state != null) onRestoreInstanceState(state) else scrollToPosition(0) } viewModel.visibleScrollableViews[position] = WeakReference(this) - + val adapter = ShelfListItemViewAdapter(clientId, transition, listener, item) + binding.recyclerView.adapter = adapter + binding.recyclerView.layoutManager = layoutManager } val layoutManager get() = binding.recyclerView.layoutManager @@ -101,11 +109,11 @@ sealed class ShelfViewHolder( class Category( val binding: ItemShelfCategoryBinding ) : ShelfViewHolder(binding.root) { - override fun bind(container: Shelf) { - container as Shelf.Category - binding.title.text = container.title - binding.subtitle.text = container.subtitle - binding.subtitle.isVisible = container.subtitle.isNullOrBlank().not() + override fun bind(item: Shelf) { + item as Shelf.Category + binding.title.text = item.title + binding.subtitle.text = item.subtitle + binding.subtitle.isVisible = item.subtitle.isNullOrBlank().not() } override val transitionView = binding.root @@ -124,12 +132,14 @@ sealed class ShelfViewHolder( class Media( val binding: ItemShelfMediaBinding, private val clientId: String, - val listener: MediaItemAdapter.Listener, + val listener: ShelfAdapter.Listener, ) : ShelfViewHolder(binding.root) { - override fun bind(container: Shelf) { - val item = (container as? Shelf.Item)?.media ?: return - binding.bind(item) - binding.more.setOnClickListener { listener.onLongClick(clientId, item, transitionView) } + override fun bind(item: Shelf) { + val media = (item as? Shelf.Item)?.media ?: return + binding.bind(media) + binding.more.setOnClickListener { + listener.onLongClick(clientId, media, transitionView) + } } override val transitionView: View = binding.root @@ -138,7 +148,7 @@ sealed class ShelfViewHolder( fun create( parent: ViewGroup, clientId: String, - listener: MediaItemAdapter.Listener + listener: ShelfAdapter.Listener ): ShelfViewHolder { val layoutInflater = LayoutInflater.from(parent.context) return Media( diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShowButtonViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShowButtonViewHolder.kt new file mode 100644 index 00000000..761c40e0 --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/ShowButtonViewHolder.kt @@ -0,0 +1,47 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import dev.brahmkshatriya.echo.R +import dev.brahmkshatriya.echo.common.models.Shelf +import dev.brahmkshatriya.echo.databinding.ItemShelfShowButtonBinding + +class ShowButtonViewHolder( + val binding: ItemShelfShowButtonBinding +) : ShelfListItemViewHolder(binding.root) { + + @SuppressLint("NotifyDataSetChanged") + override fun bind(item: Any) { + item as Boolean + val id = if (item) R.string.show_less else R.string.show_all + binding.show.setText(id) + binding.show.setOnClickListener { onClick(item, shelf.list) } + } + + private fun onClick(showingAll: Boolean, list: List) { + if (showingAll) adapter.submitList(list.take(MAX) + false) + else adapter.submitList(list + true) + } + + override val transitionView = binding.root + + companion object { + + private const val MAX = 3 + fun create(parent: ViewGroup): ShowButtonViewHolder { + val inflater = LayoutInflater.from(parent.context) + return ShowButtonViewHolder( + ItemShelfShowButtonBinding.inflate(inflater, parent, false) + ) + } + + fun initialize(adapter: ShelfListItemViewAdapter, shelf: Shelf.Lists.Tracks) { + if (shelf.list.size <= MAX) adapter.submitList(shelf.list) + else { + val list = shelf.list.subList(0, MAX) + adapter.submitList(list + false) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/TrackViewHolder.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/TrackViewHolder.kt new file mode 100644 index 00000000..ea545355 --- /dev/null +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/adapter/TrackViewHolder.kt @@ -0,0 +1,65 @@ +package dev.brahmkshatriya.echo.ui.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import dev.brahmkshatriya.echo.R +import dev.brahmkshatriya.echo.common.models.EchoMediaItem +import dev.brahmkshatriya.echo.common.models.Shelf +import dev.brahmkshatriya.echo.databinding.ItemTrackBinding +import dev.brahmkshatriya.echo.ui.item.TrackAdapter +import dev.brahmkshatriya.echo.utils.loadInto +import dev.brahmkshatriya.echo.utils.toTimeString + +class TrackViewHolder( + private val listener: TrackAdapter.Listener, + private val clientId: String, + private val context: EchoMediaItem?, + val binding: ItemTrackBinding +) : ShelfListItemViewHolder(binding.root) { + + override fun bind(item: Any) { + val shelf = shelf as? Shelf.Lists.Tracks ?: return + val pos = bindingAdapterPosition + val track = shelf.list[pos] + val list = shelf.list + val isNumbered = shelf.isNumbered + binding.itemNumber.text = + binding.root.context.getString(R.string.number_dot, pos + 1) + binding.itemNumber.isVisible = isNumbered + 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 + + binding.root.setOnClickListener { + listener.onClick(clientId, context, list, pos, binding.root) + } + binding.root.setOnLongClickListener { + listener.onLongClick(clientId, context, list, pos, binding.root) + } + binding.itemMore.setOnClickListener { + listener.onLongClick(clientId, context, list, pos, binding.root) + } + } + + override val transitionView = binding.root + + companion object { + fun create( + parent: ViewGroup, listener: TrackAdapter.Listener, clientId: String, context: EchoMediaItem? + ): TrackViewHolder { + val inflater = LayoutInflater.from(parent.context) + return TrackViewHolder( + listener, clientId, context, ItemTrackBinding.inflate(inflater, parent, false) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistFragment.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistFragment.kt index 290d20e5..ca3e0494 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistFragment.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/editplaylist/EditPlaylistFragment.kt @@ -25,7 +25,7 @@ import dev.brahmkshatriya.echo.ui.common.openFragment import dev.brahmkshatriya.echo.ui.editplaylist.EditPlaylistViewModel.ListAction.Add import dev.brahmkshatriya.echo.ui.editplaylist.EditPlaylistViewModel.ListAction.Move import dev.brahmkshatriya.echo.ui.editplaylist.EditPlaylistViewModel.ListAction.Remove -import dev.brahmkshatriya.echo.ui.player.PlaylistAdapter +import dev.brahmkshatriya.echo.ui.adapter.PlaylistAdapter import dev.brahmkshatriya.echo.utils.FastScrollerHelper import dev.brahmkshatriya.echo.utils.autoCleared import dev.brahmkshatriya.echo.utils.dpToPx diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/TrackAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/TrackAdapter.kt index bbeb1e89..a90273e0 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/item/TrackAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/item/TrackAdapter.kt @@ -1,26 +1,22 @@ package dev.brahmkshatriya.echo.ui.item -import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible import androidx.paging.PagingData import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import dev.brahmkshatriya.echo.R import dev.brahmkshatriya.echo.common.models.EchoMediaItem +import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.common.models.Track -import dev.brahmkshatriya.echo.databinding.ItemTrackSmallBinding -import dev.brahmkshatriya.echo.utils.loadInto -import dev.brahmkshatriya.echo.utils.toTimeString +import dev.brahmkshatriya.echo.ui.adapter.TrackViewHolder class TrackAdapter( private val clientId: String, private val transition: String, private val listener: Listener, private val context: EchoMediaItem? = null, -) : PagingDataAdapter(DiffCallback) { + private val isNumbered: Boolean = false +) : PagingDataAdapter(DiffCallback) { object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Track, newItem: Track) = oldItem.id == newItem.id @@ -29,19 +25,11 @@ class TrackAdapter( interface Listener { fun onClick( - clientId: String, - context: EchoMediaItem?, - list: List, - pos: Int, - view: View + clientId: String, context: EchoMediaItem?, list: List, pos: Int, view: View ) fun onLongClick( - clientId: String, - context: EchoMediaItem?, - list: List, - pos: Int, - view: View + clientId: String, context: EchoMediaItem?, list: List, pos: Int, view: View ): Boolean } @@ -49,43 +37,16 @@ class TrackAdapter( submitData(pagingData ?: PagingData.empty()) } - inner class ViewHolder(val binding: ItemTrackSmallBinding) : - RecyclerView.ViewHolder(binding.root) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + TrackViewHolder.create(parent, listener, clientId, context) - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemTrackSmallBinding.inflate(LayoutInflater.from(parent.context), parent, false) - ) - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: TrackViewHolder, position: Int) { val binding = holder.binding val track = getItem(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 - binding.root.transitionName = (transition + track.id).hashCode().toString() - - binding.root.setOnClickListener { - val list = snapshot().items - listener.onClick(clientId, context, list, position, binding.root) - } - binding.root.setOnLongClickListener { - val list = snapshot().items - listener.onLongClick(clientId, context, list, position, binding.root) - } - binding.itemMore.setOnClickListener { - val list = snapshot().items - listener.onLongClick(clientId, context, list, position, binding.root) - } + val lists = snapshot().items + val shelf = Shelf.Lists.Tracks("", lists, isNumbered = isNumbered) + holder.shelf = shelf + holder.bind(track) } } \ No newline at end of file diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/LifeCycleListAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/LifeCycleListAdapter.kt deleted file mode 100644 index 3af6eeae..00000000 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/LifeCycleListAdapter.kt +++ /dev/null @@ -1,70 +0,0 @@ -package dev.brahmkshatriya.echo.ui.player - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.LifecycleRegistry -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import androidx.viewbinding.ViewBinding - -abstract class LifeCycleListAdapter( - diffCallback: DiffUtil.ItemCallback -) : ListAdapter>(diffCallback) { - - - abstract fun inflateCallback( - inflater: LayoutInflater, - container: ViewGroup? - ): TBindingType - - abstract fun Holder.onBind(position: Int) - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): Holder { - val binding = inflateCallback(LayoutInflater.from(parent.context), parent) - val holder = Holder(binding) { onBind(it) } - holder.lifecycleRegistry = LifecycleRegistry(holder) - return holder - } - - override fun onBindViewHolder( - holder: Holder, - position: Int - ) { - destroyLifeCycle(holder) - holder.lifecycleRegistry = LifecycleRegistry(holder) - holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) - holder.bind(position) - } - - private fun destroyLifeCycle(holder: Holder) { - if(holder.lifecycleRegistry.currentState.isAtLeast(Lifecycle.State.STARTED)) - holder.lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) - } - - override fun onViewRecycled(holder: Holder) { - super.onViewRecycled(holder) - destroyLifeCycle(holder) - } - - - class Holder( - val binding: TBindingType, - private val onBind: Holder.(position: Int) -> Unit - ) : RecyclerView.ViewHolder(binding.root), LifecycleOwner { - - lateinit var lifecycleRegistry : LifecycleRegistry - - fun bind(position: Int) { - onBind(position) - } - - override val lifecycle get() = lifecycleRegistry - } -} - - diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt index 536f652e..fc335f69 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/PlayerTrackAdapter.kt @@ -36,7 +36,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDE import com.google.android.material.button.MaterialButton import com.google.android.material.slider.Slider import dev.brahmkshatriya.echo.R -import dev.brahmkshatriya.echo.common.clients.LibraryClient +import dev.brahmkshatriya.echo.common.clients.TrackLikeClient import dev.brahmkshatriya.echo.common.models.EchoMediaItem import dev.brahmkshatriya.echo.common.models.EchoMediaItem.Companion.toMediaItem import dev.brahmkshatriya.echo.common.models.Streamable @@ -52,6 +52,7 @@ import dev.brahmkshatriya.echo.playback.MediaItemUtils.track import dev.brahmkshatriya.echo.playback.MediaItemUtils.video import dev.brahmkshatriya.echo.playback.VideoResolver import dev.brahmkshatriya.echo.plugger.getExtension +import dev.brahmkshatriya.echo.ui.adapter.LifeCycleListAdapter import dev.brahmkshatriya.echo.ui.player.PlayerColors.Companion.defaultPlayerColors import dev.brahmkshatriya.echo.ui.player.PlayerColors.Companion.getColorsFrom import dev.brahmkshatriya.echo.ui.settings.LookFragment @@ -76,16 +77,13 @@ import kotlin.math.min class PlayerTrackAdapter( val fragment: Fragment, private val listener: Listener -) : LifeCycleListAdapter(DiffCallback) { +) : LifeCycleListAdapter(DiffCallback) { interface Listener { fun onMoreClicked(clientId: String?, item: EchoMediaItem, loaded: Boolean) fun onItemClicked(clientId: String?, item: EchoMediaItem) } - private val viewModel by fragment.activityViewModels() - private val uiViewModel by fragment.activityViewModels() - object DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: MediaItem, newItem: MediaItem) = oldItem.mediaId == newItem.mediaId @@ -95,11 +93,20 @@ class PlayerTrackAdapter( } - override fun inflateCallback(inflater: LayoutInflater, container: ViewGroup?) = - ItemPlayerTrackBinding.inflate(inflater, container, false) + override fun createHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return ViewHolder(ItemPlayerTrackBinding.inflate(inflater, parent, false)) + } + + inner class ViewHolder(val binding: ItemPlayerTrackBinding) : Holder(binding.root) { + override fun bind(item: MediaItem) { onBind(bindingAdapterPosition) } + } + + private val viewModel by fragment.activityViewModels() + private val uiViewModel by fragment.activityViewModels() @OptIn(UnstableApi::class) - override fun Holder.onBind(position: Int) { + fun ViewHolder.onBind(position: Int) { binding.bgVisualizer.processor = viewModel.fftAudioProcessor val item = getItem(position) ?: return @@ -215,7 +222,7 @@ class PlayerTrackAdapter( binding.playerControls.trackHeart.run { val client = viewModel.extensionListFlow.getExtension(clientId)?.client - val isLibrary = client is LibraryClient + val isLibrary = client is TrackLikeClient isVisible = isLibrary val likeListener = viewModel.likeListener diff --git a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/QueueFragment.kt b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/QueueFragment.kt index 0e1946eb..420a8893 100644 --- a/app/src/main/java/dev/brahmkshatriya/echo/ui/player/QueueFragment.kt +++ b/app/src/main/java/dev/brahmkshatriya/echo/ui/player/QueueFragment.kt @@ -14,6 +14,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDE import dev.brahmkshatriya.echo.databinding.FragmentPlaylistBinding import dev.brahmkshatriya.echo.playback.MediaItemUtils import dev.brahmkshatriya.echo.playback.Radio +import dev.brahmkshatriya.echo.ui.adapter.PlaylistAdapter import dev.brahmkshatriya.echo.utils.autoCleared import dev.brahmkshatriya.echo.utils.dpToPx import dev.brahmkshatriya.echo.utils.observe diff --git a/app/src/main/res/layout/item_shelf_category.xml b/app/src/main/res/layout/item_shelf_category.xml index 04698f6f..b73dab68 100644 --- a/app/src/main/res/layout/item_shelf_category.xml +++ b/app/src/main/res/layout/item_shelf_category.xml @@ -1,14 +1,12 @@ + android:minHeight="64dp"> + android:layout_marginHorizontal="8dp" + app:cardBackgroundColor="@android:color/transparent"> + + + app:icon="@drawable/ic_back" + tools:visibility="gone" /> @@ -65,10 +72,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" - android:orientation="horizontal" - android:paddingHorizontal="16dp" android:paddingBottom="8dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - tools:listitem="@layout/new_item_media_track" /> + tools:listitem="@layout/item_track" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_shelf_media_grid.xml b/app/src/main/res/layout/item_shelf_media_grid.xml new file mode 100644 index 00000000..785c27db --- /dev/null +++ b/app/src/main/res/layout/item_shelf_media_grid.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_shelf_show_button.xml b/app/src/main/res/layout/item_shelf_show_button.xml new file mode 100644 index 00000000..771fff03 --- /dev/null +++ b/app/src/main/res/layout/item_shelf_show_button.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_track.xml b/app/src/main/res/layout/item_track.xml new file mode 100644 index 00000000..0534b08f --- /dev/null +++ b/app/src/main/res/layout/item_track.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_track_small.xml b/app/src/main/res/layout/item_track_small.xml deleted file mode 100644 index 38bfc9a6..00000000 --- a/app/src/main/res/layout/item_track_small.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-as/strings.xml b/app/src/main/res/values-as/strings.xml index eff5b134..c349a837 100644 --- a/app/src/main/res/values-as/strings.xml +++ b/app/src/main/res/values-as/strings.xml @@ -4,7 +4,6 @@ ঘৰ পুথিভঁৰাল আলবাম - %1$d. গানসমূহ ৰেডিঅ\' দৰ্শন diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index b2437959..5593354d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -18,7 +18,6 @@ Playliste Lädt… Album - %1$d. Abspielen %1$d Song diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 527a1469..459a964e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -104,7 +104,6 @@ $1%s : Utilisateur non autorisée Activé Vider la playlist - %1$d. Glisser pour changer la position de la playlist Chargement… Album diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 353d1b41..a61d2efd 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -83,7 +83,6 @@ प्लेलिस्ट लोड हो रहा है… एल्बम - %1$d। चलाएँ %1$s में सहेजा गया प्लेलिस्ट में सहेजा गया diff --git a/app/src/main/res/values-hng/strings.xml b/app/src/main/res/values-hng/strings.xml index 4d38e684..0061c567 100644 --- a/app/src/main/res/values-hng/strings.xml +++ b/app/src/main/res/values-hng/strings.xml @@ -20,7 +20,6 @@ Playlist clear karo Playlist Load ho raha hai… - %1$d. Chalaaye Gaane Radio diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 56692eeb..94fa3c7c 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -28,7 +28,6 @@ カバーアートのトランジション %1$sが作成 アルバム - %1$d。 %1$d人のフォロワー diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index ccd77fc1..d6756bb0 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -5,7 +5,6 @@ Shuffle Playlist Laden… - %1$d. Afspelen Nummers Bekijken diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0e42369e..b7b28dd6 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -26,7 +26,6 @@ Playlista Wczytywanie… Album - %1$d. Odtwórz Radio Przestań obserwować diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 65fa1218..b3f0a36e 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -129,7 +129,6 @@ Playlist A carregar… Álbum - %1$d. Tocar Começar Rádio Automaticamente Adicionar automaticamente músicas relacionadas às da playlist, para ter uma fila infinita diff --git a/app/src/main/res/values-sa/strings.xml b/app/src/main/res/values-sa/strings.xml index ce8b4fe6..f9123907 100644 --- a/app/src/main/res/values-sa/strings.xml +++ b/app/src/main/res/values-sa/strings.xml @@ -42,7 +42,6 @@ प्लेलिस्ट् लोड् कुर्वन् अस्ति… एल्बम् - %1$d. चलय %1$d गीतम् diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index aaa5f8ed..12f5182d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -25,7 +25,6 @@ Oynatma Listesi Yükleniyor… Albüm - %1$d. Oynat %1$d Şarkı diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5136739a..9dc07701 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -175,7 +175,6 @@ 管理扩展 已禁用 Echo - %1$d. 黑色主题 Seekbar \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 679f5226..211bc610 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,7 +25,7 @@ Playlist Loading… Album - %1$d. + %1$d. Play %1$d Song @@ -214,6 +214,8 @@ This permission is required to access your music library Remove from Library Save to Library + Show All + Show Less Highest Medium diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/clients/LibraryClient.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/LibraryClient.kt index 98a108bb..2730663d 100644 --- a/common/src/main/java/dev/brahmkshatriya/echo/common/clients/LibraryClient.kt +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/LibraryClient.kt @@ -3,10 +3,8 @@ package dev.brahmkshatriya.echo.common.clients import dev.brahmkshatriya.echo.common.helpers.PagedData import dev.brahmkshatriya.echo.common.models.Shelf import dev.brahmkshatriya.echo.common.models.Tab -import dev.brahmkshatriya.echo.common.models.Track interface LibraryClient { suspend fun getLibraryTabs(): List fun getLibraryFeed(tab: Tab?): PagedData - suspend fun likeTrack(track: Track, liked: Boolean): Boolean } \ No newline at end of file diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackHideClient.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackHideClient.kt new file mode 100644 index 00000000..5970cbb0 --- /dev/null +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackHideClient.kt @@ -0,0 +1,5 @@ +package dev.brahmkshatriya.echo.common.clients + +interface TrackHideClient { + suspend fun hideTrack(trackId: String, isHidden: Boolean) +} \ No newline at end of file diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackLikeClient.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackLikeClient.kt new file mode 100644 index 00000000..fead5fc0 --- /dev/null +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/clients/TrackLikeClient.kt @@ -0,0 +1,7 @@ +package dev.brahmkshatriya.echo.common.clients + +import dev.brahmkshatriya.echo.common.models.Track + +interface TrackLikeClient { + suspend fun likeTrack(track: Track, isLiked: Boolean) +} \ No newline at end of file diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Artist.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Artist.kt index d058a78d..5edae80d 100644 --- a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Artist.kt +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Artist.kt @@ -9,7 +9,8 @@ data class Artist( val cover: ImageHolder? = null, val followers: Int? = null, val description: String? = null, - val isFollowing : Boolean = false, + val banners: List = listOf(), + val isFollowing: Boolean = false, val subtitle: String? = null, val extras: Map = mapOf() ) diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Shelf.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Shelf.kt index 72281b6c..3087adb7 100644 --- a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Shelf.kt +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Shelf.kt @@ -36,6 +36,7 @@ sealed class Shelf { list: List, subtitle: String? = null, type: Type = Type.Linear, + val isNumbered: Boolean = false, more: PagedData? = null ) : Lists( title = title, diff --git a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Track.kt b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Track.kt index d6cbf5df..a1a1a13a 100644 --- a/common/src/main/java/dev/brahmkshatriya/echo/common/models/Track.kt +++ b/common/src/main/java/dev/brahmkshatriya/echo/common/models/Track.kt @@ -14,11 +14,12 @@ data class Track( val releaseDate: String? = null, val description: String? = null, val isExplicit: Boolean = false, - val isLiked: Boolean = false, val genre: String? = null, val subtitle: String? = null, val extras: Map = mapOf(), val streamables: List = listOf(), + val isLiked: Boolean = false, + val isHidden: Boolean = false ) { val subtitleStreamables: List by lazy { streamables.filter { it.mediaType == Streamable.MediaType.Subtitle } @@ -32,7 +33,7 @@ data class Track( mediaStreamables + streamables.filter { it.mediaType == Streamable.MediaType.Video } } - val mediaStreamables: List by lazy { + private val mediaStreamables: List by lazy { streamables.filter { it.mediaType == Streamable.MediaType.AudioVideo } } } \ No newline at end of file