diff --git a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt index cc094cf..9ece861 100644 --- a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt +++ b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt @@ -7,12 +7,19 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.fragments.FunkwhaleAdapter +import com.github.apognu.otter.utils.Event +import com.github.apognu.otter.utils.EventBus import com.github.apognu.otter.utils.Radio +import com.github.apognu.otter.views.LoadingImageView import kotlinx.android.synthetic.main.row_radio.view.* +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class RadiosAdapter(val context: Context?, private val listener: OnRadioClickListener) : FunkwhaleAdapter() { interface OnRadioClickListener { - fun onClick(holder: View?, radio: Radio) + fun onClick(holder: ViewHolder, radio: Radio) } override fun getItemCount() = data.size @@ -31,7 +38,6 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis val radio = data[position] holder.art.visibility = View.VISIBLE - holder.nativeArt.visibility = View.GONE holder.name.text = radio.name holder.description.text = radio.description @@ -43,24 +49,46 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis } icon?.let { - holder.art.visibility = View.GONE - holder.nativeArt.visibility = View.VISIBLE + holder.native = true - holder.nativeArt.setImageDrawable(context.getDrawable(icon)) - holder.nativeArt.alpha = 0.7f - holder.nativeArt.setColorFilter(context.getColor(R.color.controlForeground)) + holder.art.setImageDrawable(context.getDrawable(icon)) + holder.art.alpha = 0.7f + holder.art.setColorFilter(context.getColor(R.color.controlForeground)) } } } inner class ViewHolder(view: View, private val listener: OnRadioClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener { - val nativeArt = view.native_art val art = view.art val name = view.name val description = view.description + var native = false + override fun onClick(view: View?) { - listener.onClick(view, data[layoutPosition]) + listener.onClick(this, data[layoutPosition]) + } + + fun spin() { + context?.let { + val originalDrawable = art.drawable + val originalColorFilter = art.colorFilter + val imageAnimator = LoadingImageView.start(context, art) + + art.setColorFilter(context.getColor(R.color.controlForeground)) + + GlobalScope.launch(Main) { + EventBus.get().collect { message -> + when (message) { + is Event.RadioStarted -> { + art.colorFilter = originalColorFilter + + LoadingImageView.stop(context, originalDrawable, art, imageAnimator) + } + } + } + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt index 3d10075..85fbd81 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt @@ -18,11 +18,18 @@ import com.github.apognu.otter.R import com.github.apognu.otter.activities.MainActivity import com.github.apognu.otter.adapters.AlbumsAdapter import com.github.apognu.otter.repositories.AlbumsRepository +import com.github.apognu.otter.repositories.ArtistTracksRepository +import com.github.apognu.otter.repositories.Repository import com.github.apognu.otter.utils.* +import com.github.apognu.otter.views.LoadingFlotingActionButton import com.squareup.picasso.Picasso import kotlinx.android.synthetic.main.fragment_albums.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -30,6 +37,8 @@ class AlbumsFragment : FunkwhaleFragment() { override val viewRes = R.layout.fragment_albums override val recycler: RecyclerView get() = albums + lateinit var artistTracksRepository: ArtistTracksRepository + var artistId = 0 var artistName = "" var artistArt = "" @@ -91,6 +100,7 @@ class AlbumsFragment : FunkwhaleFragment() { adapter = AlbumsAdapter(context, OnAlbumClickListener()) repository = AlbumsRepository(context, artistId) + artistTracksRepository = ArtistTracksRepository(context, artistId) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -107,7 +117,7 @@ class AlbumsFragment : FunkwhaleFragment() { cover_background?.let { background -> activity?.let { activity -> - GlobalScope.launch(Dispatchers.IO) { + GlobalScope.launch(IO) { val width = DisplayMetrics().apply { activity.windowManager.defaultDisplay.getMetrics(this) }.widthPixels @@ -130,6 +140,25 @@ class AlbumsFragment : FunkwhaleFragment() { } artist.text = artistName + + play.setOnClickListener { + val loaderAnimation = LoadingFlotingActionButton.start(play) + + GlobalScope.launch(IO) { + artistTracksRepository.fetch(Repository.Origin.Network.origin) + .map { it.data } + .toList() + .flatten() + .shuffled() + .also { + CommandBus.send(Command.ReplaceQueue(it)) + + withContext(Main) { + LoadingFlotingActionButton.stop(play, loaderAnimation) + } + } + } + } } inner class OnAlbumClickListener : AlbumsAdapter.OnAlbumClickListener { diff --git a/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt index 8482dfb..e495e9a 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt @@ -1,15 +1,17 @@ package com.github.apognu.otter.fragments import android.os.Bundle -import android.view.View +import androidx.core.view.forEach import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.adapters.RadiosAdapter import com.github.apognu.otter.repositories.RadiosRepository -import com.github.apognu.otter.utils.Command -import com.github.apognu.otter.utils.CommandBus -import com.github.apognu.otter.utils.Radio +import com.github.apognu.otter.utils.* import kotlinx.android.synthetic.main.fragment_radios.* +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class RadiosFragment : FunkwhaleFragment() { override val viewRes = R.layout.fragment_radios @@ -23,8 +25,25 @@ class RadiosFragment : FunkwhaleFragment() { } inner class RadioClickListener : RadiosAdapter.OnRadioClickListener { - override fun onClick(holder: View?, radio: Radio) { + override fun onClick(holder: RadiosAdapter.ViewHolder, radio: Radio) { + holder.spin() + recycler.forEach { + it.isEnabled = false + it.isClickable = false + } + CommandBus.send(Command.PlayRadio(radio)) + + GlobalScope.launch(Main) { + EventBus.get().collect { message -> + when (message) { + is Event.RadioStarted -> recycler.forEach { + it.isEnabled = true + it.isClickable = true + } + } + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt index e5637fc..151800d 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt @@ -126,6 +126,8 @@ class RadioPlayer(val context: Context) { withContext(Main) { context.toast(context.getString(R.string.radio_playback_error)) } + } finally { + EventBus.send(Event.RadioStarted) } } } diff --git a/app/src/main/java/com/github/apognu/otter/repositories/ArtistTracksRepository.kt b/app/src/main/java/com/github/apognu/otter/repositories/ArtistTracksRepository.kt new file mode 100644 index 0000000..32967ca --- /dev/null +++ b/app/src/main/java/com/github/apognu/otter/repositories/ArtistTracksRepository.kt @@ -0,0 +1,18 @@ +package com.github.apognu.otter.repositories + +import android.content.Context +import com.github.apognu.otter.utils.FunkwhaleResponse +import com.github.apognu.otter.utils.Track +import com.github.apognu.otter.utils.TracksCache +import com.github.apognu.otter.utils.TracksResponse +import com.github.kittinunf.fuel.gson.gsonDeserializerOf +import com.google.gson.reflect.TypeToken +import java.io.BufferedReader + +class ArtistTracksRepository(override val context: Context?, private val artistId: Int) : Repository() { + override val cacheId = "tracks-artist-${artistId}" + override val upstream = HttpUpstream>(HttpUpstream.Behavior.AtOnce, "/api/v1/tracks/?playable=true&artist=${artistId}", object : TypeToken() {}.type) + + override fun cache(data: List) = TracksCache(data) + override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/apognu/otter/utils/EventBus.kt b/app/src/main/java/com/github/apognu/otter/utils/EventBus.kt index b51ba40..ba3b613 100644 --- a/app/src/main/java/com/github/apognu/otter/utils/EventBus.kt +++ b/app/src/main/java/com/github/apognu/otter/utils/EventBus.kt @@ -42,6 +42,7 @@ sealed class Event { class RefreshTrack(val track: Track?, val play: Boolean) : Event() class StateChanged(val playing: Boolean) : Event() object QueueChanged : Event() + object RadioStarted : Event() } sealed class Request(var channel: Channel? = null) { diff --git a/app/src/main/java/com/github/apognu/otter/views/LoaderViews.kt b/app/src/main/java/com/github/apognu/otter/views/LoaderViews.kt new file mode 100644 index 0000000..9cf27ea --- /dev/null +++ b/app/src/main/java/com/github/apognu/otter/views/LoaderViews.kt @@ -0,0 +1,59 @@ +package com.github.apognu.otter.views + +import android.animation.ObjectAnimator +import android.content.Context +import android.graphics.drawable.Drawable +import android.view.View +import android.widget.ImageView +import com.github.apognu.otter.R +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton + +object LoadingFlotingActionButton { + fun start(button: ExtendedFloatingActionButton): ObjectAnimator { + button.isEnabled = false + button.setIconResource(R.drawable.fab_spinner) + button.shrink() + + return ObjectAnimator.ofFloat(button, View.ROTATION, 0f, 360f).apply { + duration = 500 + repeatCount = ObjectAnimator.INFINITE + start() + } + } + + fun stop(button: ExtendedFloatingActionButton, animator: ObjectAnimator) { + animator.cancel() + + button.isEnabled = true + button.setIconResource(R.drawable.play) + button.rotation = 0.0f + button.extend() + } +} + +object LoadingImageView { + fun start(context: Context?, image: ImageView): ObjectAnimator? { + context?.let { + image.isEnabled = false + image.setImageDrawable(context.getDrawable(R.drawable.fab_spinner)) + + return ObjectAnimator.ofFloat(image, View.ROTATION, 0f, 360f).apply { + duration = 500 + repeatCount = ObjectAnimator.INFINITE + start() + } + } + + return null + } + + fun stop(context: Context?, original: Drawable, image: ImageView, animator: ObjectAnimator?) { + context?.let { + animator?.cancel() + + image.isEnabled = true + image.setImageDrawable(original) + image.rotation = 0.0f + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/fab_spinner.xml b/app/src/main/res/drawable/fab_spinner.xml new file mode 100644 index 0000000..5e1e500 --- /dev/null +++ b/app/src/main/res/drawable/fab_spinner.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_albums.xml b/app/src/main/res/layout-land/fragment_albums.xml index 35ec3a1..d789d44 100644 --- a/app/src/main/res/layout-land/fragment_albums.xml +++ b/app/src/main/res/layout-land/fragment_albums.xml @@ -39,6 +39,40 @@ android:gravity="center_vertical" android:orientation="vertical"> + + + + + + + + + + + android:orientation="horizontal"> - + android:layout_weight="1" + android:orientation="vertical"> - + + + + + diff --git a/app/src/main/res/layout/row_radio.xml b/app/src/main/res/layout/row_radio.xml index fb194a3..320f62b 100644 --- a/app/src/main/res/layout/row_radio.xml +++ b/app/src/main/res/layout/row_radio.xml @@ -13,23 +13,12 @@ android:transitionGroup="true" tools:showIn="@layout/fragment_radios"> - -