Merge pull request 'add dynamic spanCount for library/search fragemnt and MediaFragmentSimilar' (#68) from feature/dynamic_span_count into develop

Reviewed-on: #68
This commit is contained in:
Jannik 2022-12-04 15:24:18 +01:00
commit 81a20e0aa9
16 changed files with 226 additions and 221 deletions

View File

@ -27,6 +27,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.children
import androidx.core.view.isVisible
@ -44,7 +45,6 @@ import org.mosad.teapod.ui.activity.main.viewmodel.HomeViewModel
import org.mosad.teapod.util.*
import org.mosad.teapod.util.adapter.MediaEpisodeListAdapter
import org.mosad.teapod.util.adapter.MediaItemListAdapter
import org.mosad.teapod.util.decoration.MediaItemDecoration
class HomeFragment : Fragment() {
@ -52,6 +52,8 @@ class HomeFragment : Fragment() {
private val model: HomeViewModel by viewModels()
private lateinit var binding: FragmentHomeBinding
private val itemOffset = 21
private val playerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
model.updateUpNextItems()
}
@ -64,40 +66,39 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerUpNext.addItemDecoration(MediaItemDecoration(9))
binding.recyclerWatchlist.addItemDecoration(MediaItemDecoration(9))
binding.recyclerRecommendations.addItemDecoration(MediaItemDecoration(9))
binding.recyclerNewTitles.addItemDecoration(MediaItemDecoration(9))
binding.recyclerTopTen.addItemDecoration(MediaItemDecoration(9))
binding.recyclerUpNext.adapter = MediaEpisodeListAdapter(
MediaEpisodeListAdapter.OnClickListener {
playerResult.launch(playerIntent(it.panel.episodeMetadata.seasonId, it.panel.id))
}
},
itemOffset
)
binding.recyclerWatchlist.adapter = MediaItemListAdapter(
MediaItemListAdapter.OnClickListener {
activity?.showFragment(MediaFragment(it.id))
}
},
itemOffset
)
binding.recyclerRecommendations.adapter = MediaItemListAdapter(
MediaItemListAdapter.OnClickListener {
activity?.showFragment(MediaFragment(it.id))
}
},
itemOffset
)
binding.recyclerNewTitles.adapter = MediaItemListAdapter(
MediaItemListAdapter.OnClickListener {
activity?.showFragment(MediaFragment(it.id))
}
},
itemOffset
)
binding.recyclerTopTen.adapter = MediaItemListAdapter(
MediaItemListAdapter.OnClickListener {
activity?.showFragment(MediaFragment(it.id))
}
},
itemOffset
)
binding.textHighlightMyList.setOnClickListener {
@ -108,6 +109,13 @@ class HomeFragment : Fragment() {
// TODO since this might take a few seconds show a loading animation for the watchlist button
}
// set the shimmer items size as it's depending on the screen size
setShimmerLayoutItemSize(binding.shimmerLayoutUpNext)
setShimmerLayoutItemSize(binding.shimmerLayoutWatchlist)
setShimmerLayoutItemSize(binding.shimmerLayoutRecommendations)
setShimmerLayoutItemSize(binding.shimmerLayoutNewTitles)
setShimmerLayoutItemSize(binding.shimmerLayoutTopTen)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
model.onUiState(viewLifecycleOwner.lifecycleScope) { uiState ->
@ -169,10 +177,19 @@ class HomeFragment : Fragment() {
private fun bindUiStateLoading() {
// hide highlights layout
binding.linearHighlight.isVisible = false
println(binding.root.childCount)
binding.root.children.filter { it is ShimmerFrameLayout }.forEach {
it as ShimmerFrameLayout
it.startShimmer()
binding.shimmerLayoutUpNext.startShimmer()
binding.shimmerLayoutWatchlist.startShimmer()
binding.shimmerLayoutRecommendations.startShimmer()
binding.shimmerLayoutNewTitles.startShimmer()
binding.shimmerLayoutTopTen.startShimmer()
}
private fun setShimmerLayoutItemSize(shimmerLayout: ShimmerFrameLayout) {
(shimmerLayout.children.first() as? LinearLayout)?.children?.forEach { child ->
child.layoutParams.apply {
width = (binding.root.measuredWidth / requireContext().resources.getInteger(R.integer.item_media_columns)) - itemOffset
}
}
}

View File

@ -12,14 +12,15 @@ import kotlinx.coroutines.launch
import org.mosad.teapod.databinding.FragmentLibraryBinding
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.util.ItemMedia
import org.mosad.teapod.util.adapter.MediaItemAdapter
import org.mosad.teapod.util.adapter.MediaItemListAdapter
import org.mosad.teapod.util.decoration.MediaItemDecoration
import org.mosad.teapod.util.showFragment
import org.mosad.teapod.util.toItemMediaList
class LibraryFragment : Fragment() {
private lateinit var binding: FragmentLibraryBinding
private lateinit var adapter: MediaItemAdapter
private lateinit var adapter: MediaItemListAdapter
private val itemList = arrayListOf<ItemMedia>()
private val pageSize = 30
@ -33,26 +34,23 @@ class LibraryFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerMediaLibrary.addItemDecoration(MediaItemDecoration(9))
// TODO replace with pagination3
// https://medium.com/swlh/paging3-recyclerview-pagination-made-easy-333c7dfa8797
binding.recyclerMediaLibrary.addOnScrollListener(PaginationScrollListener())
// init async
lifecycleScope.launch {
// create and set the adapter, needs context
context?.let {
val initialResults = Crunchyroll.browse(n = pageSize)
itemList.addAll(initialResults.items.map { item ->
ItemMedia(item.id, item.title, item.images.poster_wide[0][0].source)
})
itemList.addAll(Crunchyroll.browse(n = pageSize).toItemMediaList())
nextItemIndex += pageSize
adapter = MediaItemAdapter(itemList)
adapter.onItemClick = { mediaIdStr, _ ->
activity?.showFragment(MediaFragment(mediaIdStr))
}
adapter = MediaItemListAdapter(MediaItemListAdapter.OnClickListener {
activity?.showFragment(MediaFragment(it.id))
})
binding.recyclerMediaLibrary.adapter = adapter
binding.recyclerMediaLibrary.addItemDecoration(MediaItemDecoration(9))
// TODO replace with pagination3
// https://medium.com/swlh/paging3-recyclerview-pagination-made-easy-333c7dfa8797
binding.recyclerMediaLibrary.addOnScrollListener(PaginationScrollListener())
adapter.submitList(itemList)
}
}
@ -72,10 +70,7 @@ class LibraryFragment : Fragment() {
isLoading = true
lifecycleScope.launch {
val firstNewItemIndex = itemList.lastIndex + 1
val results = Crunchyroll.browse(start = nextItemIndex, n = pageSize)
itemList.addAll(results.items.map { item ->
ItemMedia(item.id, item.title, item.images.poster_wide[0][0].source)
})
itemList.addAll(Crunchyroll.browse(start = nextItemIndex, n = pageSize).toItemMediaList())
nextItemIndex += pageSize
adapter.notifyItemRangeInserted(firstNewItemIndex, pageSize)

View File

@ -9,18 +9,18 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import org.mosad.teapod.databinding.FragmentSearchBinding
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
import org.mosad.teapod.util.ItemMedia
import org.mosad.teapod.util.adapter.MediaItemAdapter
import org.mosad.teapod.util.adapter.MediaItemListAdapter
import org.mosad.teapod.util.decoration.MediaItemDecoration
import org.mosad.teapod.util.showFragment
import org.mosad.teapod.util.toItemMediaList
class SearchFragment : Fragment() {
private lateinit var binding: FragmentSearchBinding
private lateinit var adapter: MediaItemAdapter
private lateinit var adapter: MediaItemListAdapter
private val itemList = arrayListOf<ItemMedia>()
private var searchJob: Job? = null
@ -34,19 +34,13 @@ class SearchFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
// create and set the adapter, needs context
context?.let {
adapter = MediaItemAdapter(itemList)
adapter.onItemClick = { mediaIdStr, _ ->
binding.searchText.clearFocus()
activity?.showFragment(MediaFragment(mediaIdStr))
}
binding.recyclerMediaSearch.addItemDecoration(MediaItemDecoration(9))
binding.recyclerMediaSearch.adapter = adapter
binding.recyclerMediaSearch.addItemDecoration(MediaItemDecoration(9))
}
}
adapter = MediaItemListAdapter(MediaItemListAdapter.OnClickListener {
binding.searchText.clearFocus()
activity?.showFragment(MediaFragment(it.id))
})
binding.recyclerMediaSearch.adapter = adapter
initActions()
}
@ -65,6 +59,9 @@ class SearchFragment : Fragment() {
})
}
/**
* Search for a query string at Crunchyroll and submit the results to the adapter.
*/
private fun search(query: String) {
// if the query hasn't changed since the last successful search, return
if (query == oldSearchQuery) return
@ -79,9 +76,9 @@ class SearchFragment : Fragment() {
itemList.clear() // TODO needs clean up
// TODO add top results first heading
itemList.addAll(results.items[0].items.map { item ->
ItemMedia(item.id, item.title, item.images.poster_wide[0][0].source)
})
adapter.submitList(
results.items.firstOrNull()?.items?.toItemMediaList() ?: listOf<ItemMedia>()
)
// TODO currently only tv shows are supported, hence only the first items array
// should be always present
@ -107,9 +104,6 @@ class SearchFragment : Fragment() {
// })
// }
adapter.notifyDataSetChanged()
//adapter.notifyItemRangeInserted(0, itemList.size)
// after successfully searching the query term, add it as old query, to make sure we
// don't search again if the query hasn't changed
oldSearchQuery = query

View File

@ -11,16 +11,19 @@ import org.mosad.teapod.R
import org.mosad.teapod.databinding.ItemMediaBinding
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
class MediaEpisodeListAdapter(private val onClickListener: OnClickListener) : ListAdapter<ContinueWatchingItem, MediaEpisodeListAdapter.MediaViewHolder>(DiffCallback) {
class MediaEpisodeListAdapter(private val onClickListener: OnClickListener, private val itemOffset: Int = 0) : ListAdapter<ContinueWatchingItem, MediaEpisodeListAdapter.MediaViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaViewHolder {
return MediaViewHolder(
ItemMediaBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val binding = ItemMediaBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
binding.root.layoutParams.apply {
width = (parent.measuredWidth / parent.context.resources.getInteger(R.integer.item_media_columns)) - itemOffset
}
return MediaViewHolder(binding)
}
override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {

View File

@ -1,44 +0,0 @@
package org.mosad.teapod.util.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import org.mosad.teapod.databinding.ItemMediaBinding
import org.mosad.teapod.util.ItemMedia
@Deprecated("Use MediaItemListAdapter instead")
class MediaItemAdapter(private val items: List<ItemMedia>) : RecyclerView.Adapter<MediaItemAdapter.MediaViewHolder>() {
var onItemClick: ((id: String, position: Int) -> Unit)? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaItemAdapter.MediaViewHolder {
return MediaViewHolder(ItemMediaBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: MediaItemAdapter.MediaViewHolder, position: Int) {
holder.binding.root.apply {
holder.binding.textTitle.text = items[position].title
Glide.with(context).load(items[position].posterUrl).into(holder.binding.imagePoster)
}
}
override fun getItemCount(): Int {
return items.size
}
inner class MediaViewHolder(val binding: ItemMediaBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.imageEpisodePlay.isVisible = false // hide the play button for media items
binding.root.setOnClickListener {
onItemClick?.invoke(
items[bindingAdapterPosition].id,
bindingAdapterPosition
)
}
}
}
}

View File

@ -7,19 +7,23 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ItemMediaBinding
import org.mosad.teapod.util.ItemMedia
class MediaItemListAdapter(private val onClickListener: OnClickListener) : ListAdapter<ItemMedia, MediaItemListAdapter.MediaViewHolder>(DiffCallback) {
class MediaItemListAdapter(private val onClickListener: OnClickListener, private val itemOffset: Int = 0) : ListAdapter<ItemMedia, MediaItemListAdapter.MediaViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaViewHolder {
return MediaViewHolder(
ItemMediaBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
val binding = ItemMediaBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
binding.root.layoutParams.apply {
width = (parent.measuredWidth / parent.context.resources.getInteger(R.integer.item_media_columns)) - itemOffset
}
return MediaViewHolder(binding)
}
override fun onBindViewHolder(holder: MediaViewHolder, position: Int) {
@ -36,7 +40,7 @@ class MediaItemListAdapter(private val onClickListener: OnClickListener) : ListA
fun bind(item: ItemMedia) {
binding.textTitle.text = item.title
Glide.with(binding.imagePoster)
Glide.with(binding.root.context)
.load(item.posterUrl)
.into(binding.imagePoster)

View File

@ -120,7 +120,7 @@
<LinearLayout
android:id="@+id/linear_up_next"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="7dp">
@ -139,7 +139,7 @@
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer_layout_up_next"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
tools:visibility="gone">
<LinearLayout
@ -159,7 +159,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_up_next"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_media" />
@ -207,7 +207,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_watchlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_media" />

View File

@ -19,7 +19,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:spanCount="@integer/item_media_columns"
tools:listitem="@layout/item_media" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -16,7 +16,7 @@
android:paddingEnd="3dp"
android:paddingBottom="3dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:spanCount="@integer/item_media_columns"
tools:listitem="@layout/item_media" />
</FrameLayout>

View File

@ -35,7 +35,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_text"
app:spanCount="2"
app:spanCount="@integer/item_media_columns"
tools:listitem="@layout/item_media">
</androidx.recyclerview.widget.RecyclerView>

View File

@ -1,71 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp"
app:cardElevation="4dp">
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintWidth_max="195dp">
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp"
app:cardElevation="4dp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.96">
<FrameLayout
android:id="@+id/frame_image_progress"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/text_title"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth="195dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/media_poster_desc"
android:scaleType="centerCrop"
tools:srcCompat="@color/imagePlaceholder" />
<FrameLayout
android:id="@+id/frame_image_progress"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/text_title"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/image_episode_play"
android:layout_width="wrap_content"
<ImageView
android:id="@+id/image_poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/media_poster_desc"
android:scaleType="fitCenter"
tools:srcCompat="@drawable/placeholder_image" />
<ImageView
android:id="@+id/image_episode_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_circle__black_transparent_24dp"
android:contentDescription="@string/button_play"
app:srcCompat="@drawable/ic_baseline_play_arrow_24"
app:tint="#FFFFFF" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_playhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:max="100"
app:trackColor="#00FFFFFF"
app:trackThickness="2dp" />
</FrameLayout>
<TextView
android:id="@+id/text_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_circle__black_transparent_24dp"
android:contentDescription="@string/button_play"
app:srcCompat="@drawable/ic_baseline_play_arrow_24"
app:tint="#FFFFFF" />
android:gravity="center"
android:lines="2"
android:maxLines="2"
android:padding="3dp"
android:text="@string/text_title_ex"
android:textAlignment="center"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frame_image_progress" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_playhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:max="100"
app:trackColor="#00FFFFFF"
app:trackThickness="2dp" />
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/text_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="2"
android:maxLines="2"
android:padding="3dp"
android:text="@string/text_title_ex"
android:textAlignment="center"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frame_image_progress" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,50 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="3dp"
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp"
app:cardElevation="4dp">
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintWidth_max="195dp">
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="3dp"
android:backgroundTint="?themeSecondary"
app:cardCornerRadius="7dp"
app:cardElevation="4dp"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.96">
<FrameLayout
android:id="@+id/frame_image_progress"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth="195dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintWidth_max="195dp">
<FrameLayout
android:id="@+id/frame_image_progress"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/image_poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?shapeTextBackground"
tools:ignore="ContentDescription" />
</FrameLayout>
<ImageView
android:id="@+id/image_poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?shapeTextBackground"
android:id="@+id/image_dummy_text"
android:layout_width="128dp"
android:layout_height="19dp"
android:layout_margin="11dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frame_image_progress"
app:srcCompat="@drawable/shape_rounded_corner"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
<ImageView
android:id="@+id/image_dummy_text"
android:layout_width="128dp"
android:layout_height="19dp"
android:layout_margin="11dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/frame_image_progress"
app:srcCompat="@drawable/shape_rounded_corner"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_media_columns" type="integer">3</item>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_media_columns" type="integer">4</item>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_media_columns" type="integer">5</item>
</resources>

View File

@ -2,4 +2,5 @@
<resources>
<dimen name="player_styled_progress_layout_height">28dp</dimen>
<dimen name="player_styled_progress_margin_bottom">52dp</dimen>
<item name="item_media_columns" type="integer">2</item>
</resources>