Browse Source

Grid items optimizations (#6641)

Use ConstraintLayout for ez size ratio calculation and merge cover-only view
holder with compact's
Ivan Iskandar 3 years ago
parent
commit
fad1449de3

+ 7 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt

@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.ui.browse.source.browse
 
-import android.view.View
 import androidx.core.view.isVisible
 import coil.clear
 import coil.imageLoader
@@ -16,14 +15,14 @@ import eu.kanade.tachiyomi.widget.StateImageViewTarget
  * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
  * All the elements from the layout file "item_source_grid" are available in this class.
  *
- * @param view the inflated view for this holder.
+ * @param binding the inflated view for this holder.
  * @param adapter the adapter handling this holder.
  * @constructor creates a new catalogue holder.
  */
-class SourceComfortableGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
-    SourceHolder<SourceComfortableGridItemBinding>(view, adapter) {
-
-    override val binding = SourceComfortableGridItemBinding.bind(view)
+class SourceComfortableGridHolder(
+    override val binding: SourceComfortableGridItemBinding,
+    adapter: FlexibleAdapter<*>
+) : SourceHolder<SourceComfortableGridItemBinding>(binding.root, adapter) {
 
     /**
      * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
@@ -49,15 +48,12 @@ class SourceComfortableGridHolder(private val view: View, private val adapter: F
     }
 
     override fun setImage(manga: Manga) {
-        // For rounded corners
-        binding.card.clipToOutline = true
-
         binding.thumbnail.clear()
         if (!manga.thumbnail_url.isNullOrEmpty()) {
-            val crossfadeDuration = view.context.imageLoader.defaults.transition.let {
+            val crossfadeDuration = binding.root.context.imageLoader.defaults.transition.let {
                 if (it is CrossfadeTransition) it.durationMillis else 0
             }
-            val request = ImageRequest.Builder(view.context)
+            val request = ImageRequest.Builder(binding.root.context)
                 .data(manga)
                 .setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
                 .target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration))

+ 7 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceCompactGridHolder.kt

@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.ui.browse.source.browse
 
-import android.view.View
 import androidx.core.view.isVisible
 import coil.clear
 import coil.imageLoader
@@ -16,14 +15,14 @@ import eu.kanade.tachiyomi.widget.StateImageViewTarget
  * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
  * All the elements from the layout file "item_source_grid" are available in this class.
  *
- * @param view the inflated view for this holder.
+ * @param binding the inflated view for this holder.
  * @param adapter the adapter handling this holder.
  * @constructor creates a new catalogue holder.
  */
-open class SourceCompactGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
-    SourceHolder<SourceCompactGridItemBinding>(view, adapter) {
-
-    override val binding = SourceCompactGridItemBinding.bind(view)
+class SourceCompactGridHolder(
+    override val binding: SourceCompactGridItemBinding,
+    adapter: FlexibleAdapter<*>
+) : SourceHolder<SourceCompactGridItemBinding>(binding.root, adapter) {
 
     /**
      * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
@@ -49,15 +48,12 @@ open class SourceCompactGridHolder(private val view: View, private val adapter:
     }
 
     override fun setImage(manga: Manga) {
-        // For rounded corners
-        binding.card.clipToOutline = true
-
         binding.thumbnail.clear()
         if (!manga.thumbnail_url.isNullOrEmpty()) {
-            val crossfadeDuration = view.context.imageLoader.defaults.transition.let {
+            val crossfadeDuration = binding.root.context.imageLoader.defaults.transition.let {
                 if (it is CrossfadeTransition) it.durationMillis else 0
             }
-            val request = ImageRequest.Builder(view.context)
+            val request = ImageRequest.Builder(binding.root.context)
                 .data(manga)
                 .setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
                 .target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration))

+ 7 - 35
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt

@@ -1,10 +1,6 @@
 package eu.kanade.tachiyomi.ui.browse.source.browse
 
-import android.view.Gravity
 import android.view.View
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.widget.FrameLayout
-import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.recyclerview.widget.RecyclerView
 import com.fredporciuncula.flow.preferences.Preference
 import eu.davidea.flexibleadapter.FlexibleAdapter
@@ -15,16 +11,15 @@ import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
 import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding
 import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.widget.AutofitRecyclerView
 
 class SourceItem(val manga: Manga, private val displayMode: Preference<DisplayModeSetting>) :
     AbstractFlexibleItem<SourceHolder<*>>() {
 
     override fun getLayoutRes(): Int {
         return when (displayMode.get()) {
-            DisplayModeSetting.LIST -> R.layout.source_list_item
+            DisplayModeSetting.COMPACT_GRID, DisplayModeSetting.COVER_ONLY_GRID -> R.layout.source_compact_grid_item
             DisplayModeSetting.COMFORTABLE_GRID -> R.layout.source_comfortable_grid_item
-            else -> R.layout.source_compact_grid_item
+            DisplayModeSetting.LIST -> R.layout.source_list_item
         }
     }
 
@@ -33,37 +28,14 @@ class SourceItem(val manga: Manga, private val displayMode: Preference<DisplayMo
         adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
     ): SourceHolder<*> {
         return when (displayMode.get()) {
-            DisplayModeSetting.LIST -> {
-                SourceListHolder(view, adapter)
+            DisplayModeSetting.COMPACT_GRID, DisplayModeSetting.COVER_ONLY_GRID -> {
+                SourceCompactGridHolder(SourceCompactGridItemBinding.bind(view), adapter)
             }
             DisplayModeSetting.COMFORTABLE_GRID -> {
-                val binding = SourceComfortableGridItemBinding.bind(view)
-                val parent = adapter.recyclerView as AutofitRecyclerView
-                val coverHeight = parent.itemWidth / 3 * 4
-                view.apply {
-                    binding.card.layoutParams = ConstraintLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight
-                    )
-                }
-                SourceComfortableGridHolder(view, adapter)
+                SourceComfortableGridHolder(SourceComfortableGridItemBinding.bind(view), adapter)
             }
-            else -> {
-                val binding = SourceCompactGridItemBinding.bind(view)
-                val parent = adapter.recyclerView as AutofitRecyclerView
-                val coverHeight = parent.itemWidth / 3 * 4
-                view.apply {
-                    binding.card.layoutParams = FrameLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight
-                    )
-                    binding.gradient.layoutParams = FrameLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight / 2,
-                        Gravity.BOTTOM
-                    )
-                }
-                SourceCompactGridHolder(view, adapter)
+            DisplayModeSetting.LIST -> {
+                SourceListHolder(view, adapter)
             }
         }
     }

+ 3 - 9
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryComfortableGridHolder.kt

@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.ui.library
 
-import android.view.View
 import androidx.core.view.isVisible
 import androidx.recyclerview.widget.RecyclerView
 import coil.clear
@@ -13,17 +12,15 @@ import eu.kanade.tachiyomi.util.view.loadAnyAutoPause
  * Class used to hold the displayed data of a manga in the library, like the cover or the title.
  * All the elements from the layout file "item_source_grid" are available in this class.
  *
- * @param view the inflated view for this holder.
+ * @param binding the inflated view for this holder.
  * @param adapter the adapter handling this holder.
  * @param listener a listener to react to single tap and long tap events.
  * @constructor creates a new library holder.
  */
 class LibraryComfortableGridHolder(
-    private val view: View,
+    override val binding: SourceComfortableGridItemBinding,
     adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
-) : LibraryHolder<SourceComfortableGridItemBinding>(view, adapter) {
-
-    override val binding = SourceComfortableGridItemBinding.bind(view)
+) : LibraryHolder<SourceComfortableGridItemBinding>(binding.root, adapter) {
 
     /**
      * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
@@ -57,9 +54,6 @@ class LibraryComfortableGridHolder(
         // set local visibility if its local manga
         binding.badges.localText.isVisible = item.isLocal
 
-        // For rounded corners
-        binding.card.clipToOutline = true
-
         // Update the cover.
         binding.thumbnail.clear()
         binding.thumbnail.loadAnyAutoPause(item.manga)

+ 21 - 14
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCompactGridHolder.kt

@@ -1,6 +1,5 @@
 package eu.kanade.tachiyomi.ui.library
 
-import android.view.View
 import androidx.core.view.isVisible
 import coil.clear
 import eu.davidea.flexibleadapter.FlexibleAdapter
@@ -9,19 +8,18 @@ import eu.kanade.tachiyomi.util.view.loadAnyAutoPause
 
 /**
  * Class used to hold the displayed data of a manga in the library, like the cover or the title.
- * All the elements from the layout file "item_source_grid" are available in this class.
+ * All the elements from the layout file "source_compact_grid_item" are available in this class.
  *
- * @param view the inflated view for this holder.
+ * @param binding the inflated view for this holder.
  * @param adapter the adapter handling this holder.
- * @param listener a listener to react to single tap and long tap events.
+ * @param coverOnly true if title should be hidden a.k.a cover only mode.
  * @constructor creates a new library holder.
  */
-open class LibraryCompactGridHolder(
-    private val view: View,
-    private val adapter: FlexibleAdapter<*>
-) : LibraryHolder<SourceCompactGridItemBinding>(view, adapter) {
-
-    override val binding = SourceCompactGridItemBinding.bind(view)
+class LibraryCompactGridHolder(
+    override val binding: SourceCompactGridItemBinding,
+    adapter: FlexibleAdapter<*>,
+    private val coverOnly: Boolean
+) : LibraryHolder<SourceCompactGridItemBinding>(binding.root, adapter) {
 
     /**
      * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
@@ -55,11 +53,20 @@ open class LibraryCompactGridHolder(
         // set local visibility if its local manga
         binding.badges.localText.isVisible = item.isLocal
 
-        // For rounded corners
-        binding.card.clipToOutline = true
-
         // Update the cover.
         binding.thumbnail.clear()
-        binding.thumbnail.loadAnyAutoPause(item.manga)
+        if (coverOnly) {
+            // Cover only mode: Hides title text unless thumbnail is unavailable
+            if (!item.manga.thumbnail_url.isNullOrEmpty()) {
+                binding.thumbnail.loadAnyAutoPause(item.manga)
+                binding.title.isVisible = false
+            } else {
+                binding.title.text = item.manga.title
+                binding.title.isVisible = true
+            }
+            binding.thumbnail.foreground = null
+        } else {
+            binding.thumbnail.loadAnyAutoPause(item.manga)
+        }
     }
 }

+ 0 - 60
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCoverOnlyGridHolder.kt

@@ -1,60 +0,0 @@
-package eu.kanade.tachiyomi.ui.library
-
-import android.view.View
-import androidx.core.view.isVisible
-import androidx.recyclerview.widget.RecyclerView
-import coil.clear
-import eu.davidea.flexibleadapter.FlexibleAdapter
-import eu.davidea.flexibleadapter.items.IFlexible
-import eu.kanade.tachiyomi.databinding.SourceCoverOnlyGridItemBinding
-import eu.kanade.tachiyomi.util.view.loadAnyAutoPause
-
-class LibraryCoverOnlyGridHolder(
-    view: View,
-    adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
-) : LibraryHolder<SourceCoverOnlyGridItemBinding>(view, adapter) {
-
-    override val binding = SourceCoverOnlyGridItemBinding.bind(view)
-
-    /**
-     * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
-     * holder with the given manga.
-     *
-     * @param item the manga item to bind.
-     */
-    override fun onSetValues(item: LibraryItem) {
-        // For rounded corners
-        binding.badges.leftBadges.clipToOutline = true
-        binding.badges.rightBadges.clipToOutline = true
-
-        // Update the unread count and its visibility.
-        with(binding.badges.unreadText) {
-            isVisible = item.unreadCount > 0
-            text = item.unreadCount.toString()
-        }
-        // Update the download count and its visibility.
-        with(binding.badges.downloadText) {
-            isVisible = item.downloadCount > 0
-            text = item.downloadCount.toString()
-        }
-        // Update the source language and its visibility
-        with(binding.badges.languageText) {
-            isVisible = item.sourceLanguage.isNotEmpty()
-            text = item.sourceLanguage
-        }
-        // set local visibility if its local manga
-        binding.badges.localText.isVisible = item.isLocal
-
-        // For rounded corners
-        binding.card.clipToOutline = true
-
-        // Update the cover.
-        binding.thumbnail.clear()
-        if (!item.manga.thumbnail_url.isNullOrEmpty()) {
-            binding.thumbnail.loadAnyAutoPause(item.manga)
-        } else {
-            // Set manga title
-            binding.title.text = item.manga.title
-        }
-    }
-}

+ 6 - 42
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt

@@ -1,10 +1,6 @@
 package eu.kanade.tachiyomi.ui.library
 
-import android.view.Gravity
 import android.view.View
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.widget.FrameLayout
-import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.recyclerview.widget.RecyclerView
 import com.fredporciuncula.flow.preferences.Preference
 import eu.davidea.flexibleadapter.FlexibleAdapter
@@ -15,10 +11,8 @@ import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.LibraryManga
 import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
 import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding
-import eu.kanade.tachiyomi.databinding.SourceCoverOnlyGridItemBinding
 import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
-import eu.kanade.tachiyomi.widget.AutofitRecyclerView
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 
@@ -47,9 +41,8 @@ class LibraryItem(
 
     override fun getLayoutRes(): Int {
         return when (getDisplayMode()) {
-            DisplayModeSetting.COMPACT_GRID -> R.layout.source_compact_grid_item
+            DisplayModeSetting.COMPACT_GRID, DisplayModeSetting.COVER_ONLY_GRID -> R.layout.source_compact_grid_item
             DisplayModeSetting.COMFORTABLE_GRID -> R.layout.source_comfortable_grid_item
-            DisplayModeSetting.COVER_ONLY_GRID -> R.layout.source_cover_only_grid_item
             DisplayModeSetting.LIST -> R.layout.source_list_item
         }
     }
@@ -57,42 +50,13 @@ class LibraryItem(
     override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): LibraryHolder<*> {
         return when (getDisplayMode()) {
             DisplayModeSetting.COMPACT_GRID -> {
-                val binding = SourceCompactGridItemBinding.bind(view)
-                val parent = adapter.recyclerView as AutofitRecyclerView
-                val coverHeight = parent.itemWidth / 3 * 4
-                view.apply {
-                    binding.card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight)
-                    binding.gradient.layoutParams = FrameLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight / 2,
-                        Gravity.BOTTOM
-                    )
-                }
-                LibraryCompactGridHolder(view, adapter)
-            }
-            DisplayModeSetting.COMFORTABLE_GRID -> {
-                val binding = SourceComfortableGridItemBinding.bind(view)
-                val parent = adapter.recyclerView as AutofitRecyclerView
-                val coverHeight = parent.itemWidth / 3 * 4
-                view.apply {
-                    binding.card.layoutParams = ConstraintLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight
-                    )
-                }
-                LibraryComfortableGridHolder(view, adapter)
+                LibraryCompactGridHolder(SourceCompactGridItemBinding.bind(view), adapter, coverOnly = false)
             }
             DisplayModeSetting.COVER_ONLY_GRID -> {
-                val binding = SourceCoverOnlyGridItemBinding.bind(view)
-                val parent = adapter.recyclerView as AutofitRecyclerView
-                val coverHeight = parent.itemWidth / 3 * 4
-                view.apply {
-                    binding.card.layoutParams = ConstraintLayout.LayoutParams(
-                        MATCH_PARENT,
-                        coverHeight
-                    )
-                }
-                LibraryCoverOnlyGridHolder(view, adapter)
+                LibraryCompactGridHolder(SourceCompactGridItemBinding.bind(view), adapter, coverOnly = true)
+            }
+            DisplayModeSetting.COMFORTABLE_GRID -> {
+                LibraryComfortableGridHolder(SourceComfortableGridItemBinding.bind(view), adapter)
             }
             DisplayModeSetting.LIST -> {
                 LibraryListHolder(view, adapter)

+ 2 - 1
app/src/main/res/drawable/gradient_shape.xml → app/src/main/res/drawable/card_gradient_shape.xml

@@ -5,8 +5,9 @@
     <gradient
         android:angle="90"
         android:centerColor="#00000000"
+        android:centerY="0.3"
         android:endColor="#00ffffff"
         android:startColor="#aa000000" />
 
-    <corners android:radius="0dp" />
+    <corners android:radius="@dimen/card_radius" />
 </shape>

+ 52 - 54
app/src/main/res/layout/source_comfortable_grid_item.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout 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="match_parent"
@@ -9,57 +9,55 @@
     android:foreground="@drawable/library_item_selector_overlay"
     android:padding="4dp">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <com.google.android.material.progressindicator.CircularProgressIndicator
+        android:id="@+id/progress"
+        style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:indeterminate="true"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@+id/thumbnail"
+        app:layout_constraintEnd_toEndOf="@+id/thumbnail"
+        app:layout_constraintStart_toStartOf="@+id/thumbnail"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible" />
+
+    <com.google.android.material.imageview.ShapeableImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="centerCrop"
+        app:layout_constraintDimensionRatio="w,3:2"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Cover"
+        tools:ignore="ContentDescription"
+        tools:src="@mipmap/ic_launcher" />
+
+    <include
+        android:id="@+id/badges"
+        layout="@layout/source_grid_item_badges"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <FrameLayout
-            android:id="@+id/card"
-            android:layout_width="match_parent"
-            android:layout_height="220dp"
-            android:background="@drawable/rounded_rectangle"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent">
-
-            <ImageView
-                android:id="@+id/thumbnail"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="?attr/colorSurface"
-                android:scaleType="centerCrop"
-                tools:ignore="ContentDescription"
-                tools:src="@mipmap/ic_launcher" />
-
-            <include
-                android:id="@+id/badges"
-                layout="@layout/source_grid_item_badges" />
-
-            <com.google.android.material.progressindicator.CircularProgressIndicator
-                android:id="@+id/progress"
-                style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:indeterminate="true"
-                android:visibility="gone" />
-
-        </FrameLayout>
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:padding="4dp"
-            android:textAppearance="?attr/textAppearanceTitleSmall"
-            android:textSize="12sp"
-            android:textColor="@color/source_comfortable_item_title"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/card"
-            tools:text="Sample name" />
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
-
-</FrameLayout>
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="4dp"
+        android:layout_marginTop="4dp"
+        app:layout_constraintEnd_toEndOf="@+id/thumbnail"
+        app:layout_constraintStart_toStartOf="@+id/thumbnail"
+        app:layout_constraintTop_toTopOf="@+id/thumbnail" />
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:padding="4dp"
+        android:textAppearance="?attr/textAppearanceTitleSmall"
+        android:textColor="@color/source_comfortable_item_title"
+        android:textSize="12sp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/thumbnail"
+        tools:text="Sample name" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 59 - 52
app/src/main/res/layout/source_compact_grid_item.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<FrameLayout 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="match_parent"
     android:layout_height="wrap_content"
@@ -8,58 +9,64 @@
     android:foreground="@drawable/library_item_selector_overlay"
     android:padding="4dp">
 
-    <FrameLayout
-        android:id="@+id/card"
-        android:layout_width="wrap_content"
-        android:layout_height="220dp"
-        android:background="@drawable/rounded_rectangle">
-
-        <ImageView
-            android:id="@+id/thumbnail"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:background="?attr/colorSurface"
-            android:scaleType="centerCrop"
-            tools:ignore="ContentDescription"
-            tools:src="@mipmap/ic_launcher" />
-
-        <View
-            android:id="@+id/gradient"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom"
-            android:background="@drawable/gradient_shape" />
+    <com.google.android.material.imageview.ShapeableImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:scaleType="centerCrop"
+        android:foreground="@drawable/card_gradient_shape"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintDimensionRatio="w,2:3"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Cover"
+        tools:ignore="ContentDescription"
+        tools:src="@mipmap/ic_launcher" />
 
-        <include
-            android:id="@+id/badges"
-            layout="@layout/source_grid_item_badges" />
+    <include
+        android:id="@+id/badges"
+        layout="@layout/source_grid_item_badges"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="4dp"
+        android:layout_marginTop="4dp"
+        app:layout_constraintEnd_toEndOf="@+id/thumbnail"
+        app:layout_constraintStart_toStartOf="@+id/thumbnail"
+        app:layout_constraintTop_toTopOf="@+id/thumbnail" />
 
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:padding="8dp"
-            android:shadowColor="@color/md_black_1000"
-            android:shadowDx="0"
-            android:shadowDy="0"
-            android:shadowRadius="4"
-            android:textAppearance="?attr/textAppearanceTitleSmall"
-            android:textSize="12sp"
-            android:textColor="@color/md_white_1000"
-            tools:text="Sample name" />
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:padding="8dp"
+        android:shadowColor="@color/md_black_1000"
+        android:shadowDx="0"
+        android:shadowDy="0"
+        android:shadowRadius="4"
+        android:textAppearance="?attr/textAppearanceTitleSmall"
+        android:textColor="@color/md_white_1000"
+        android:textSize="12sp"
+        app:layout_constraintBottom_toBottomOf="@+id/thumbnail"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        tools:text="Sample name" />
 
-        <com.google.android.material.progressindicator.CircularProgressIndicator
-            android:id="@+id/progress"
-            style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:indeterminate="true"
-            android:visibility="gone" />
-
-    </FrameLayout>
+    <com.google.android.material.progressindicator.CircularProgressIndicator
+        android:id="@+id/progress"
+        style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:indeterminate="true"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible" />
 
-</FrameLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 0 - 68
app/src/main/res/layout/source_cover_only_grid_item.xml

@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout 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="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_margin="2dp"
-    android:background="@drawable/library_item_selector"
-    android:foreground="@drawable/library_item_selector_overlay"
-    android:padding="4dp">
-
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <FrameLayout
-            android:id="@+id/card"
-            android:layout_width="match_parent"
-            android:layout_height="220dp"
-            android:background="@drawable/rounded_rectangle"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent">
-
-            <ImageView
-                android:id="@+id/thumbnail"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="?attr/colorSurface"
-                android:scaleType="centerCrop"
-                tools:ignore="ContentDescription"
-                tools:src="@mipmap/ic_launcher" />
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="bottom"
-                android:ellipsize="end"
-                android:maxLines="2"
-                android:padding="8dp"
-                android:shadowColor="@color/md_black_1000"
-                android:shadowDx="0"
-                android:shadowDy="0"
-                android:shadowRadius="4"
-                android:textAppearance="?attr/textAppearanceTitleSmall"
-                android:textSize="12sp"
-                android:textColor="@color/md_white_1000"
-                tools:text="Sample name" />
-
-            <include
-                android:id="@+id/badges"
-                layout="@layout/source_grid_item_badges" />
-
-            <com.google.android.material.progressindicator.CircularProgressIndicator
-                android:id="@+id/progress"
-                style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center"
-                android:indeterminate="true"
-                android:visibility="gone" />
-
-        </FrameLayout>
-
-    </androidx.constraintlayout.widget.ConstraintLayout>
-
-</FrameLayout>