Improve explore fragment

pull/163/head
Koitharu 4 years ago
parent b7442fe445
commit 49634a2f52
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -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

@ -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())
init {
buildList()
}
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())
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
}
}

@ -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),
)

@ -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
} // TODO
binding.buttonBookmarks.setOnClickListener(clickListener)
binding.buttonHistory.setOnClickListener(clickListener)
binding.buttonLocal.setOnClickListener(clickListener)
binding.buttonSuggestions.setOnClickListener(clickListener)
}
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)

@ -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
}

@ -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)
}

@ -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()
}
}

@ -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)
}
}

@ -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))
}
}

@ -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" />

@ -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" />
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" />
<com.google.android.material.button.MaterialButton
android:id="@+id/suggestions"
android:id="@+id/button_local"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
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" />
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" />
<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_marginTop="8dp"
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"
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" />
app:icon="@drawable/ic_bookmark" />
</androidx.constraintlayout.widget.ConstraintLayout>
</GridLayout>

@ -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>

@ -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="">

Loading…
Cancel
Save