Merge pull request #887 from Bnyro/master

cleanup and combine duplicated adapters and views
This commit is contained in:
Bnyro 2022-07-28 08:22:41 +02:00 committed by GitHub
commit 23464f09a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 48 additions and 324 deletions

View File

@ -5,7 +5,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.VideoChannelRowBinding import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.dialogs.VideoOptionsDialog import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.ConnectionHelper
@ -29,20 +29,20 @@ class ChannelAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
val binding = VideoChannelRowBinding.inflate(layoutInflater, parent, false) val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
return ChannelViewHolder(binding) return ChannelViewHolder(binding)
} }
override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) { override fun onBindViewHolder(holder: ChannelViewHolder, position: Int) {
val trending = videoFeed[position] val trending = videoFeed[position]
holder.binding.apply { holder.binding.apply {
channelDescription.text = trending.title videoTitle.text = trending.title
channelViews.text = videoInfo.text =
trending.views.formatShort() + "" + trending.views.formatShort() + "" +
DateUtils.getRelativeTimeSpanString(trending.uploaded!!) DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
channelDuration.text = thumbnailDuration.text =
DateUtils.formatElapsedTime(trending.duration!!) DateUtils.formatElapsedTime(trending.duration!!)
ConnectionHelper.loadImage(trending.thumbnail, channelThumbnail) ConnectionHelper.loadImage(trending.thumbnail, thumbnail)
root.setOnClickListener { root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, trending.url) NavigationHelper.navigateVideo(root.context, trending.url)
} }
@ -56,4 +56,4 @@ class ChannelAdapter(
} }
} }
class ChannelViewHolder(val binding: VideoChannelRowBinding) : RecyclerView.ViewHolder(binding.root) class ChannelViewHolder(val binding: VideoRowBinding) : RecyclerView.ViewHolder(binding.root)

View File

