Initial adding Explore screen

This commit is contained in:
Zakhar Timoshenko
2022-07-07 00:14:21 +03:00
parent 6a8a6a08db
commit b9428e3898
24 changed files with 568 additions and 24 deletions

View File

@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.core.network.networkModule
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.uiModule
import org.koitharu.kotatsu.details.detailsModule
import org.koitharu.kotatsu.explore.exploreModule
import org.koitharu.kotatsu.favourites.favouritesModule
import org.koitharu.kotatsu.history.historyModule
import org.koitharu.kotatsu.library.libraryModule
@@ -79,6 +80,7 @@ class KotatsuApp : Application() {
shikimoriModule,
bookmarksModule,
libraryModule,
exploreModule,
)
}
}

View File

@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.base.ui.util.ActionModeDelegate
import org.koitharu.kotatsu.base.ui.util.WindowInsetsDelegate
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.settings.SettingsActivity
abstract class BaseActivity<B : ViewBinding> :
AppCompatActivity(),

View File

@@ -332,7 +332,8 @@ class DetailsFragment :
override fun onWindowInsetsChanged(insets: Insets) {
binding.root.updatePadding(
bottom = insets.bottom,
left = insets.left,
right = insets.right,
)
}

View File

@@ -0,0 +1,10 @@
package org.koitharu.kotatsu.explore
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.explore.ui.ExploreViewModel
val exploreModule
get() = module {
viewModel { ExploreViewModel(get()) }
}

View File

@@ -0,0 +1,79 @@
package org.koitharu.kotatsu.explore.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
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.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.settings.SettingsActivity
class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
RecyclerViewOwner,
SourcesHeaderEventListener {
private val viewModel by viewModel<ExploreViewModel>()
private var exploreAdapter: ExploreAdapter? = null
private var paddingHorizontal = 0
override val recyclerView: RecyclerView
get() = binding.recyclerView
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): FragmentExploreBinding {
return FragmentExploreBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, 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
}
}
override fun onDestroyView() {
super.onDestroyView()
exploreAdapter = null
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.root.updatePadding(
left = insets.left,
right = insets.right,
)
binding.recyclerView.updatePadding(
left = insets.left + paddingHorizontal,
right = insets.right + paddingHorizontal,
bottom = insets.bottom,
)
}
override fun onManageClick(view: View) {
startActivity(SettingsActivity.newManageSourcesIntent(view.context))
}
override fun onRetryClick(error: Throwable) = Unit
override fun onEmptyActionClick() = Unit
companion object {
fun newInstance() = ExploreFragment()
}
}

View File

@@ -0,0 +1,71 @@
package org.koitharu.kotatsu.explore.ui
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
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 = "!"
class ExploreViewModel(
private val settings: AppSettings,
) : BaseViewModel() {
val items = MutableLiveData<List<ExploreItem>>(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)
result += ExploreItem.Buttons
val enabledSources = map.remove(KEY_ENABLED)
if (!enabledSources.isNullOrEmpty()) {
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)
}
}
}
}

View File

@@ -0,0 +1,27 @@
package org.koitharu.kotatsu.explore.ui.adapter
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
class ExploreAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
listener: SourcesHeaderEventListener,
) : AsyncListDifferDelegationAdapter<ExploreItem>(
ExploreDiffCallback(),
exploreButtonsDelegate(),
sourceHeaderDelegate(listener),
sourceItemDelegate(coil, lifecycleOwner),
) {
init {
delegatesManager
.addDelegate(exploreButtonsDelegate())
.addDelegate(sourceHeaderDelegate(listener = listener))
.addDelegate(sourceItemDelegate(coil, lifecycleOwner))
}
}

View File

