Library UI, artists listing, new album layout, etc

This commit is contained in:
Kelvin
2025-11-09 23:43:13 +01:00
parent 347ef855b3
commit 75ef7085eb
20 changed files with 812 additions and 79 deletions
@@ -1,31 +1,25 @@
package com.futo.platformplayer.fragment.mainactivity.main
import android.content.Intent
import android.content.pm.PackageManager
import android.content.Context
import android.os.Bundle
import android.provider.MediaStore
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.futo.platformplayer.R
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.api.media.structures.AdhocPager
import com.futo.platformplayer.api.media.structures.EmptyPager
import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StateLibrary
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.views.buttons.BigButton
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
import com.futo.platformplayer.toHumanDuration
import com.futo.platformplayer.views.AlbumHeaderView
import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.viewholders.TrackViewHolder
class LibraryAlbumFragment : MainFragment() {
@@ -57,20 +51,34 @@ class LibraryAlbumFragment : MainFragment() {
}
class FragView: VideoListEditorView {
val fragment: LibraryAlbumFragment;
class FragView : FeedView<LibraryAlbumFragment, IPlatformVideo, IPlatformVideo, IPager<IPlatformVideo>, TrackViewHolder> {
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
private val _header: AlbumHeaderView;
private var _album: Album? = null;
private var _tracks: List<IPlatformVideo>? = null;
private var _url: String? = null;
constructor(fragment: LibraryAlbumFragment, inflater: LayoutInflater) : super(inflater) {
this.fragment = fragment;
constructor(fragment: LibraryAlbumFragment, inflater: LayoutInflater) : super(fragment, inflater) {
_header = AlbumHeaderView(context);
_toolbarContentView.addView(_header);
_header.onPlayAll.subscribe {
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
StatePlayer.instance.setPlaylist(playlist, focus = true);
}
}
_header.onShuffle.subscribe {
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
StatePlayer.instance.setPlaylist(playlist, focus = true, shuffle = true);
}
}
}
fun onShown(parameter: Any? = null) {
fun onShown(parameter: Any?) {
val album = if(parameter is String)
StateLibrary.instance.getAlbum(parameter);
else if(parameter is Long)
@@ -81,43 +89,61 @@ class LibraryAlbumFragment : MainFragment() {
if(album == null) {
_album = null;
_tracks = null;
setVideos(listOf(), false);
setPager(EmptyPager());
return;
}
setName(album.name);
_header.setName(album.name);
_header.setThumbnail(album.thumbnail);
val tracks = album.getTracks();
_album = album;
_tracks = tracks;
setMetadata(tracks.size, if(tracks.size > 0) tracks.sumOf { it.duration } else -1);
setVideos(tracks, false, album.thumbnail);
_header.setMetadata("${tracks.size} tracks" + if(tracks.size > 0) ("" + tracks.sumOf { it.duration }.toHumanDuration(false)) else "");
setPager(AdhocPager({listOf()}, tracks));
}
override fun onPlayAllClick() {
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
StatePlayer.instance.setPlaylist(playlist, focus = true);
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<IPlatformVideo>): InsertedViewAdapterWithLoader<TrackViewHolder> {
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
childCountGetter = { dataset.size },
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
childViewHolderFactory = { viewGroup, _ ->
val holder = TrackViewHolder(viewGroup);
holder.onClick.subscribe { c ->
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
val index = playlist.videos.indexOf(c);
if (index == -1)
return@subscribe;
StatePlayer.instance.setPlaylist(playlist, index, true);
}
};
holder.onOptions.subscribe {
if(it is IPlatformVideo)
UISlideOverlays.showVideoOptionsOverlay(it, _overlayContainer);
}
return@InsertedViewAdapterWithLoader holder;
}
);
}
override fun updateSpanCount(){ }
override fun createLayoutManager(recyclerResults: RecyclerView, context: Context): GridLayoutManager {
val glmResults = GridLayoutManager(context, 1)
_swipeRefresh.layoutParams = (_swipeRefresh.layoutParams as MarginLayoutParams?)?.apply {
rightMargin = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
8.0f,
context.resources.displayMetrics
).toInt()
}
return glmResults
}
override fun onShuffleClick() {
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
StatePlayer.instance.setPlaylist(playlist, focus = true, shuffle = true);
}
}
override fun onVideoOptions(video: IPlatformVideo) {
UISlideOverlays.showVideoOptionsOverlay(video, overlayContainer);
}
override fun onVideoClicked(video: IPlatformVideo) {
val playlist = _album?.toPlaylist(_tracks);
if (playlist != null) {
val index = playlist.videos.indexOf(video);
if (index == -1)
return;
StatePlayer.instance.setPlaylist(playlist, index, true);
}
companion object {
private const val TAG = "LibraryArtistsFragmentsView";
}
}
}
@@ -36,9 +36,11 @@ import com.futo.platformplayer.states.StateLibrary
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringStorage
import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.LibraryTypeHeaderView
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.SubscriptionAdapter
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
import com.futo.platformplayer.views.adapters.viewholders.SelectablePlaylist
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.platform.PlatformIndicator
@@ -75,24 +77,41 @@ class LibraryAlbumsFragment : MainFragment() {
fun newInstance() = LibraryAlbumsFragment().apply {}
}
class FragView : FeedView<LibraryAlbumsFragment, Album, Album, IPager<Album>, AlbumViewHolder> {
class FragView : FeedView<LibraryAlbumsFragment, Album, Album, IPager<Album>, AlbumTileViewHolder> {
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
constructor(fragment: LibraryAlbumsFragment, inflater: LayoutInflater) : super(fragment, inflater)
val libraryTypeHeader: LibraryTypeHeaderView;
constructor(fragment: LibraryAlbumsFragment, inflater: LayoutInflater) : super(fragment, inflater) {
libraryTypeHeader = LibraryTypeHeaderView(context);
libraryTypeHeader.setSelectedType(LibraryTypeHeaderView.SelectedType.Albums);
libraryTypeHeader.setMetadata("");
libraryTypeHeader.onSelectedChanged.subscribe {
when(it) {
LibraryTypeHeaderView.SelectedType.Artists -> fragment.navigate<LibraryArtistsFragment>();
else -> {}
}
}
_toolbarContentView.addView(libraryTypeHeader);
}
fun onShown() {
val initialAlbums = StateLibrary.instance.getAlbums();
Logger.i(TAG, "Initial album count: " + initialAlbums.size);
libraryTypeHeader.setMetadata("${initialAlbums.size} albums");
setPager(AdhocPager<Album>({ listOf(); }, initialAlbums));
}
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumViewHolder> {
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumTileViewHolder> {
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
childCountGetter = { dataset.size },
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
childViewHolderFactory = { viewGroup, _ ->
val holder = AlbumViewHolder(viewGroup);
val holder = AlbumTileViewHolder(viewGroup);
holder.setAutoSize(resources.displayMetrics.widthPixels / resources.displayMetrics.density)
holder.onClick.subscribe { c -> fragment.navigate<LibraryAlbumFragment>(c) };
return@InsertedViewAdapterWithLoader holder;
}
@@ -102,12 +121,12 @@ class LibraryAlbumsFragment : MainFragment() {
override fun updateSpanCount(){ }
override fun createLayoutManager(recyclerResults: RecyclerView, context: Context): GridLayoutManager {
val glmResults = GridLayoutManager(context, 1)
val glmResults = GridLayoutManager(context, AlbumTileViewHolder.getAutoSizeColumns(resources.displayMetrics.widthPixels / resources.displayMetrics.density))
_swipeRefresh.layoutParams = (_swipeRefresh.layoutParams as MarginLayoutParams?)?.apply {
rightMargin = TypedValue.applyDimension(
leftMargin = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
8.0f,
3f,
context.resources.displayMetrics
).toInt()
}
@@ -51,6 +51,7 @@ import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.viewholders.AlbumTileViewHolder
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.subscriptions.SubscribeButton
@@ -518,7 +519,7 @@ class LibraryArtistFragment : MainFragment() {
_lastArtist = artist;
}
}
class ArtistAlbumsView : FeedView<LibraryArtistFragment, Album, Album, IPager<Album>, AlbumViewHolder> {
class ArtistAlbumsView : FeedView<LibraryArtistFragment, Album, Album, IPager<Album>, AlbumTileViewHolder> {
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
constructor(fragment: LibraryArtistFragment, inflater: LayoutInflater) : super(fragment, inflater)
@@ -533,12 +534,13 @@ class LibraryArtistFragment : MainFragment() {
setPager(AdhocPager({ listOf() }, initialAlbums));
}
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumViewHolder> {
override fun createAdapter(recyclerResults: RecyclerView, context: Context, dataset: ArrayList<Album>): InsertedViewAdapterWithLoader<AlbumTileViewHolder> {
return InsertedViewAdapterWithLoader(context, arrayListOf(), arrayListOf(),
childCountGetter = { dataset.size },
childViewHolderBinder = { viewHolder, position -> viewHolder.bind(dataset[position]); },
childViewHolderFactory = { viewGroup, _ ->
val holder = AlbumViewHolder(viewGroup);
val holder = AlbumTileViewHolder(viewGroup);
holder.setAutoSize(resources.displayMetrics.widthPixels / resources.displayMetrics.density)
holder.onClick.subscribe { c -> fragment.navigate<LibraryAlbumFragment>(c) };
return@InsertedViewAdapterWithLoader holder;
}
@@ -548,7 +550,7 @@ class LibraryArtistFragment : MainFragment() {
override fun updateSpanCount(){ }
override fun createLayoutManager(recyclerResults: RecyclerView, context: Context): GridLayoutManager {
val glmResults = GridLayoutManager(context, 1)
val glmResults = GridLayoutManager(context, AlbumTileViewHolder.getAutoSizeColumns(resources.displayMetrics.widthPixels / resources.displayMetrics.density))
_swipeRefresh.layoutParams = (_swipeRefresh.layoutParams as MarginLayoutParams?)?.apply {
rightMargin = TypedValue.applyDimension(
@@ -12,6 +12,7 @@ import android.widget.EditText
import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.LinearLayout.GONE
import android.widget.LinearLayout.VISIBLE
import android.widget.Spinner
@@ -37,6 +38,7 @@ import com.futo.platformplayer.states.StateLibrary
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringStorage
import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.LibraryTypeHeaderView
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.SubscriptionAdapter
@@ -80,21 +82,35 @@ class LibraryArtistsFragment : MainFragment() {
class FragView : FeedView<LibraryArtistsFragment, Artist, Artist, IPager<Artist>, ArtistViewHolder> {
override val feedStyle: FeedStyle = FeedStyle.THUMBNAIL; //R.layout.list_creator;
constructor(fragment: LibraryArtistsFragment, inflater: LayoutInflater) : super(fragment, inflater)
val libraryTypeHeader: LibraryTypeHeaderView;
constructor(fragment: LibraryArtistsFragment, inflater: LayoutInflater) : super(fragment, inflater) {
libraryTypeHeader = LibraryTypeHeaderView(context);
libraryTypeHeader.setSelectedType(LibraryTypeHeaderView.SelectedType.Artists);
libraryTypeHeader.setMetadata("");
libraryTypeHeader.onSelectedChanged.subscribe {
when(it) {
LibraryTypeHeaderView.SelectedType.Albums -> fragment.navigate<LibraryAlbumsFragment>();
else -> {}
}
}
_toolbarContentView.addView(libraryTypeHeader);
}
fun onShown() {
val intialArtists = StateLibrary.instance.getArtists(ArtistOrdering.Alphabethic);
Logger.i(TAG, "Initial album count: " + intialArtists.size);
setPager(AdhocPager<Artist>({ listOf(); }, intialArtists));
reload();
}
override fun reload() {
try {
setLoading(true);
val intialArtists = StateLibrary.instance.getArtists(ArtistOrdering.Alphabethic);
Logger.i(TAG, "Initial album count: " + intialArtists.size);
libraryTypeHeader.setMetadata("${intialArtists.size} artists");
setPager(AdhocPager<Artist>({ listOf(); }, intialArtists));
}
finally {
@@ -170,7 +186,13 @@ class LibraryArtistsFragment : MainFragment() {
*/
_textName.text = artist.name;
_textMetadata.text = artist.countTracks?.let { "${it} tracks" } ?: "";
val metaComps = listOf(
artist.countTracks?.let { "${it} tracks" },
artist.countAlbums?.let { "${it} albums" }
).filterNotNull();
_textMetadata.text = metaComps.joinToString(", ");
}
}
@@ -0,0 +1,70 @@
package com.futo.platformplayer.views
import android.content.Context
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.Glide
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
class AlbumHeaderView: ConstraintLayout {
val textName: TextView;
val textMetadata: TextView;
val imageThumbnail: ImageView;
val imageThumbnailBackground: ImageView;
val buttonPlayAll: LinearLayout;
val buttonShuffle: LinearLayout;
val onPlayAll = Event0();
val onShuffle = Event0();
constructor(context: Context) : super(context) {
inflate(context, R.layout.view_album_header, this)
textName = findViewById(R.id.text_name);
textMetadata = findViewById(R.id.text_metadata);
imageThumbnail = findViewById(R.id.image_thumbnail);
imageThumbnailBackground = findViewById(R.id.image_thumbnail_background);
buttonPlayAll = findViewById(R.id.button_play_all);
buttonShuffle = findViewById(R.id.button_shuffle);
buttonPlayAll.setOnClickListener { onPlayAll.emit() };
buttonShuffle.setOnClickListener { onShuffle.emit() };
}
fun setThumbnail(thumbnail: String?) {
if (thumbnail != null)
Glide.with(imageThumbnail)
.load(thumbnail)
.placeholder(R.drawable.placeholder_channel_thumbnail)
.into(imageThumbnail)
else
Glide.with(imageThumbnail)
.load(R.drawable.placeholder_channel_thumbnail)
.into(imageThumbnail);
if (thumbnail != null)
Glide.with(imageThumbnailBackground)
.load(thumbnail)
.placeholder(R.drawable.placeholder_channel_thumbnail)
.into(imageThumbnailBackground)
else
Glide.with(imageThumbnailBackground)
.load(R.drawable.placeholder_channel_thumbnail)
.into(imageThumbnailBackground);
}
fun setName(str: String){
textName.text = str;
}
fun setMetadata(str: String) {
textMetadata.text = str;
}
}
@@ -0,0 +1,67 @@
package com.futo.platformplayer.views
import android.content.Context
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1
class LibraryTypeHeaderView: ConstraintLayout {
var selected: SelectedType = SelectedType.Artists;
val pillArtist: PillV2;
val pillAlbums: PillV2;
val textMetadata: TextView;
val pills: List<PillV2>
val onSelectedChanged = Event1<SelectedType>();
constructor(context: Context) : super(context) {
inflate(context, R.layout.view_library_type_header, this)
textMetadata = findViewById(R.id.text_metadata);
pillArtist = findViewById(R.id.pill_artist);
pillAlbums = findViewById(R.id.pill_albums);
pillArtist.onClick.subscribe {
setSelectedType(SelectedType.Artists, true);
}
pillAlbums.onClick.subscribe {
setSelectedType(SelectedType.Albums, true);
}
pills = listOf(pillArtist, pillAlbums);
setSelectedType(SelectedType.Artists, false);
}
fun setMetadata(str: String) {
textMetadata.text = str;
}
fun setSelectedType(selected: SelectedType, notify: Boolean = false){
this.selected = selected;
pills.forEach { it.setIsEnabled(false) };
when(selected) {
SelectedType.Artists -> {
pillArtist.setIsEnabled(true);
}
SelectedType.Albums -> {
pillAlbums.setIsEnabled(true);
}
}
if(notify)
onSelectedChanged.emit(selected);
}
enum class SelectedType {
Artists,
Albums
}
}
@@ -0,0 +1,51 @@
package com.futo.platformplayer.views
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1
class PillV2: FrameLayout {
val root: FrameLayout;
val text: TextView;
var isToggled: Boolean = false;
val onClick = Event1<Boolean>();
constructor(context: Context, attr: AttributeSet? = null) : super(context, attr) {
inflate(context, R.layout.view_tag_v2, this);
root = findViewById(R.id.root);
text = findViewById(R.id.text_tag);
val attrArr = context.obtainStyledAttributes(attr, R.styleable.PillV2, 0, 0);
val attrEnabled = attrArr.getBoolean(R.styleable.PillV2_pillV2Enabled, false);
val attrText = attrArr.getText(R.styleable.PillV2_pillV2Text) ?: "";
text.text = attrText;
setIsEnabled(attrEnabled);
setOnClickListener {
setIsEnabled(!isToggled);
onClick.emit(isToggled);
}
}
fun setText(text: String) {
this.text.text = text;
}
fun setIsEnabled(enabled: Boolean = true) {
if(enabled)
root.setBackgroundResource(R.drawable.background_2e_round_4dp)
else
root.setBackgroundResource(R.drawable.background_black_2e_round_4dp);
this.isToggled = enabled;
}
}
@@ -5,9 +5,12 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams
import com.bumptech.glide.Glide
import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.dp
import com.futo.platformplayer.states.Album
import com.futo.platformplayer.views.adapters.AnyAdapter
@@ -19,12 +22,14 @@ class AlbumTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<
val onClick = Event1<Album?>();
protected var _root: ConstraintLayout;
protected var _album: Album? = null;
protected val _imageThumbnail: ImageView
protected val _textName: TextView
protected val _textMetadata: TextView
init {
_root = _view.findViewById(R.id.root);
_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
_textName = _view.findViewById(R.id.text_name);
_textMetadata = _view.findViewById(R.id.text_metadata);
@@ -32,6 +37,25 @@ class AlbumTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<
_view.setOnClickListener { onClick.emit(_album) };
}
fun setWidth(dp: Int) {
_root.updateLayoutParams {
this.width = (dp - 12).dp(_viewGroup.context.resources);
this.height = (dp + 48).dp(_viewGroup.context.resources);
}
_imageThumbnail.updateLayoutParams {
this.width = (dp - 12).dp(_viewGroup.context.resources);
this.height = (dp - 12).dp(_viewGroup.context.resources);
}
}
fun setAutoSize(totalWidth: Float) {
val viewWidth = 98;
val dpWidth = totalWidth;
val columns = Math.max(((dpWidth) / viewWidth).toInt(), 1);
val remainder = dpWidth - columns * viewWidth;
val targetSize = viewWidth + (remainder / columns).toInt();
setWidth(targetSize);
}
override fun bind(album: Album) {
_album = album;
@@ -49,4 +73,12 @@ class AlbumTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<
_textMetadata.text = album.artist ?: "";
}
companion object {
fun getAutoSizeColumns(totalWidth: Float): Int {
val viewWidth = 98;
val dpWidth = totalWidth;
val columns = Math.max(((dpWidth) / viewWidth).toInt(), 1);
return columns;
}
}
}
@@ -0,0 +1,63 @@
package com.futo.platformplayer.views.adapters.viewholders
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.toHumanDuration
import com.futo.platformplayer.views.adapters.AnyAdapter
class TrackViewHolder(private val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHolder<IPlatformContent>(
LayoutInflater.from(_viewGroup.context).inflate(
R.layout.list_track,
_viewGroup, false)) {
val onClick = Event1<IPlatformContent>();
val onOptions = Event1<IPlatformContent>();
protected var _content: IPlatformContent? = null;
//protected val _imageThumbnail: ImageView
protected val _textName: TextView
protected val _textMetadata: TextView
protected val _imageSettings: ImageView;
init {
//_imageThumbnail = _view.findViewById(R.id.image_thumbnail);
_textName = _view.findViewById(R.id.text_name);
_textMetadata = _view.findViewById(R.id.text_metadata);
_imageSettings = _view.findViewById(R.id.button_options);
_view.setOnClickListener { _content?.let { onClick.emit(it) } };
_imageSettings.setOnClickListener { _content?.let { onOptions.emit(it) } };
}
override fun bind(content: IPlatformContent) {
_content = content;
/*
_imageThumbnail?.let {
if (artist.thumbnail != null)
Glide.with(it)
.load(artist.thumbnail)
.placeholder(R.drawable.placeholder_channel_thumbnail)
.into(it)
else
Glide.with(it).load(R.drawable.placeholder_channel_thumbnail).into(it);
};
*/
_textName.text = content.name;
val metaComps = listOf(
if(content is IPlatformVideo) "${content.duration.toHumanDuration(false)}" else null
).filterNotNull();
_textMetadata.text = metaComps.joinToString(", ");
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#2e2e2e" />
<corners android:radius="4dp" />
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
<stroke android:width="1dp" android:color="#2e2e2e" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#000000" />
<corners android:radius="4dp" />
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
<stroke android:width="1dp" android:color="#2e2e2e" />
</shape>
@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="428dp"
android:height="230dp"
android:viewportWidth="428"
android:viewportHeight="201">
<path
android:pathData="M0,0h428v201h-428z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="214"
android:startY="0"
android:endX="214"
android:endY="201"
android:type="linear">
<item android:offset="0" android:color="#AA0A0A0A"/>
<item android:offset="1" android:color="#FF000000"/>
</gradient>
</aapt:attr>
</path>
</vector>
+2 -2
View File
@@ -6,8 +6,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/root"
android:clickable="true">
@@ -33,6 +31,7 @@
android:fontFamily="@font/inter_light"
tools:text="Legendary grant recipient: Marvin Wißfeld Very Long Title That is Long"
android:maxLines="2"
android:layout_marginTop="5dp"
app:layout_constraintLeft_toRightOf="@id/image_thumbnail"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_trash"
@@ -47,6 +46,7 @@
android:textSize="10dp"
android:textColor="@color/gray_e0"
android:fontFamily="@font/inter_extra_light"
android:layout_marginBottom="5dp"
tools:text="3 videos"
android:maxLines="1"
app:layout_constraintTop_toBottomOf="@id/text_name"
+7 -5
View File
@@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_width="86dp"
android:layout_height="146dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
@@ -15,13 +15,15 @@
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_thumbnail"
android:layout_height="86dp"
android:layout_width="86dp"
android:layout_width="0dp"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="H,1,1"
app:shapeAppearanceOverlay="@style/roundedCorners_4dp"
app:srcCompat="@drawable/unknown_music"
android:background="@drawable/video_thumbnail_outline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="@+id/text_name"
+4 -4
View File
@@ -11,9 +11,7 @@
android:id="@+id/root"
android:clickable="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:paddingTop="10dp">
android:paddingRight="10dp">
<TextView
android:id="@+id/text_name"
@@ -21,8 +19,9 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="13dp"
android:layout_marginTop="5dp"
android:textColor="@color/white"
android:fontFamily="@font/inter_light"
android:fontFamily="@font/inter_regular"
tools:text="Example Artist"
android:maxLines="1"
app:layout_constraintLeft_toLeftOf="parent"
@@ -37,6 +36,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="10dp"
android:layout_marginBottom="5dp"
android:textColor="@color/gray_e0"
android:fontFamily="@font/inter_extra_light"
tools:text="3 videos"
+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="vertical"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
android:id="@+id/root"
android:clickable="true"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<TextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="13dp"
android:layout_marginTop="15dp"
android:textColor="@color/white"
android:fontFamily="@font/inter_regular"
tools:text="Example Artist"
android:maxLines="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_options"
android:layout_marginRight="20dp"
app:layout_constraintBottom_toTopOf="@id/text_metadata"
android:layout_marginStart="10dp" />
<TextView
android:id="@+id/text_metadata"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="10dp"
android:layout_marginBottom="15dp"
android:textColor="#595959"
android:fontFamily="@font/inter_regular"
tools:text="3 videos"
android:maxLines="1"
app:layout_constraintTop_toBottomOf="@id/text_name"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_options"
android:layout_marginRight="20dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="10dp" />
<ImageView
android:id="@+id/button_options"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ic_settings"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!--
<ImageButton
android:id="@+id/button_play"
android:layout_width="34dp"
android:layout_height="34dp"
app:srcCompat="@drawable/ic_play_white_nopad"
android:scaleType="fitCenter"
android:padding="8dp"
app:layout_constraintRight_toLeftOf="@id/button_trash"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="10dp"/> -->
<View
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#181818" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="0dp"
app:layout_scrollFlags="scroll"
app:contentInsetStart="0dp"
app:contentInsetEnd="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="230dp">
<ImageView
android:id="@+id/image_thumbnail_background"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/background_thumbnail_live"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/bottom_gradient_dark"
android:scaleType="centerCrop" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-220dp"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="@+id/image_thumbnail"
android:layout_width="108dp"
android:layout_height="108dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="30dp"
app:srcCompat="@drawable/background_thumbnail_live"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_medium"
android:textColor="@color/white"
android:textSize="18dp"
tools:text="Playlist name"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/image_thumbnail"
app:layout_constraintLeft_toLeftOf="@id/container_buttons"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_metadata"/>
<TextView
android:id="@+id/text_metadata"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_extra_light"
android:textColor="#595959"
android:textSize="14dp"
tools:text="15 tracks - 1h 15 minutes"
android:layout_marginBottom="15dp"
app:layout_constraintLeft_toLeftOf="@id/container_buttons"
app:layout_constraintBottom_toTopOf="@id/container_buttons"
app:layout_constraintRight_toRightOf="parent" />
<LinearLayout
android:id="@+id/container_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_horizontal"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/button_play_all"
android:layout_width="120dp"
android:layout_height="40dp"
android:background="@drawable/background_button_primary_round"
android:gravity="center"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_play_white_nopad"
android:layout_marginEnd="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_light"
android:textColor="@color/white"
android:textSize="16dp"
android:text="@string/play_all" />
</LinearLayout>
<LinearLayout
android:id="@+id/button_shuffle"
android:layout_width="120dp"
android:layout_height="40dp"
android:background="@drawable/background_button_round"
android:gravity="center"
android:layout_marginStart="5dp"
android:orientation="horizontal">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_shuffle"
android:layout_marginEnd="5dp"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter_light"
android:textColor="@color/white"
android:textSize="16dp"
android:text="@string/shuffle" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.futo.platformplayer.views.SearchView
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="-10dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/container_buttons"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root">
<LinearLayout
android:id="@+id/container_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="7dp"
android:orientation="horizontal">
<com.futo.platformplayer.views.PillV2
android:id="@+id/pill_artist"
app:pillV2Text="Artist"
android:layout_margin="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.futo.platformplayer.views.PillV2
android:id="@+id/pill_albums"
app:pillV2Text="Albums"
android:layout_margin="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:id="@+id/text_metadata"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_margin="3dp"
android:gravity="center_vertical"
android:textSize="13sp"
android:textColor="#D0D0D0"
android:text="0 artists"
app:layout_constraintLeft_toLeftOf="@id/container_tags"
app:layout_constraintTop_toBottomOf="@id/container_tags"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:layout_marginTop="3dp"
android:layout_marginBottom="3dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:background="@drawable/background_black_2e_round_4dp"
android:id="@+id/root">
<TextView
android:id="@+id/text_tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="center"
android:gravity="center"
android:textSize="11dp"
android:fontFamily="@font/inter_light"
tools:text="Artist" />
</FrameLayout>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PillV2">
<attr name="pillV2Enabled" format="boolean" />
<attr name="pillV2Text" format="string" />
</declare-styleable>
</resources>