Ability to shuffle play all tracks from an artist. Should close #21. Also added animations over long-running operations.
This commit is contained in:
parent
cb43615cb1
commit
c75f2e45f6
|
@ -7,12 +7,19 @@ import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.apognu.otter.R
|
import com.github.apognu.otter.R
|
||||||
import com.github.apognu.otter.fragments.FunkwhaleAdapter
|
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.utils.Radio
|
||||||
|
import com.github.apognu.otter.views.LoadingImageView
|
||||||
import kotlinx.android.synthetic.main.row_radio.view.*
|
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<Radio, RadiosAdapter.ViewHolder>() {
|
class RadiosAdapter(val context: Context?, private val listener: OnRadioClickListener) : FunkwhaleAdapter<Radio, RadiosAdapter.ViewHolder>() {
|
||||||
interface OnRadioClickListener {
|
interface OnRadioClickListener {
|
||||||
fun onClick(holder: View?, radio: Radio)
|
fun onClick(holder: ViewHolder, radio: Radio)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount() = data.size
|
override fun getItemCount() = data.size
|
||||||
|
@ -31,7 +38,6 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis
|
||||||
val radio = data[position]
|
val radio = data[position]
|
||||||
|
|
||||||
holder.art.visibility = View.VISIBLE
|
holder.art.visibility = View.VISIBLE
|
||||||
holder.nativeArt.visibility = View.GONE
|
|
||||||
holder.name.text = radio.name
|
holder.name.text = radio.name
|
||||||
holder.description.text = radio.description
|
holder.description.text = radio.description
|
||||||
|
|
||||||
|
@ -43,24 +49,46 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis
|
||||||
}
|
}
|
||||||
|
|
||||||
icon?.let {
|
icon?.let {
|
||||||
holder.art.visibility = View.GONE
|
holder.native = true
|
||||||
holder.nativeArt.visibility = View.VISIBLE
|
|
||||||
|
|
||||||
holder.nativeArt.setImageDrawable(context.getDrawable(icon))
|
holder.art.setImageDrawable(context.getDrawable(icon))
|
||||||
holder.nativeArt.alpha = 0.7f
|
holder.art.alpha = 0.7f
|
||||||
holder.nativeArt.setColorFilter(context.getColor(R.color.controlForeground))
|
holder.art.setColorFilter(context.getColor(R.color.controlForeground))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(view: View, private val listener: OnRadioClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
inner class ViewHolder(view: View, private val listener: OnRadioClickListener) : RecyclerView.ViewHolder(view), View.OnClickListener {
|
||||||
val nativeArt = view.native_art
|
|
||||||
val art = view.art
|
val art = view.art
|
||||||
val name = view.name
|
val name = view.name
|
||||||
val description = view.description
|
val description = view.description
|
||||||
|
|
||||||
|
var native = false
|
||||||
|
|
||||||
override fun onClick(view: View?) {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,11 +18,18 @@ import com.github.apognu.otter.R
|
||||||
import com.github.apognu.otter.activities.MainActivity
|
import com.github.apognu.otter.activities.MainActivity
|
||||||
import com.github.apognu.otter.adapters.AlbumsAdapter
|
import com.github.apognu.otter.adapters.AlbumsAdapter
|
||||||
import com.github.apognu.otter.repositories.AlbumsRepository
|
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.utils.*
|
||||||
|
import com.github.apognu.otter.views.LoadingFlotingActionButton
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.android.synthetic.main.fragment_albums.*
|
import kotlinx.android.synthetic.main.fragment_albums.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@ -30,6 +37,8 @@ class AlbumsFragment : FunkwhaleFragment<Album, AlbumsAdapter>() {
|
||||||
override val viewRes = R.layout.fragment_albums
|
override val viewRes = R.layout.fragment_albums
|
||||||
override val recycler: RecyclerView get() = albums
|
override val recycler: RecyclerView get() = albums
|
||||||
|
|
||||||
|
lateinit var artistTracksRepository: ArtistTracksRepository
|
||||||
|
|
||||||
var artistId = 0
|
var artistId = 0
|
||||||
var artistName = ""
|
var artistName = ""
|
||||||
var artistArt = ""
|
var artistArt = ""
|
||||||
|
@ -91,6 +100,7 @@ class AlbumsFragment : FunkwhaleFragment<Album, AlbumsAdapter>() {
|
||||||
|
|
||||||
adapter = AlbumsAdapter(context, OnAlbumClickListener())
|
adapter = AlbumsAdapter(context, OnAlbumClickListener())
|
||||||
repository = AlbumsRepository(context, artistId)
|
repository = AlbumsRepository(context, artistId)
|
||||||
|
artistTracksRepository = ArtistTracksRepository(context, artistId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -107,7 +117,7 @@ class AlbumsFragment : FunkwhaleFragment<Album, AlbumsAdapter>() {
|
||||||
|
|
||||||
cover_background?.let { background ->
|
cover_background?.let { background ->
|
||||||
activity?.let { activity ->
|
activity?.let { activity ->
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(IO) {
|
||||||
val width = DisplayMetrics().apply {
|
val width = DisplayMetrics().apply {
|
||||||
activity.windowManager.defaultDisplay.getMetrics(this)
|
activity.windowManager.defaultDisplay.getMetrics(this)
|
||||||
}.widthPixels
|
}.widthPixels
|
||||||
|
@ -130,6 +140,25 @@ class AlbumsFragment : FunkwhaleFragment<Album, AlbumsAdapter>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
artist.text = artistName
|
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 {
|
inner class OnAlbumClickListener : AlbumsAdapter.OnAlbumClickListener {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
package com.github.apognu.otter.fragments
|
package com.github.apognu.otter.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import androidx.core.view.forEach
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.apognu.otter.R
|
import com.github.apognu.otter.R
|
||||||
import com.github.apognu.otter.adapters.RadiosAdapter
|
import com.github.apognu.otter.adapters.RadiosAdapter
|
||||||
import com.github.apognu.otter.repositories.RadiosRepository
|
import com.github.apognu.otter.repositories.RadiosRepository
|
||||||
import com.github.apognu.otter.utils.Command
|
import com.github.apognu.otter.utils.*
|
||||||
import com.github.apognu.otter.utils.CommandBus
|
|
||||||
import com.github.apognu.otter.utils.Radio
|
|
||||||
import kotlinx.android.synthetic.main.fragment_radios.*
|
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<Radio, RadiosAdapter>() {
|
class RadiosFragment : FunkwhaleFragment<Radio, RadiosAdapter>() {
|
||||||
override val viewRes = R.layout.fragment_radios
|
override val viewRes = R.layout.fragment_radios
|
||||||
|
@ -23,8 +25,25 @@ class RadiosFragment : FunkwhaleFragment<Radio, RadiosAdapter>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class RadioClickListener : RadiosAdapter.OnRadioClickListener {
|
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))
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -126,6 +126,8 @@ class RadioPlayer(val context: Context) {
|
||||||
withContext(Main) {
|
withContext(Main) {
|
||||||
context.toast(context.getString(R.string.radio_playback_error))
|
context.toast(context.getString(R.string.radio_playback_error))
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
EventBus.send(Event.RadioStarted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Track, TracksCache>() {
|
||||||
|
override val cacheId = "tracks-artist-${artistId}"
|
||||||
|
override val upstream = HttpUpstream<Track, FunkwhaleResponse<Track>>(HttpUpstream.Behavior.AtOnce, "/api/v1/tracks/?playable=true&artist=${artistId}", object : TypeToken<TracksResponse>() {}.type)
|
||||||
|
|
||||||
|
override fun cache(data: List<Track>) = TracksCache(data)
|
||||||
|
override fun uncache(reader: BufferedReader) = gsonDeserializerOf(TracksCache::class.java).deserialize(reader)
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ sealed class Event {
|
||||||
class RefreshTrack(val track: Track?, val play: Boolean) : Event()
|
class RefreshTrack(val track: Track?, val play: Boolean) : Event()
|
||||||
class StateChanged(val playing: Boolean) : Event()
|
class StateChanged(val playing: Boolean) : Event()
|
||||||
object QueueChanged : Event()
|
object QueueChanged : Event()
|
||||||
|
object RadioStarted : Event()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Request(var channel: Channel<Response>? = null) {
|
sealed class Request(var channel: Channel<Response>? = null) {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<rotate
|
||||||
|
android:fromDegrees="0"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%"
|
||||||
|
android:toDegrees="360">
|
||||||
|
|
||||||
|
<shape
|
||||||
|
android:shape="ring"
|
||||||
|
android:thickness="3dp"
|
||||||
|
android:type="sweep"
|
||||||
|
android:useLevel="false">
|
||||||
|
|
||||||
|
<gradient
|
||||||
|
android:angle="0"
|
||||||
|
android:centerColor="#00ffffff"
|
||||||
|
android:endColor="#00ffffff"
|
||||||
|
android:startColor="#ffffffff"
|
||||||
|
android:type="sweep"
|
||||||
|
android:useLevel="false" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
|
|
||||||
|
</rotate>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layer-list>
|
|
@ -39,6 +39,40 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/cover"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:contentDescription="@string/alt_artist_art"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/play"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@color/colorPrimary"
|
||||||
|
android:elevation="10dp"
|
||||||
|
android:text="@string/playback_shuffle"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
app:icon="@drawable/play"
|
||||||
|
app:iconTint="@android:color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/cover"
|
||||||
|
app:layout_constraintLeft_toLeftOf="@id/cover"
|
||||||
|
app:layout_constraintRight_toRightOf="@id/cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cover" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -53,34 +53,56 @@
|
||||||
app:layout_constraintVertical_bias="0"
|
app:layout_constraintVertical_bias="0"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/play"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@color/colorPrimary"
|
||||||
|
android:elevation="10dp"
|
||||||
|
android:text="@string/playback_shuffle"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
app:icon="@drawable/play"
|
||||||
|
app:iconTint="@android:color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/cover"
|
||||||
|
app:layout_constraintLeft_toLeftOf="@id/cover"
|
||||||
|
app:layout_constraintRight_toRightOf="@id/cover"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cover" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:baselineAligned="false"
|
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="vertical">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_weight="1"
|
||||||
android:layout_marginTop="16dp"
|
android:orientation="vertical">
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/albums"
|
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/artist"
|
android:layout_width="match_parent"
|
||||||
style="@style/AppTheme.Title"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:text="@string/albums"
|
||||||
android:layout_marginBottom="16dp"
|
android:textAllCaps="true"
|
||||||
tools:text="Muse" />
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/artist"
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
tools:text="Muse" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -13,23 +13,12 @@
|
||||||
android:transitionGroup="true"
|
android:transitionGroup="true"
|
||||||
tools:showIn="@layout/fragment_radios">
|
tools:showIn="@layout/fragment_radios">
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/native_art"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:contentDescription="@string/alt_album_cover"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="@drawable/cover"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
<com.github.apognu.otter.views.SquareImageView
|
<com.github.apognu.otter.views.SquareImageView
|
||||||
android:id="@+id/art"
|
android:id="@+id/art"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerInside"
|
||||||
android:src="@drawable/cover"
|
android:src="@drawable/cover"
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue