Improve explore fragment

This commit is contained in:
Koitharu
2022-07-07 14:28:06 +03:00
parent b7442fe445
commit 49634a2f52
13 changed files with 232 additions and 175 deletions

View File

@@ -11,16 +11,19 @@ import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter
import org.koitharu.kotatsu.explore.ui.adapter.SourcesHeaderEventListener
import org.koitharu.kotatsu.library.ui.adapter.LibraryAdapter
import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
import org.koitharu.kotatsu.history.ui.HistoryActivity
import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.settings.SettingsActivity
class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
RecyclerViewOwner,
SourcesHeaderEventListener {
ExploreListEventListener, OnListItemClickListener<ExploreItem.Source> {
private val viewModel by viewModel<ExploreViewModel>()
private var exploreAdapter: ExploreAdapter? = null
@@ -35,15 +38,15 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, this)
exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, this, this)
with(binding.recyclerView) {
adapter = exploreAdapter
setHasFixedSize(true)
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
paddingHorizontal = spacing
}
viewModel.items.observe(viewLifecycleOwner) {
exploreAdapter!!.items = it
viewModel.content.observe(viewLifecycleOwner) {
exploreAdapter?.items = it
}
}
@@ -68,6 +71,19 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
startActivity(SettingsActivity.newManageSourcesIntent(view.context))
}
override fun onClick(v: View) {
val intent = when (v.id) {
R.id.button_history -> HistoryActivity.newIntent(v.context)
else -> return
}
startActivity(intent)
}
override fun onItemClick(item: ExploreItem.Source, view: View) {
val intent = MangaListActivity.newIntent(view.context, item.source)
startActivity(intent)
}
override fun onRetryClick(error: Throwable) = Unit
override fun onEmptyActionClick() = Unit

View File

@@ -1,71 +1,40 @@
package org.koitharu.kotatsu.explore.ui
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.getLocaleTitle
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
import org.koitharu.kotatsu.utils.ext.map
import java.util.*
private const val KEY_ENABLED = "!"
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
class ExploreViewModel(
private val settings: AppSettings,
) : BaseViewModel() {
val items = MutableLiveData<List<ExploreItem>>(emptyList())
val content: LiveData<List<ExploreItem>> = settings.observe()
.filter { it == AppSettings.KEY_SOURCES_HIDDEN || it == AppSettings.KEY_SOURCES_ORDER }
.onStart { emit("") }
.map { settings.getMangaSources(includeHidden = false) }
.distinctUntilChanged()
.map { buildList(it) }
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
init {
buildList()
}
private fun buildList() {
val sources = settings.getMangaSources(includeHidden = true)
val hiddenSources = settings.hiddenSources
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
if (it.name !in hiddenSources) {
KEY_ENABLED
} else {
it.locale
}
}
val result = ArrayList<ExploreItem>(sources.size + map.size + 1)
private fun buildList(sources: List<MangaSource>): List<ExploreItem> {
val result = ArrayList<ExploreItem>(sources.size + 2)
result += ExploreItem.Buttons
val enabledSources = map.remove(KEY_ENABLED)
if (!enabledSources.isNullOrEmpty()) {
if (sources.isNotEmpty()) {
result += ExploreItem.Header(R.string.enabled_sources)
enabledSources.mapTo(result) {
ExploreItem.Source(
source = it,
summary = it.getLocaleTitle(),
)
}
}
items.value = result
}
private class LocaleKeyComparator : Comparator<String?> {
private val deviceLocales = LocaleListCompat.getAdjustedDefault()
.map { it.language }
override fun compare(a: String?, b: String?): Int {
when {
a == b -> return 0
a == null -> return 1
b == null -> return -1
}
val ai = deviceLocales.indexOf(a!!)
val bi = deviceLocales.indexOf(b!!)
return when {
ai < 0 && bi < 0 -> a.compareTo(b)
ai < 0 -> 1
bi < 0 -> -1
else -> ai.compareTo(bi)
}
sources.mapTo(result) { ExploreItem.Source(it) }
} else {
// TODO
}
return result
}
}

View File

@@ -3,25 +3,17 @@ package org.koitharu.kotatsu.explore.ui.adapter
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
class ExploreAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
listener: SourcesHeaderEventListener,
listener: ExploreListEventListener,
clickListener: OnListItemClickListener<ExploreItem.Source>,
) : AsyncListDifferDelegationAdapter<ExploreItem>(
ExploreDiffCallback(),
exploreButtonsDelegate(),
sourceHeaderDelegate(listener),
sourceItemDelegate(coil, lifecycleOwner),
) {
init {
delegatesManager
.addDelegate(exploreButtonsDelegate())
.addDelegate(sourceHeaderDelegate(listener = listener))
.addDelegate(sourceItemDelegate(coil, lifecycleOwner))
}
}
exploreButtonsAD(listener),
exploreSourcesHeaderAD(listener),
exploreSourceItemAD(coil, clickListener, lifecycleOwner),
)