@ -7,9 +7,9 @@ import android.view.ViewGroup
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.databinding.ChannelSearchRowBinding import com.github.libretube.databinding.ChannelRowBinding
import com.github.libretube.databinding.PlaylistSearchRowBinding import com.github.libretube.databinding.PlaylistSearchRowBinding
import com.github.libretube.databinding.VideoSearchRowBinding import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.dialogs.PlaylistOptionsDialog import com.github.libretube.dialogs.PlaylistOptionsDialog
import com.github.libretube.dialogs.VideoOptionsDialog import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.obj.SearchItem import com.github.libretube.obj.SearchItem
@ -45,10 +45,10 @@ class SearchAdapter(
return when (viewType) { return when (viewType) {
0 -> SearchViewHolder( 0 -> SearchViewHolder(
VideoSearchRowBinding.inflate(layoutInflater, parent, false) VideoRowBinding.inflate(layoutInflater, parent, false)
) )
1 -> SearchViewHolder( 1 -> SearchViewHolder(
ChannelSearchRowBinding.inflate(layoutInflater, parent, false) ChannelRowBinding.inflate(layoutInflater, parent, false)
) )
2 -> SearchViewHolder( 2 -> SearchViewHolder(
PlaylistSearchRowBinding.inflate(layoutInflater, parent, false) PlaylistSearchRowBinding.inflate(layoutInflater, parent, false)
@ -78,26 +78,26 @@ class SearchAdapter(
} }
} }
private fun bindWatch(item: SearchItem, binding: VideoSearchRowBinding) { private fun bindWatch(item: SearchItem, binding: VideoRowBinding) {
binding.apply { binding.apply {
ConnectionHelper.loadImage(item.thumbnail, searchThumbnail) ConnectionHelper.loadImage(item.thumbnail, thumbnail)
if (item.duration != -1L) { if (item.duration != -1L) {
searchThumbnailDuration.text = DateUtils.formatElapsedTime(item.duration!!) thumbnailDuration.text = DateUtils.formatElapsedTime(item.duration!!)
} else { } else {
searchThumbnailDuration.text = root.context.getString(R.string.live) thumbnailDuration.text = root.context.getString(R.string.live)
searchThumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark) thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
} }
ConnectionHelper.loadImage(item.uploaderAvatar, searchChannelImage) ConnectionHelper.loadImage(item.uploaderAvatar, channelImage)
searchDescription.text = item.title videoTitle.text = item.title
val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else "" val viewsString = if (item.views?.toInt() != -1) item.views.formatShort() else ""
val uploadDate = if (item.uploadedDate != null) item.uploadedDate else "" val uploadDate = if (item.uploadedDate != null) item.uploadedDate else ""
searchViews.text = videoInfo.text =
if (viewsString != "" && uploadDate != "") { if (viewsString != "" && uploadDate != "") {
"$viewsString$uploadDate" "$viewsString$uploadDate"
} else { } else {
viewsString + uploadDate viewsString + uploadDate
} }
searchChannelName.text = item.uploaderName channelName.text = item.uploaderName
root.setOnClickListener { root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, item.url) NavigationHelper.navigateVideo(root.context, item.url)
} }
@ -107,13 +107,13 @@ class SearchAdapter(
.show(childFragmentManager, "VideoOptionsDialog") .show(childFragmentManager, "VideoOptionsDialog")
true true
} }
searchChannelImage.setOnClickListener { channelImage.setOnClickListener {
NavigationHelper.navigateChannel(root.context, item.uploaderUrl) NavigationHelper.navigateChannel(root.context, item.uploaderUrl)
} }
} }
} }
private fun bindChannel(item: SearchItem, binding: ChannelSearchRowBinding) { private fun bindChannel(item: SearchItem, binding: ChannelRowBinding) {
binding.apply { binding.apply {
ConnectionHelper.loadImage(item.thumbnail, searchChannelImage) ConnectionHelper.loadImage(item.thumbnail, searchChannelImage)
searchChannelName.text = item.name searchChannelName.text = item.name
@ -132,7 +132,7 @@ class SearchAdapter(
} }
} }
private fun isSubscribed(channelId: String, token: String, binding: ChannelSearchRowBinding) { private fun isSubscribed(channelId: String, token: String, binding: ChannelRowBinding) {
var isSubscribed = false var isSubscribed = false
// check whether the user subscribed to the channel // check whether the user subscribed to the channel
@ -223,15 +223,15 @@ class SearchAdapter(
} }
class SearchViewHolder : RecyclerView.ViewHolder { class SearchViewHolder : RecyclerView.ViewHolder {
var videoRowBinding: VideoSearchRowBinding? = null var videoRowBinding: VideoRowBinding? = null
var channelRowBinding: ChannelSearchRowBinding? = null var channelRowBinding: ChannelRowBinding? = null
var playlistRowBinding: PlaylistSearchRowBinding? = null var playlistRowBinding: PlaylistSearchRowBinding? = null
constructor(binding: VideoSearchRowBinding) : super(binding.root) { constructor(binding: VideoRowBinding) : super(binding.root) {
videoRowBinding = binding videoRowBinding = binding
} }
constructor(binding: ChannelSearchRowBinding) : super(binding.root) { constructor(binding: ChannelRowBinding) : super(binding.root) {
channelRowBinding = binding channelRowBinding = binding
} }

View File

@ -1,74 +0,0 @@
package com.github.libretube.adapters
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.databinding.TrendingRowBinding
import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.obj.StreamItem
import com.github.libretube.util.ConnectionHelper
import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.formatShort
class SubscriptionAdapter(
private val videoFeed: List<StreamItem>,
private val childFragmentManager: FragmentManager
) : RecyclerView.Adapter<SubscriptionViewHolder>() {
private val TAG = "SubscriptionAdapter"
var i = 0
override fun getItemCount(): Int {
return i
}
fun updateItems() {
i += 10
if (i > videoFeed.size) {
i = videoFeed.size
}
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
return SubscriptionViewHolder(binding)
}
override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
val trending = videoFeed[position]
holder.binding.apply {
textViewTitle.text = trending.title
textViewChannel.text =
trending.uploaderName + "" +
trending.views.formatShort() + "" +
DateUtils.getRelativeTimeSpanString(trending.uploaded!!)
if (trending.duration != -1L) {
thumbnailDuration.text = DateUtils.formatElapsedTime(trending.duration!!)
} else {
thumbnailDuration.text = root.context.getString(R.string.live)
thumbnailDuration.setBackgroundColor(R.attr.colorPrimaryDark)
}
channelImage.setOnClickListener {
NavigationHelper.navigateChannel(root.context, trending.uploaderUrl)
}
ConnectionHelper.loadImage(trending.thumbnail, thumbnail)
ConnectionHelper.loadImage(trending.uploaderAvatar, channelImage)
root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, trending.url)
}
root.setOnLongClickListener {
val videoId = trending.url!!.replace("/watch?v=", "")
VideoOptionsDialog(videoId, root.context)
.show(childFragmentManager, "VideoOptionsDialog")
true
}
}
}
}
class SubscriptionViewHolder(val binding: TrendingRowBinding) :
RecyclerView.ViewHolder(binding.root)

View File

@ -14,23 +14,23 @@ import com.github.libretube.util.NavigationHelper
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
class TrendingAdapter( class TrendingAdapter(
private val videoFeed: List<StreamItem>, private val streamItems: List<StreamItem>,
private val childFragmentManager: FragmentManager private val childFragmentManager: FragmentManager
) : RecyclerView.Adapter<TrendingViewHolder>() { ) : RecyclerView.Adapter<SubscriptionViewHolder>() {
private val TAG = "TrendingAdapter" private val TAG = "SubscriptionAdapter"
override fun getItemCount(): Int { override fun getItemCount(): Int {
return videoFeed.size return streamItems.size
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrendingViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
val binding = TrendingRowBinding.inflate(layoutInflater, parent, false) val binding = TrendingRowBinding.inflate(layoutInflater, parent, false)
return TrendingViewHolder(binding) return SubscriptionViewHolder(binding)
} }
override fun onBindViewHolder(holder: TrendingViewHolder, position: Int) { override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) {
val trending = videoFeed[position] val trending = streamItems[position]
holder.binding.apply { holder.binding.apply {
textViewTitle.text = trending.title textViewTitle.text = trending.title
textViewChannel.text = textViewChannel.text =
@ -48,7 +48,6 @@ class TrendingAdapter(
} }
ConnectionHelper.loadImage(trending.thumbnail, thumbnail) ConnectionHelper.loadImage(trending.thumbnail, thumbnail)
ConnectionHelper.loadImage(trending.uploaderAvatar, channelImage) ConnectionHelper.loadImage(trending.uploaderAvatar, channelImage)
root.setOnClickListener { root.setOnClickListener {
NavigationHelper.navigateVideo(root.context, trending.url) NavigationHelper.navigateVideo(root.context, trending.url)
} }
@ -62,4 +61,5 @@ class TrendingAdapter(
} }
} }
class TrendingViewHolder(val binding: TrendingRowBinding) : RecyclerView.ViewHolder(binding.root) class SubscriptionViewHolder(val binding: TrendingRowBinding) :
RecyclerView.ViewHolder(binding.root)

View File

@ -5,7 +5,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.WatchHistoryRowBinding import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.dialogs.VideoOptionsDialog import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.obj.WatchHistoryItem import com.github.libretube.obj.WatchHistoryItem
import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.ConnectionHelper
@ -26,7 +26,7 @@ class WatchHistoryAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
val binding = WatchHistoryRowBinding.inflate(layoutInflater, parent, false) val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
return WatchHistoryViewHolder(binding) return WatchHistoryViewHolder(binding)
} }
@ -35,7 +35,7 @@ class WatchHistoryAdapter(
holder.binding.apply { holder.binding.apply {
videoTitle.text = video.title videoTitle.text = video.title
channelName.text = video.uploader channelName.text = video.uploader
uploadDate.text = video.uploadDate videoInfo.text = video.uploadDate
thumbnailDuration.text = DateUtils.formatElapsedTime(video.duration?.toLong()!!) thumbnailDuration.text = DateUtils.formatElapsedTime(video.duration?.toLong()!!)
ConnectionHelper.loadImage(video.thumbnailUrl, thumbnail) ConnectionHelper.loadImage(video.thumbnailUrl, thumbnail)
ConnectionHelper.loadImage(video.uploaderAvatar, channelImage) ConnectionHelper.loadImage(video.uploaderAvatar, channelImage)
@ -60,5 +60,5 @@ class WatchHistoryAdapter(
} }
} }
class WatchHistoryViewHolder(val binding: WatchHistoryRowBinding) : class WatchHistoryViewHolder(val binding: VideoRowBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)