@@ -0,0 +1,65 @@
package org.koitharu.kotatsu.explore.ui.adapter
import android.view.View
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import coil.request.Disposable
import coil.request.ImageRequest
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
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
fun exploreButtonsDelegate() = adapterDelegateViewBinding<ExploreItem.Buttons, ExploreItem, ItemExploreButtonsBinding>(
{ layoutInflater, parent -> ItemExploreButtonsBinding.inflate(layoutInflater, parent, false) }
) {
binding.localStorage.requestFocus() // stub
} // TODO
fun sourceHeaderDelegate(
listener: SourcesHeaderEventListener,
) = adapterDelegateViewBinding<ExploreItem.Header, ExploreItem, ItemExploreHeaderBinding>(
{ layoutInflater, parent -> ItemExploreHeaderBinding.inflate(layoutInflater, parent, false) }
) {
val listenerAdapter = View.OnClickListener {
listener.onManageClick(itemView)
}
binding.buttonMore.setOnClickListener(listenerAdapter)
bind {
binding.textViewTitle.setText(R.string.remote_sources)
}
}
fun sourceItemDelegate(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
) = adapterDelegateViewBinding<ExploreItem.Source, ExploreItem, ItemExploreSourceBinding>(
{ layoutInflater, parent -> ItemExploreSourceBinding.inflate(layoutInflater, parent, false) },
on = { item, _, _ -> item is ExploreItem.Source }
) {
var imageRequest: Disposable? = null
bind {
binding.textViewTitle.text = item.source.title
imageRequest = ImageRequest.Builder(context)
.data(item.faviconUrl)
.error(R.drawable.ic_favicon_fallback)
.target(binding.imageViewCover)
.lifecycle(lifecycleOwner)
.enqueueWith(coil)
}
onViewRecycled {
imageRequest?.dispose()
imageRequest = null
}
}

View File

@@ -0,0 +1,28 @@
package org.koitharu.kotatsu.explore.ui.adapter
import androidx.recyclerview.widget.DiffUtil
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
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.Source && newItem is ExploreItem.Source -> {
oldItem.source == newItem.source
}
oldItem is ExploreItem.Header && newItem is ExploreItem.Header -> {
oldItem.titleResId == newItem.titleResId
}
else -> false
}
}
override fun areContentsTheSame(oldItem: ExploreItem, newItem: ExploreItem): Boolean {
return oldItem == newItem
}
}

View File

@@ -0,0 +1,10 @@
package org.koitharu.kotatsu.explore.ui.adapter
import android.view.View
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
interface SourcesHeaderEventListener : ListStateHolderListener {
fun onManageClick(view: View)
}

View File

@@ -0,0 +1,52 @@
package org.koitharu.kotatsu.explore.ui.model
import android.net.Uri
import androidx.annotation.StringRes
import org.koitharu.kotatsu.parsers.model.MangaSource
sealed interface ExploreItem {
object Buttons : ExploreItem
class Header(
@StringRes val titleResId: Int,
) : ExploreItem {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Header
return titleResId == other.titleResId
}
override fun hashCode(): Int = titleResId
}
class Source(
val source: MangaSource,
val summary: String?,
) : ExploreItem {
val faviconUrl: Uri
get() = Uri.fromParts("favicon", source.name, null)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
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
}
}
}

View File

@@ -3,7 +3,9 @@ package org.koitharu.kotatsu.history.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.fragment.app.commit
import org.koitharu.kotatsu.R
@@ -26,7 +28,11 @@ class HistoryActivity : BaseActivity<ActivityContainerBinding>() {
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updatePadding(
binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left
rightMargin = insets.right
}
binding.root.updatePadding(
left = insets.left,
right = insets.right,
)

View File

@@ -99,9 +99,11 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(), LibraryListEvent
override fun onEmptyActionClick() = Unit
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
binding.root.updatePadding(
left = insets.left,
right = insets.right,
)
binding.recyclerView.updatePadding(
bottom = insets.bottom,
)
}

View File