View File

@@ -7,22 +7,29 @@ import coil.request.Disposable
import coil.request.ImageRequest
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding
import org.koitharu.kotatsu.databinding.ItemExploreHeaderBinding
import org.koitharu.kotatsu.databinding.ItemExploreSourceBinding
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.image.FaviconFallbackDrawable
fun exploreButtonsDelegate() = adapterDelegateViewBinding<ExploreItem.Buttons, ExploreItem, ItemExploreButtonsBinding>(
fun exploreButtonsAD(
clickListener: View.OnClickListener,
) = adapterDelegateViewBinding<ExploreItem.Buttons, ExploreItem, ItemExploreButtonsBinding>(
{ layoutInflater, parent -> ItemExploreButtonsBinding.inflate(layoutInflater, parent, false) }
) {
binding.localStorage.requestFocus() // stub
binding.buttonBookmarks.setOnClickListener(clickListener)
binding.buttonHistory.setOnClickListener(clickListener)
binding.buttonLocal.setOnClickListener(clickListener)
binding.buttonSuggestions.setOnClickListener(clickListener)
}
} // TODO
fun sourceHeaderDelegate(
listener: SourcesHeaderEventListener,
fun exploreSourcesHeaderAD(
listener: ExploreListEventListener,
) = adapterDelegateViewBinding<ExploreItem.Header, ExploreItem, ItemExploreHeaderBinding>(
{ layoutInflater, parent -> ItemExploreHeaderBinding.inflate(layoutInflater, parent, false) }
) {
@@ -38,8 +45,9 @@ fun sourceHeaderDelegate(
}
}
fun sourceItemDelegate(
fun exploreSourceItemAD(
coil: ImageLoader,
listener: OnListItemClickListener<ExploreItem.Source>,
lifecycleOwner: LifecycleOwner,
) = adapterDelegateViewBinding<ExploreItem.Source, ExploreItem, ItemExploreSourceBinding>(
{ layoutInflater, parent -> ItemExploreSourceBinding.inflate(layoutInflater, parent, false) },
@@ -47,12 +55,19 @@ fun sourceItemDelegate(
) {
var imageRequest: Disposable? = null
val eventListener = AdapterDelegateClickListenerAdapter(this, listener)
binding.root.setOnClickListener(eventListener)
binding.root.setOnLongClickListener(eventListener)
bind {
binding.textViewTitle.text = item.source.title
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name)
imageRequest = ImageRequest.Builder(context)
.data(item.faviconUrl)
.error(R.drawable.ic_favicon_fallback)
.fallback(fallbackIcon)
.placeholder(fallbackIcon)
.error(fallbackIcon)
.target(binding.imageViewCover)
.lifecycle(lifecycleOwner)
.enqueueWith(coil)

View File

@@ -8,9 +8,7 @@ class ExploreDiffCallback : DiffUtil.ItemCallback<ExploreItem>() {
override fun areItemsTheSame(oldItem: ExploreItem, newItem: ExploreItem): Boolean {
return when {
oldItem.javaClass != newItem.javaClass -> false
oldItem is ExploreItem.Buttons && newItem is ExploreItem.Buttons -> {
oldItem == newItem
}
oldItem is ExploreItem.Buttons && newItem is ExploreItem.Buttons -> true
oldItem is ExploreItem.Source && newItem is ExploreItem.Source -> {
oldItem.source == newItem.source
}

View File

@@ -3,8 +3,7 @@ package org.koitharu.kotatsu.explore.ui.adapter
import android.view.View
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
interface SourcesHeaderEventListener : ListStateHolderListener {
interface ExploreListEventListener : ListStateHolderListener, View.OnClickListener {
fun onManageClick(view: View)
}

View File

@@ -2,9 +2,10 @@ package org.koitharu.kotatsu.explore.ui.model
import android.net.Uri
import androidx.annotation.StringRes
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaSource
sealed interface ExploreItem {
sealed interface ExploreItem : ListModel {
object Buttons : ExploreItem
@@ -24,7 +25,6 @@ sealed interface ExploreItem {
class Source(
val source: MangaSource,
val summary: String?,
) : ExploreItem {
val faviconUrl: Uri
@@ -37,15 +37,12 @@ sealed interface ExploreItem {
other as Source
if (source != other.source) return false
if (summary != other.summary) return false
return true
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + summary.hashCode()
return result
return source.hashCode()
}
}

View File

@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel
@@ -24,17 +25,21 @@ class MangaListActivity : BaseActivity<ActivityContainerBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityContainerBinding.inflate(layoutInflater))
val tags = intent.getParcelableExtra<ParcelableMangaTags>(EXTRA_TAGS)?.tags ?: run {
finishAfterTransition()
return
}
val tags = intent.getParcelableExtra<ParcelableMangaTags>(EXTRA_TAGS)?.tags
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val fm = supportFragmentManager
if (fm.findFragmentById(R.id.container) == null) {
val source = intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: tags?.firstOrNull()?.source
if (source == null) {
finishAfterTransition()
return
}
fm.commit {
val fragment = RemoteListFragment.newInstance(tags.first().source)
val fragment = RemoteListFragment.newInstance(source)
replace(R.id.container, fragment)
runOnCommit(ApplyFilterRunnable(fragment, tags))
if (!tags.isNullOrEmpty()) {
runOnCommit(ApplyFilterRunnable(fragment, tags))
}
}
}
}
@@ -70,9 +75,12 @@ class MangaListActivity : BaseActivity<ActivityContainerBinding>() {
companion object {
private const val EXTRA_TAGS = "tags"
private const val EXTRA_SOURCE = "source"
fun newIntent(context: Context, tags: Set<MangaTag>) =
Intent(context, MangaListActivity::class.java)
.putExtra(EXTRA_TAGS, ParcelableMangaTags(tags))
fun newIntent(context: Context, tags: Set<MangaTag>) = Intent(context, MangaListActivity::class.java)
.putExtra(EXTRA_TAGS, ParcelableMangaTags(tags))
fun newIntent(context: Context, source: MangaSource) = Intent(context, MangaListActivity::class.java)
.putExtra(EXTRA_SOURCE, source)
}
}

View File

@@ -0,0 +1,66 @@
package org.koitharu.kotatsu.utils.image
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import androidx.core.graphics.ColorUtils
import kotlin.math.absoluteValue
class FaviconFallbackDrawable(
context: Context,
name: String,
) : Drawable() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val letter = name.take(1).uppercase()
private val color = colorOfString(name)
private val textBounds = Rect()
private val tempRect = Rect()
init {
paint.style = Paint.Style.FILL
paint.textAlign = Paint.Align.CENTER
paint.isFakeBoldText = true
}
override fun draw(canvas: Canvas) {
val cx = bounds.exactCenterX()
paint.color = color
canvas.drawPaint(paint)
paint.color = Color.WHITE
val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom
canvas.drawText(letter, cx, ty, paint)
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
val innerWidth = bounds.width() - (paint.strokeWidth * 2f)
paint.textSize = getTextSizeForWidth(innerWidth, "100%")
paint.getTextBounds(letter, 0, letter.length, textBounds)
invalidateSelf()
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
}
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("Deprecated in Java")
override fun getOpacity() = PixelFormat.TRANSPARENT
private fun getTextSizeForWidth(width: Float, text: String): Float {
val testTextSize = 48f
paint.textSize = testTextSize
paint.getTextBounds(text, 0, text.length, tempRect)
return testTextSize * width / tempRect.width()
}
private fun colorOfString(str: String): Int {
val hue = (str.hashCode() % 360).absoluteValue.toFloat()
return ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
}
}

View File

@@ -5,45 +5,45 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp">
android:padding="4dp"
android:weightSum="4">
<com.google.android.material.button.MaterialButton
android:id="@+id/favourites"
android:id="@+id/button_history"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/history"
app:icon="@drawable/ic_history" />
<com.google.android.material.button.MaterialButton
android:id="@+id/local_storage"
android:id="@+id/button_local"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/local_storage"
app:icon="@drawable/ic_storage" />
<com.google.android.material.button.MaterialButton
android:id="@+id/suggestions"
android:id="@+id/button_suggestions"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/suggestions"
app:icon="@drawable/ic_suggestion" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
android:id="@+id/button_bookmarks"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/bookmarks"
app:icon="@drawable/ic_bookmark" />

View File

@@ -1,62 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:orientation="vertical"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp">
android:rowCount="2">
<com.google.android.material.button.MaterialButton
android:id="@+id/favourites"
android:id="@+id/button_history"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_columnWeight="0.5"
android:layout_gravity="fill_horizontal"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="@string/history"
app:icon="@drawable/ic_history"
app:layout_constraintEnd_toStartOf="@+id/local_storage"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:icon="@drawable/ic_history" />
<com.google.android.material.button.MaterialButton
android:id="@+id/local_storage"
android:id="@+id/button_suggestions"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/local_storage"
app:icon="@drawable/ic_storage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/favourites"
app:layout_constraintTop_toTopOf="@+id/favourites" />
<com.google.android.material.button.MaterialButton
android:id="@+id/suggestions"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_columnWeight="0.5"
android:layout_gravity="fill_horizontal"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="@string/suggestions"
app:icon="@drawable/ic_suggestion"
app:layout_constraintEnd_toStartOf="@+id/downloads"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/favourites"
app:layout_constraintTop_toBottomOf="@+id/favourites" />
app:icon="@drawable/ic_suggestion" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
android:id="@+id/button_local"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/bookmarks"
app:icon="@drawable/ic_bookmark"
app:layout_constraintEnd_toEndOf="@+id/local_storage"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/suggestions"
app:layout_constraintTop_toBottomOf="@+id/local_storage" />
android:layout_columnWeight="0.5"
android:layout_gravity="fill_horizontal"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="@string/local_storage"
app:icon="@drawable/ic_storage" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_bookmarks"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_height="wrap_content"
android:layout_columnWeight="0.5"
android:layout_gravity="fill_horizontal"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:text="@string/bookmarks"
app:icon="@drawable/ic_bookmark" />
</GridLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<LinearLayout
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"
@@ -7,32 +7,26 @@
android:layout_height="?attr/listPreferredItemHeightSmall"
android:background="?selectableItemBackground"
android:clipChildren="false"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/list_spacing">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/imageView_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="32dp"
android:layout_height="32dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
app:shapeAppearance="?shapeAppearanceCornerSmall"
tools:src="@tools:sample/backgrounds/scenic" />
<TextView
android:id="@+id/textView_title"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodyMedium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView_cover"
app:layout_constraintTop_toTopOf="@+id/imageView_cover"
tools:text="Title" />
tools:text="@tools:sample/lorem[2]" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@@ -128,15 +128,17 @@
</style>
<style name="Widget.Kotatsu.ExploreButton" parent="Widget.Material3.Button.TonalButton.Icon">
<item name="android:paddingTop">16dp</item>
<item name="android:paddingBottom">16dp</item>
<item name="android:minHeight">58dp</item>
<item name="android:textColor">?attr/colorOnSurface</item>
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Material3.FloatingActionButton</item>
<item name="singleLine">true</item>
<item name="shapeAppearance">?shapeAppearanceCornerLarge</item>
<item name="iconPadding">16dp</item>
<item name="iconGravity">start</item>
<item name="android:insetTop">2dp</item>
<item name="android:insetBottom">2dp</item>
<item name="iconTint">?attr/colorPrimary</item>
<item name="android:gravity">start|center_vertical</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
<item name="android:textAppearance">?textAppearanceButton</item>
</style>
<style name="ThemeOverlay.Kotatsu.MainToolbar" parent="">