View File

@ -14,8 +14,8 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.adapters.SubscriptionAdapter
import com.github.libretube.adapters.SubscriptionChannelAdapter import com.github.libretube.adapters.SubscriptionChannelAdapter
import com.github.libretube.adapters.TrendingAdapter
import com.github.libretube.databinding.FragmentSubscriptionsBinding import com.github.libretube.databinding.FragmentSubscriptionsBinding
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.preferences.PreferenceKeys
@ -29,7 +29,7 @@ class SubscriptionsFragment : Fragment() {
lateinit var token: String lateinit var token: String
private var isLoaded = false private var isLoaded = false
private var subscriptionAdapter: SubscriptionAdapter? = null private var subscriptionAdapter: TrendingAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -59,7 +59,7 @@ class SubscriptionsFragment : Fragment() {
val grid = PreferenceHelper.getString( val grid = PreferenceHelper.getString(
PreferenceKeys.GRID_COLUMNS, PreferenceKeys.GRID_COLUMNS,
resources.getInteger(R.integer.grid_items).toString() resources.getInteger(R.integer.grid_items).toString()
)!! )
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt()) binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
fetchFeed(binding.subFeed, binding.subProgress) fetchFeed(binding.subFeed, binding.subProgress)
@ -85,20 +85,6 @@ class SubscriptionsFragment : Fragment() {
binding.subFeedContainer.visibility = View.VISIBLE binding.subFeedContainer.visibility = View.VISIBLE
} }
} }
binding.scrollviewSub.viewTreeObserver
.addOnScrollChangedListener {
if (binding.scrollviewSub.getChildAt(0).bottom
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
) {
// scroll view is at bottom
if (isLoaded) {
binding.subRefresh.isRefreshing = true
subscriptionAdapter?.updateItems()
binding.subRefresh.isRefreshing = false
}
}
}
} else { } else {
binding.subRefresh.isEnabled = false binding.subRefresh.isEnabled = false
} }
@ -120,9 +106,8 @@ class SubscriptionsFragment : Fragment() {
binding.subRefresh.isRefreshing = false binding.subRefresh.isRefreshing = false
} }
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
subscriptionAdapter = SubscriptionAdapter(response, childFragmentManager) subscriptionAdapter = TrendingAdapter(response, childFragmentManager)
feedRecView.adapter = subscriptionAdapter feedRecView.adapter = subscriptionAdapter
subscriptionAdapter?.updateItems()
} else { } else {
runOnUiThread { runOnUiThread {
with(binding.boogh) { with(binding.boogh) {

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="?android:attr/selectableItemBackground">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".45" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_search_thumbnail"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:strokeWidth="0dp">
<ImageView
android:id="@+id/channel_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="5dp"
app:cardBackgroundColor="@color/duration_background_color"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<TextView
android:id="@+id/channel_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:textColor="@color/duration_text_color"
tools:text="05:36" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/channel_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/channel_views"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toBottomOf="@+id/channel_description" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -73,7 +73,7 @@
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/upload_date" android:id="@+id/video_info"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
@ -88,7 +88,7 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/guideline" app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@id/upload_date" /> app:layout_constraintTop_toBottomOf="@id/video_info" />
<TextView <TextView
android:id="@+id/channel_name" android:id="@+id/channel_name"
@ -100,7 +100,6 @@
android:maxLines="1" android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/channel_image" app:layout_constraintStart_toEndOf="@id/channel_image"
app:layout_constraintTop_toBottomOf="@id/upload_date" /> app:layout_constraintTop_toBottomOf="@id/video_info" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,106 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/video_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="?android:attr/selectableItemBackground">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".45" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_search_thumbnail"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:strokeWidth="0dp">
<ImageView
android:id="@+id/search_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="5dp"
android:layout_marginBottom="3dp"
app:cardBackgroundColor="@color/duration_background_color"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<TextView
android:id="@+id/search_thumbnail_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:textColor="@color/duration_text_color"
android:textSize="11sp"
tools:text="05:36" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/search_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/search_views"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toBottomOf="@+id/search_description" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/search_channel_image"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/search_views" />
<TextView
android:id="@+id/search_channel_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/search_channel_image"
app:layout_constraintTop_toBottomOf="@+id/search_views" />
</androidx.constraintlayout.widget.ConstraintLayout>