@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ActivityMainBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.explore.ui.ExploreFragment
import org.koitharu.kotatsu.favourites.ui.FavouritesContainerFragment
import org.koitharu.kotatsu.library.ui.LibraryFragment
import org.koitharu.kotatsu.parsers.model.Manga
@@ -248,8 +249,8 @@ class MainActivity :
binding.root.isLiftAppBarOnScroll = true // придумать лучше
}
R.id.nav_explore -> {
setPrimaryFragment(FavouritesContainerFragment.newInstance())
binding.root.isLiftAppBarOnScroll = false // --//--
setPrimaryFragment(ExploreFragment.newInstance())
binding.root.isLiftAppBarOnScroll = true // --//--
}
R.id.nav_feed -> {
setPrimaryFragment(FeedFragment.newInstance())

View File

@@ -24,6 +24,7 @@ import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
import org.koitharu.kotatsu.main.ui.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
class SettingsActivity :
@@ -126,6 +127,7 @@ class SettingsActivity :
ACTION_SOURCE -> SourceSettingsFragment.newInstance(
intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL
)
ACTION_MANAGE_SOURCES -> SourcesSettingsFragment()
else -> SettingsHeadersFragment()
}
supportFragmentManager.commit {
@@ -150,6 +152,7 @@ class SettingsActivity :
private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER"
private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS"
private const val ACTION_SHIKIMORI = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SHIKIMORI_SETTINGS"
private const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST"
private const val EXTRA_SOURCE = "source"
private const val HOST_SHIKIMORI_AUTH = "shikimori-auth"
@@ -172,6 +175,10 @@ class SettingsActivity :
Intent(context, SettingsActivity::class.java)
.setAction(ACTION_TRACKER)
fun newManageSourcesIntent(context: Context) =
Intent(context, SettingsActivity::class.java)
.setAction(ACTION_MANAGE_SOURCES)
fun newSourceSettingsIntent(context: Context, source: MangaSource) =
Intent(context, SettingsActivity::class.java)
.setAction(ACTION_SOURCE)

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:orientation="horizontal"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/favourites"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:text="@string/history"
app:icon="@drawable/ic_history" />
<com.google.android.material.button.MaterialButton
android:id="@+id/local_storage"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:text="@string/local_storage"
app:icon="@drawable/ic_storage" />
<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_marginHorizontal="8dp"
android:layout_weight="1"
android:text="@string/suggestions"
app:icon="@drawable/ic_suggestion" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="@string/bookmarks"
app:icon="@drawable/ic_bookmark" />
</LinearLayout>

View File

@@ -1,7 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingLeft="@dimen/list_spacing"
android:paddingRight="@dimen/list_spacing"
android:paddingTop="@dimen/grid_spacing_outer"
android:paddingBottom="@dimen/grid_spacing_outer"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_explore_source" />

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/favourites"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
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" />
<com.google.android.material.button.MaterialButton
android:id="@+id/local_storage"
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: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" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.button.MaterialButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Kotatsu.ExploreButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:icon="@drawable/ic_history"
tools:text="@string/history" />

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView_title"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
tools:text="@tools:sample/lorem[2]" />
<Button
android:id="@+id/button_more"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manage" />
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:background="?selectableItemBackground"
android:clipChildren="false"
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: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"
tools:src="@tools:sample/backgrounds/scenic" />
<TextView
android:id="@+id/textView_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -2,12 +2,13 @@
<TextView
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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/textView"
android:padding="8dp"
android:textAppearance="@style/TextAppearance.AppCompat"
android:textSize="24sp"
tools:text="History"
android:padding="12dp"
android:textSize="20sp"
app:drawableEndCompat="@drawable/ic_arrow_forward"
app:drawableTint="?attr/colorPrimary" />
app:drawableTint="?attr/colorPrimary"
tools:text="History" />

View File

@@ -323,4 +323,5 @@
<string name="clear_all_history">Clear all history</string>
<string name="last_2_hours">Last 2 hours</string>
<string name="history_cleared">History cleared</string>
<string name="manage">Manage</string>
</resources>

View File

@@ -127,13 +127,14 @@
<item name="android:insetBottom">2dp</item>
</style>
<style name="Widget.Kotatsu.ExploreButton" parent="Widget.Material3.Button.ElevatedButton.Icon">
<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:textColor">?attr/colorOnSurface</item>
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Material3.FloatingActionButton</item>
<item name="iconPadding">16dp</item>
<item name="iconGravity">start</item>
<item name="iconTint">?attr/colorPrimary</item>
<item name="android:gravity">start|center_vertical</item>
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
</style>