Article support

This commit is contained in:
Kelvin 2025-05-29 16:51:06 +02:00
commit cbfd9ea559
7 changed files with 214 additions and 79 deletions

View file

@ -32,7 +32,8 @@ let Type = {
Text: { Text: {
RAW: 0, RAW: 0,
HTML: 1, HTML: 1,
MARKUP: 2 MARKUP: 2,
CODE: 3
}, },
Chapter: { Chapter: {
NORMAL: 0, NORMAL: 0,
@ -323,9 +324,10 @@ class ArticleTextSegment extends ArticleSegment {
} }
} }
class ArticleImagesSegment extends ArticleSegment { class ArticleImagesSegment extends ArticleSegment {
constructor(images) { constructor(images, caption) {
super(2); super(2);
this.images = images; this.images = images;
this.caption = caption;
} }
} }
class ArticleNestedSegment extends ArticleSegment { class ArticleNestedSegment extends ArticleSegment {

View file

@ -25,7 +25,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -851,7 +850,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
Logger.i(TAG, "handleUrl(url=$url) on IO"); Logger.i(TAG, "handleUrl(url=$url) on IO");
if (StatePlatform.instance.hasEnabledVideoClient(url)) { if (StatePlatform.instance.hasEnabledContentClient(url)) {
Logger.i(TAG, "handleUrl(url=$url) found video client"); Logger.i(TAG, "handleUrl(url=$url) found video client");
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
if (position > 0) if (position > 0)

View file

@ -5,7 +5,8 @@ import com.futo.platformplayer.api.media.exceptions.UnknownPlatformException
enum class TextType(val value: Int) { enum class TextType(val value: Int) {
RAW(0), RAW(0),
HTML(1), HTML(1),
MARKUP(2); MARKUP(2),
CODE(3);
companion object { companion object {
fun fromInt(value: Int): TextType fun fromInt(value: Int): TextType

View file

@ -2,8 +2,11 @@ package com.futo.platformplayer.fragment.mainactivity.main
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Typeface
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.os.Bundle import android.os.Bundle
import android.text.Html
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -16,23 +19,33 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.Thumbnails
import com.futo.platformplayer.api.media.models.article.IPlatformArticle import com.futo.platformplayer.api.media.models.article.IPlatformArticle
import com.futo.platformplayer.api.media.models.article.IPlatformArticleDetails import com.futo.platformplayer.api.media.models.article.IPlatformArticleDetails
import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment import com.futo.platformplayer.api.media.models.comments.PolycentricPlatformComment
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent
import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent
import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.post.IPlatformPost
import com.futo.platformplayer.api.media.models.post.IPlatformPostDetails import com.futo.platformplayer.api.media.models.post.IPlatformPostDetails
import com.futo.platformplayer.api.media.models.post.TextType
import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
import com.futo.platformplayer.api.media.models.ratings.RatingLikes import com.futo.platformplayer.api.media.models.ratings.RatingLikes
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.api.media.platforms.js.models.JSArticleDetails import com.futo.platformplayer.api.media.platforms.js.models.JSArticleDetails
import com.futo.platformplayer.api.media.platforms.js.models.JSImagesSegment import com.futo.platformplayer.api.media.platforms.js.models.JSImagesSegment
import com.futo.platformplayer.api.media.platforms.js.models.JSNestedSegment
import com.futo.platformplayer.api.media.platforms.js.models.JSTextSegment import com.futo.platformplayer.api.media.platforms.js.models.JSTextSegment
import com.futo.platformplayer.api.media.platforms.js.models.SegmentType import com.futo.platformplayer.api.media.platforms.js.models.SegmentType
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
@ -43,10 +56,16 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StatePolycentric
import com.futo.platformplayer.toHumanNowDiffString import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanNumber import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.adapters.feedtypes.PreviewLockedView
import com.futo.platformplayer.views.adapters.feedtypes.PreviewNestedVideoView
import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView
import com.futo.platformplayer.views.adapters.feedtypes.PreviewVideoView
import com.futo.platformplayer.views.comments.AddCommentView import com.futo.platformplayer.views.comments.AddCommentView
import com.futo.platformplayer.views.others.CreatorThumbnail import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.overlays.RepliesOverlay import com.futo.platformplayer.views.overlays.RepliesOverlay
@ -126,6 +145,7 @@ class ArticleDetailFragment : MainFragment {
private val _channelMeta: TextView; private val _channelMeta: TextView;
private val _textTitle: TextView; private val _textTitle: TextView;
private val _textMeta: TextView; private val _textMeta: TextView;
private val _textSummary: TextView;
private val _containerSegments: LinearLayout; private val _containerSegments: LinearLayout;
private val _platformIndicator: PlatformIndicator; private val _platformIndicator: PlatformIndicator;
private val _buttonShare: ImageButton; private val _buttonShare: ImageButton;
@ -143,6 +163,7 @@ class ArticleDetailFragment : MainFragment {
private val _layoutLoadingOverlay: FrameLayout; private val _layoutLoadingOverlay: FrameLayout;
private val _imageLoader: ImageView; private val _imageLoader: ImageView;
private var _overlayContainer: FrameLayout
private val _repliesOverlay: RepliesOverlay; private val _repliesOverlay: RepliesOverlay;
private val _commentsList: CommentsList; private val _commentsList: CommentsList;
@ -187,10 +208,12 @@ class ArticleDetailFragment : MainFragment {
_channelMeta = findViewById(R.id.text_channel_meta); _channelMeta = findViewById(R.id.text_channel_meta);
_textTitle = findViewById(R.id.text_title); _textTitle = findViewById(R.id.text_title);
_textMeta = findViewById(R.id.text_meta); _textMeta = findViewById(R.id.text_meta);
_textSummary = findViewById(R.id.text_summary);
_containerSegments = findViewById(R.id.container_segments); _containerSegments = findViewById(R.id.container_segments);
_platformIndicator = findViewById(R.id.platform_indicator); _platformIndicator = findViewById(R.id.platform_indicator);
_buttonShare = findViewById(R.id.button_share); _buttonShare = findViewById(R.id.button_share);
_overlayContainer = findViewById(R.id.overlay_container);
_layoutRating = findViewById(R.id.layout_rating); _layoutRating = findViewById(R.id.layout_rating);
_imageLikeIcon = findViewById(R.id.image_like_icon); _imageLikeIcon = findViewById(R.id.image_like_icon);
@ -449,6 +472,8 @@ class ArticleDetailFragment : MainFragment {
_textTitle.text = value.name; _textTitle.text = value.name;
_textMeta.text = value.datetime?.toHumanNowDiffString()?.let { "$it ago" } ?: "" //TODO: Include view count? _textMeta.text = value.datetime?.toHumanNowDiffString()?.let { "$it ago" } ?: "" //TODO: Include view count?
_textSummary.text = value.summary
_textSummary.isVisible = !value.summary.isNullOrEmpty()
_platformIndicator.setPlatformFromClientID(value.id.pluginId); _platformIndicator.setPlatformFromClientID(value.id.pluginId);
setPlatformRating(value.rating); setPlatformRating(value.rating);
@ -457,12 +482,18 @@ class ArticleDetailFragment : MainFragment {
when(seg.type) { when(seg.type) {
SegmentType.TEXT -> { SegmentType.TEXT -> {
if(seg is JSTextSegment) { if(seg is JSTextSegment) {
_containerSegments.addView(ArticleTextBlock(context, seg.content, seg.textType))
} }
} }
SegmentType.IMAGES -> { SegmentType.IMAGES -> {
if(seg is JSImagesSegment) { if(seg is JSImagesSegment) {
if(seg.images.size > 0)
_containerSegments.addView(ArticleImageBlock(context, seg.images[0], seg.caption))
}
}
SegmentType.NESTED -> {
if(seg is JSNestedSegment) {
_containerSegments.addView(ArticleContentBlock(context, seg.nested, _fragment, _overlayContainer));
} }
} }
else ->{} else ->{}
@ -659,14 +690,86 @@ class ArticleDetailFragment : MainFragment {
} }
} }
class ArticleTextBlock : View { class ArticleTextBlock : LinearLayout {
constructor(context: Context?) : super(context){ constructor(context: Context?, content: String, textType: TextType) : super(context){
inflate(context, R.layout.view_segment_text, this);
findViewById<TextView>(R.id.text_content)?.let {
if(textType == TextType.HTML)
it.text = Html.fromHtml(content, Html.FROM_HTML_MODE_COMPACT);
else if(textType == TextType.CODE) {
it.text = content;
it.setPadding(15.dp(resources));
it.setHorizontallyScrolling(true);
it.movementMethod = ScrollingMovementMethod();
it.setTypeface(Typeface.MONOSPACE);
it.setBackgroundResource(R.drawable.background_videodetail_description)
}
else
it.text = content;
}
} }
} }
class ArticleImageBlock: View { class ArticleImageBlock: LinearLayout {
constructor(context: Context?) : super(context){ constructor(context: Context?, image: String, caption: String? = null) : super(context){
inflate(context, R.layout.view_segment_image, this);
findViewById<ImageView>(R.id.image_content)?.let {
Glide.with(it)
.load(image)
.crossfade()
.into(it);
}
findViewById<TextView>(R.id.text_content)?.let {
if(caption?.isNullOrEmpty() == true)
it.isVisible = false;
else
it.text = caption;
}
}
}
class ArticleContentBlock: LinearLayout {
constructor(context: Context, content: IPlatformContent?, fragment: ArticleDetailFragment? = null, overlayContainer: FrameLayout? = null): super(context) {
if(content != null) {
var view: View? = null;
if(content is IPlatformNestedContent) {
view = PreviewNestedVideoView(context, FeedStyle.THUMBNAIL, null);
view.bind(content);
view.onContentUrlClicked.subscribe { a,b -> }
}
else if(content is IPlatformVideo) {
view = PreviewVideoView(context, FeedStyle.THUMBNAIL, null, true);
view.bind(content);
view.onVideoClicked.subscribe { a,b -> fragment?.navigate<VideoDetailFragment>(a) }
view.onChannelClicked.subscribe { a -> fragment?.navigate<ChannelFragment>(a) }
if(overlayContainer != null) {
view.onAddToClicked.subscribe { a -> UISlideOverlays.showVideoOptionsOverlay(a, overlayContainer) };
}
view.onAddToQueueClicked.subscribe { a -> StatePlayer.instance.addToQueue(a) }
view.onAddToWatchLaterClicked.subscribe { a ->
if(StatePlaylists.instance.addToWatchLater(SerializedPlatformVideo.fromVideo(content), true))
UIDialogs.toast("Added to watch later\n[${content.name}]")
}
}
else if(content is IPlatformPost) {
view = PreviewPostView(context, FeedStyle.THUMBNAIL);
view.bind(content);
view.onContentClicked.subscribe { a -> fragment?.navigate<PostDetailFragment>(a) }
view.onChannelClicked.subscribe { a -> fragment?.navigate<ChannelFragment>(a) }
}
else if(content is IPlatformArticle) {
view = PreviewPostView(context, FeedStyle.THUMBNAIL);
view.bind(content);
view.onContentClicked.subscribe { a -> fragment?.navigate<ArticleDetailFragment>(a) }
view.onChannelClicked.subscribe { a -> fragment?.navigate<ChannelFragment>(a) }
}
else if(content is IPlatformLockedContent) {
view = PreviewLockedView(context, FeedStyle.THUMBNAIL);
view.bind(content);
}
if(view != null)
addView(view);
}
} }
} }

View file

@ -5,7 +5,6 @@ import androidx.collection.LruCache
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.IPluginSourced import com.futo.platformplayer.api.media.IPluginSourced
import com.futo.platformplayer.api.media.PlatformMultiClientPool import com.futo.platformplayer.api.media.PlatformMultiClientPool
@ -46,7 +45,6 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.ImageVariable import com.futo.platformplayer.models.ImageVariable
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringArrayStorage import com.futo.platformplayer.stores.StringArrayStorage
import com.futo.platformplayer.stores.StringStorage
import com.futo.platformplayer.views.ToastView import com.futo.platformplayer.views.ToastView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
@ -56,7 +54,6 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.internal.concat
import java.lang.Thread.sleep import java.lang.Thread.sleep
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.streams.asSequence import kotlin.streams.asSequence
@ -669,7 +666,7 @@ class StatePlatform {
//Video //Video
fun hasEnabledVideoClient(url: String) : Boolean = getEnabledClients().any { _instantClientPool.getClientPooled(it).isContentDetailsUrl(url) }; fun hasEnabledContentClient(url: String) : Boolean = getEnabledClients().any { _instantClientPool.getClientPooled(it).isContentDetailsUrl(url) };
fun getContentClient(url: String) : IPlatformClient = getContentClientOrNull(url) fun getContentClient(url: String) : IPlatformClient = getContentClientOrNull(url)
?: throw NoPlatformClientException("No client enabled that supports this content url (${url})"); ?: throw NoPlatformClientException("No client enabled that supports this content url (${url})");
fun getContentClientOrNull(url: String) : IPlatformClient? = getEnabledClients().find { _instantClientPool.getClientPooled(it).isContentDetailsUrl(url) }; fun getContentClientOrNull(url: String) : IPlatformClient? = getEnabledClients().find { _instantClientPool.getClientPooled(it).isContentDetailsUrl(url) };

View file

@ -21,6 +21,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.PlatformAuthorLink
import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.Thumbnails
import com.futo.platformplayer.api.media.models.article.IPlatformArticle
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.post.IPlatformPost
import com.futo.platformplayer.api.media.models.post.IPlatformPostDetails import com.futo.platformplayer.api.media.models.post.IPlatformPostDetails
@ -141,6 +142,11 @@ class PreviewPostView : LinearLayout {
content.content content.content
else else
"" ""
} else if(content is IPlatformArticle) {
if(!content.summary.isNullOrEmpty())
content.summary ?: ""
else
""
} else ""; } else "";
if (content.name.isNullOrEmpty()) { if (content.name.isNullOrEmpty()) {
@ -154,7 +160,14 @@ class PreviewPostView : LinearLayout {
if (content is IPlatformPost) { if (content is IPlatformPost) {
setImages(content.thumbnails.filterNotNull()); setImages(content.thumbnails.filterNotNull());
} else { }
else if(content is IPlatformArticle) {
if(content.thumbnails != null)
setImages(listOf(content.thumbnails!!));
else
setImages(null);
}
else {
setImages(null); setImages(null);
} }

View file

@ -16,6 +16,69 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:layout_marginTop="16dp">
<LinearLayout
android:id="@+id/layout_channel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_subscribe">
<com.futo.platformplayer.views.others.CreatorThumbnail
android:id="@+id/creator_thumbnail"
android:layout_width="27dp"
android:layout_height="27dp"
android:contentDescription="@string/cd_creator_thumbnail" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="6dp"
android:orientation="vertical">
<TextView
android:id="@+id/text_channel_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="center"
android:layout_marginTop="-4dp"
android:ellipsize="end"
android:maxLines="1"
tools:text="Channel Name" />
<TextView
android:id="@+id/text_channel_meta"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textColor="#ACACAC"
android:textSize="9sp"
android:layout_gravity="center"
android:ellipsize="end"
android:maxLines="1"
tools:text="" />
</LinearLayout>
</LinearLayout>
<com.futo.platformplayer.views.subscriptions.SubscribeButton
android:id="@+id/button_subscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView <TextView
android:id="@+id/text_title" android:id="@+id/text_title"
@ -26,7 +89,7 @@
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="17sp" android:textSize="17sp"
android:textIsSelectable="true" android:textIsSelectable="true"
android:layout_marginTop="8dp" android:layout_marginTop="6dp"
android:layout_marginLeft="14dp" android:layout_marginLeft="14dp"
android:layout_marginRight="14dp" /> android:layout_marginRight="14dp" />
@ -113,68 +176,17 @@
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout <TextView
android:layout_width="match_parent" android:id="@+id/text_summary"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_marginLeft="14dp" android:layout_height="18dp"
android:layout_marginRight="14dp" android:gravity="center_vertical"
android:layout_marginTop="10dp"> android:layout_marginStart="14dp"
android:layout_marginEnd="5dp"
<LinearLayout android:textFontWeight="400"
android:id="@+id/layout_channel_button" tools:text="This is the summary of the article"
android:layout_width="0dp" android:textColor="@color/white"
android:layout_height="wrap_content" android:textSize="13sp" />
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@id/button_subscribe">
<com.futo.platformplayer.views.others.CreatorThumbnail
android:id="@+id/creator_thumbnail"
android:layout_width="27dp"
android:layout_height="27dp"
android:contentDescription="@string/cd_creator_thumbnail" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="6dp"
android:orientation="vertical">
<TextView
android:id="@+id/text_channel_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_gravity="center"
android:layout_marginTop="-4dp"
android:ellipsize="end"
android:maxLines="1"
tools:text="Channel Name" />
<TextView
android:id="@+id/text_channel_meta"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textColor="#ACACAC"
android:textSize="9sp"
android:layout_gravity="center"
android:ellipsize="end"
android:maxLines="1"
tools:text="" />
</LinearLayout>
</LinearLayout>
<com.futo.platformplayer.views.subscriptions.SubscribeButton
android:id="@+id/button_subscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <LinearLayout
android:id="@+id/container_segments" android:id="@+id/container_segments"
@ -294,4 +306,12 @@
android:visibility="gone" android:visibility="gone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:elevation="15dp">
</FrameLayout>
</FrameLayout> </FrameLayout>