Getting started with the home page.
This commit is contained in:
parent
7ba67ed80c
commit
f7a5d4c2d5
|
@ -128,6 +128,7 @@ dependencies {
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("com.google.android.material:material:1.3.0-alpha02")
|
implementation("com.google.android.material:material:1.3.0-alpha02")
|
||||||
implementation("com.android.support.constraint:constraint-layout:2.0.1")
|
implementation("com.android.support.constraint:constraint-layout:2.0.1")
|
||||||
|
implementation("com.google.android:flexbox:2.0.1")
|
||||||
|
|
||||||
implementation("com.google.android.exoplayer:exoplayer-core:2.11.5")
|
implementation("com.google.android.exoplayer:exoplayer-core:2.11.5")
|
||||||
implementation("com.google.android.exoplayer:exoplayer-ui:2.11.5")
|
implementation("com.google.android.exoplayer:exoplayer-ui:2.11.5")
|
||||||
|
|
|
@ -37,7 +37,7 @@ class AlbumsGridAdapter(val context: Context?, private val listener: OnAlbumClic
|
||||||
Picasso.get()
|
Picasso.get()
|
||||||
.maybeLoad(maybeNormalizeUrl(album.cover()))
|
.maybeLoad(maybeNormalizeUrl(album.cover()))
|
||||||
.fit()
|
.fit()
|
||||||
.placeholder(R.drawable.cover)
|
.placeholder(R.drawable.cover).placeholder(R.drawable.cover)
|
||||||
.transform(RoundedCornersTransformation(16, 0))
|
.transform(RoundedCornersTransformation(16, 0))
|
||||||
.into(holder.cover)
|
.into(holder.cover)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import com.github.apognu.otter.fragments.*
|
||||||
class BrowseTabsAdapter(val context: Fragment, manager: FragmentManager) : FragmentPagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
class BrowseTabsAdapter(val context: Fragment, manager: FragmentManager) : FragmentPagerAdapter(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||||
var tabs = mutableListOf<Fragment>()
|
var tabs = mutableListOf<Fragment>()
|
||||||
|
|
||||||
override fun getCount() = 5
|
override fun getCount() = 6
|
||||||
|
|
||||||
override fun getItem(position: Int): Fragment {
|
override fun getItem(position: Int): Fragment {
|
||||||
tabs.getOrNull(position)?.let {
|
tabs.getOrNull(position)?.let {
|
||||||
|
@ -17,11 +17,12 @@ class BrowseTabsAdapter(val context: Fragment, manager: FragmentManager) : Fragm
|
||||||
}
|
}
|
||||||
|
|
||||||
val fragment = when (position) {
|
val fragment = when (position) {
|
||||||
0 -> ArtistsFragment()
|
0 -> HomeFragment()
|
||||||
1 -> AlbumsGridFragment()
|
1 -> ArtistsFragment()
|
||||||
2 -> PlaylistsFragment()
|
2 -> AlbumsGridFragment()
|
||||||
3 -> RadiosFragment()
|
3 -> PlaylistsFragment()
|
||||||
4 -> FavoritesFragment()
|
4 -> RadiosFragment()
|
||||||
|
5 -> FavoritesFragment()
|
||||||
else -> ArtistsFragment()
|
else -> ArtistsFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +33,12 @@ class BrowseTabsAdapter(val context: Fragment, manager: FragmentManager) : Fragm
|
||||||
|
|
||||||
override fun getPageTitle(position: Int): String {
|
override fun getPageTitle(position: Int): String {
|
||||||
return when (position) {
|
return when (position) {
|
||||||
0 -> context.getString(R.string.artists)
|
0 -> "Otter"
|
||||||
1 -> context.getString(R.string.albums)
|
1 -> context.getString(R.string.artists)
|
||||||
2 -> context.getString(R.string.playlists)
|
2 -> context.getString(R.string.albums)
|
||||||
3 -> context.getString(R.string.radios)
|
3 -> context.getString(R.string.playlists)
|
||||||
4 -> context.getString(R.string.favorites)
|
4 -> context.getString(R.string.radios)
|
||||||
|
5 -> context.getString(R.string.favorites)
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.github.apognu.otter.adapters.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.apognu.otter.R
|
||||||
|
import com.github.apognu.otter.utils.mustNormalizeUrl
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
|
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation
|
||||||
|
import kotlinx.android.synthetic.main.row_dummy.view.*
|
||||||
|
|
||||||
|
class DummyAdapter(val context: Context?, val viewRes: Int = R.layout.row_dummy) : RecyclerView.Adapter<DummyAdapter.ViewHolder>() {
|
||||||
|
data class DummyItem(val label: String, val cover: String?)
|
||||||
|
|
||||||
|
var data: List<DummyItem> = listOf()
|
||||||
|
|
||||||
|
override fun getItemCount() = data.size
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
return LayoutInflater.from(context).inflate(viewRes, parent, false).run {
|
||||||
|
ViewHolder(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
data[position].also {
|
||||||
|
holder.label.text = it.label
|
||||||
|
|
||||||
|
it.cover?.let { cover ->
|
||||||
|
Picasso
|
||||||
|
.get()
|
||||||
|
.load(mustNormalizeUrl(cover))
|
||||||
|
.fit()
|
||||||
|
.placeholder(R.drawable.cover).placeholder(R.drawable.cover)
|
||||||
|
.transform(RoundedCornersTransformation(16, 0))
|
||||||
|
.into(holder.cover)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
val label = view.label
|
||||||
|
val cover = view.cover
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.github.apognu.otter.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.github.apognu.otter.R
|
||||||
|
import com.github.apognu.otter.adapters.home.DummyAdapter
|
||||||
|
import com.github.apognu.otter.repositories.Repository
|
||||||
|
import com.github.apognu.otter.repositories.home.RecentlyAddedRepository
|
||||||
|
import com.github.apognu.otter.repositories.home.RecentlyListenedRepository
|
||||||
|
import com.github.apognu.otter.repositories.home.TagsRepository
|
||||||
|
import com.github.apognu.otter.utils.untilNetwork
|
||||||
|
import com.google.android.flexbox.FlexboxLayoutManager
|
||||||
|
import kotlinx.android.synthetic.main.fragment_home.*
|
||||||
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class HomeFragment : Fragment() {
|
||||||
|
private lateinit var tagsRepository: TagsRepository
|
||||||
|
private lateinit var recentlyAddedRepository: RecentlyAddedRepository
|
||||||
|
private lateinit var recentlyListenedRepository: RecentlyListenedRepository
|
||||||
|
|
||||||
|
private lateinit var tagsAdapter: DummyAdapter
|
||||||
|
private lateinit var recentlyAddedAdapter: DummyAdapter
|
||||||
|
private lateinit var recentlyListenedAdapter: DummyAdapter
|
||||||
|
private lateinit var dummyAdapter: DummyAdapter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
tagsRepository = TagsRepository(context)
|
||||||
|
recentlyAddedRepository = RecentlyAddedRepository(context)
|
||||||
|
recentlyListenedRepository = RecentlyListenedRepository(context)
|
||||||
|
|
||||||
|
tagsAdapter = DummyAdapter(context, R.layout.row_tag)
|
||||||
|
recentlyAddedAdapter = DummyAdapter(context)
|
||||||
|
recentlyListenedAdapter = DummyAdapter(context)
|
||||||
|
dummyAdapter = DummyAdapter(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_home, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
tags.apply {
|
||||||
|
adapter = tagsAdapter
|
||||||
|
layoutManager = FlexboxLayoutManager(context).apply {
|
||||||
|
isNestedScrollingEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
random.apply {
|
||||||
|
adapter = dummyAdapter
|
||||||
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
recently_listened.apply {
|
||||||
|
adapter = recentlyListenedAdapter
|
||||||
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
recently_added.apply {
|
||||||
|
adapter = recentlyAddedAdapter
|
||||||
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
playlists.apply {
|
||||||
|
adapter = dummyAdapter
|
||||||
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
tagsRepository.fetch(Repository.Origin.Network.origin).untilNetwork(IO) {data, _, _ ->
|
||||||
|
GlobalScope.launch(Main) {
|
||||||
|
tagsAdapter.data = data.map { DummyAdapter.DummyItem(it.name, null) }
|
||||||
|
tagsAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recentlyListenedRepository.fetch(Repository.Origin.Network.origin).untilNetwork(IO) { data, _, _ ->
|
||||||
|
GlobalScope.launch(Main) {
|
||||||
|
recentlyListenedAdapter.data = data.map { DummyAdapter.DummyItem(it.track.title, it.track.album.cover.original) }
|
||||||
|
recentlyListenedAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recentlyAddedRepository.fetch(Repository.Origin.Network.origin).untilNetwork(IO) { data, _, _ ->
|
||||||
|
GlobalScope.launch(Main) {
|
||||||
|
recentlyAddedAdapter.data = data.map { DummyAdapter.DummyItem(it.title, it.album.cover.original) }
|
||||||
|
recentlyAddedAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import java.io.Reader
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
class HttpUpstream<D : Any, R : OtterResponse<D>>(val behavior: Behavior, private val url: String, private val type: Type) : Upstream<D> {
|
class HttpUpstream<D : Any, R : OtterResponse<D>>(val behavior: Behavior, private val url: String, private val type: Type, private val maxSize: Int = AppContext.PAGE_SIZE) : Upstream<D> {
|
||||||
enum class Behavior {
|
enum class Behavior {
|
||||||
Single, AtOnce, Progressive
|
Single, AtOnce, Progressive
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@ class HttpUpstream<D : Any, R : OtterResponse<D>>(val behavior: Behavior, privat
|
||||||
override fun fetch(size: Int): Flow<Repository.Response<D>> = flow {
|
override fun fetch(size: Int): Flow<Repository.Response<D>> = flow {
|
||||||
if (behavior == Behavior.Single && size != 0) return@flow
|
if (behavior == Behavior.Single && size != 0) return@flow
|
||||||
|
|
||||||
val page = ceil(size / AppContext.PAGE_SIZE.toDouble()).toInt() + 1
|
val page = ceil(size / maxSize.toDouble()).toInt() + 1
|
||||||
|
|
||||||
val url =
|
val url =
|
||||||
Uri.parse(url)
|
Uri.parse(url)
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.appendQueryParameter("page_size", AppContext.PAGE_SIZE.toString())
|
.appendQueryParameter("page_size", maxSize.toString())
|
||||||
.appendQueryParameter("page", page.toString())
|
.appendQueryParameter("page", page.toString())
|
||||||
.appendQueryParameter("scope", Settings.getScope())
|
.appendQueryParameter("scope", Settings.getScope())
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.github.apognu.otter.repositories.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.apognu.otter.repositories.HttpUpstream
|
||||||
|
import com.github.apognu.otter.repositories.Repository
|
||||||
|
import com.github.apognu.otter.utils.*
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
|
class RecentlyAddedRepository(override val context: Context?) : Repository<Track, TracksCache>() {
|
||||||
|
override val cacheId = "home-recently-added"
|
||||||
|
|
||||||
|
override val upstream =
|
||||||
|
HttpUpstream<Track, FunkwhaleResponse<Track>>(
|
||||||
|
HttpUpstream.Behavior.Single,
|
||||||
|
"/api/v1/tracks/?playable=true&ordering=-creation_date",
|
||||||
|
object : TypeToken<TracksResponse>() {}.type,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.github.apognu.otter.repositories.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.apognu.otter.repositories.HttpUpstream
|
||||||
|
import com.github.apognu.otter.repositories.Repository
|
||||||
|
import com.github.apognu.otter.utils.*
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
|
class RecentlyListenedRepository(override val context: Context?) : Repository<PlaylistTrack, PlaylistTracksCache>() {
|
||||||
|
override val cacheId = "home-recently-listened"
|
||||||
|
|
||||||
|
override val upstream =
|
||||||
|
HttpUpstream<PlaylistTrack, FunkwhaleResponse<PlaylistTrack>>(
|
||||||
|
HttpUpstream.Behavior.Single,
|
||||||
|
"/api/v1/history/listenings/?playable=true",
|
||||||
|
object : TypeToken<PlaylistTracksResponse>() {}.type,
|
||||||
|
10
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.github.apognu.otter.repositories.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.apognu.otter.repositories.HttpUpstream
|
||||||
|
import com.github.apognu.otter.repositories.Repository
|
||||||
|
import com.github.apognu.otter.utils.*
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
|
class TagsRepository(override val context: Context?) : Repository<Tag, TagsCache>() {
|
||||||
|
override val cacheId = "tags"
|
||||||
|
|
||||||
|
override val upstream =
|
||||||
|
HttpUpstream<Tag, FunkwhaleResponse<Tag>>(
|
||||||
|
HttpUpstream.Behavior.Single,
|
||||||
|
"/api/v1/tags/",
|
||||||
|
object : TypeToken<TagsResponse>() {}.type
|
||||||
|
)
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ class PlaylistTracksCache(data: List<PlaylistTrack>) : CacheItem<PlaylistTrack>(
|
||||||
class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data)
|
class RadiosCache(data: List<Radio>) : CacheItem<Radio>(data)
|
||||||
class FavoritedCache(data: List<Int>) : CacheItem<Int>(data)
|
class FavoritedCache(data: List<Int>) : CacheItem<Int>(data)
|
||||||
class QueueCache(data: List<Track>) : CacheItem<Track>(data)
|
class QueueCache(data: List<Track>) : CacheItem<Track>(data)
|
||||||
|
class TagsCache(data: List<Tag>) : CacheItem<Tag>(data)
|
||||||
|
|
||||||
abstract class OtterResponse<D : Any> {
|
abstract class OtterResponse<D : Any> {
|
||||||
abstract val count: Int
|
abstract val count: Int
|
||||||
|
@ -59,6 +60,10 @@ data class RadiosResponse(override val count: Int, override val next: String?, v
|
||||||
data class Covers(val urls: CoverUrls)
|
data class Covers(val urls: CoverUrls)
|
||||||
data class CoverUrls(val original: String)
|
data class CoverUrls(val original: String)
|
||||||
|
|
||||||
|
data class TagsResponse(override val count: Int, override val next: String?, val results: List<Tag>) : OtterResponse<Tag>() {
|
||||||
|
override fun getData() = results
|
||||||
|
}
|
||||||
|
|
||||||
typealias AlbumList = List<Album>
|
typealias AlbumList = List<Album>
|
||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
|
@ -176,4 +181,6 @@ data class DownloadInfo(
|
||||||
val title: String,
|
val title: String,
|
||||||
val artist: String,
|
val artist: String,
|
||||||
var download: Download?
|
var download: Download?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class Tag(val name: String)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<solid android:color="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="4dp"
|
||||||
|
android:bottomRightRadius="4dp"
|
||||||
|
android:topLeftRadius="4dp"
|
||||||
|
android:topRightRadius="4dp" />
|
||||||
|
|
||||||
|
<padding
|
||||||
|
android:bottom="0dp"
|
||||||
|
android:left="0dp"
|
||||||
|
android:right="0dp"
|
||||||
|
android:top="0dp" />
|
||||||
|
|
||||||
|
</shape>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Tags" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/tags"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Random picks" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/random"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Recently listened" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recently_listened"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Newly uploaded" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recently_added"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/AppTheme.Title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Playlists and radios" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/playlists"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/cover"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:src="@drawable/cover" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="1"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="4dp"
|
||||||
|
android:layout_marginVertical="4dp"
|
||||||
|
android:background="@drawable/rounded"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
Loading…
Reference in New Issue