Skip to content

Commit

Permalink
Even more refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
brahmkshatriya committed Sep 21, 2024
1 parent 5477b3f commit 2fd2a5f
Show file tree
Hide file tree
Showing 49 changed files with 818 additions and 450 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 2 additions & 14 deletions app/src/main/java/dev/brahmkshatriya/echo/offline/TestExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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<Tab>()

override fun getLibraryFeed(tab: Tab?): PagedData<Shelf> {
return PagedData.Single { emptyList() }
}

override suspend fun likeTrack(track: Track, liked: Boolean): Boolean {
println("likeTrack: ${track.title}, $liked")
return liked
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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<T : Any, Holder : LifeCycleListAdapter.Holder<T>>(
diffCallback: DiffUtil.ItemCallback<T>
) : ListAdapter<T, Holder>(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<T>(itemView: View) : RecyclerView.ViewHolder(itemView), LifecycleOwner {
abstract fun bind(item: T)
lateinit var lifecycleRegistry: LifecycleRegistry
override val lifecycle get() = lifecycleRegistry
}
}


Loading

0 comments on commit 2fd2a5f

Please sign in to comment.