Compare commits

...

12 Commits

Author SHA1 Message Date
42d933ba83 Bump version 2023-09-01 13:17:07 +03:00
4df644e21f Fix branch prediction 2023-09-01 12:02:31 +03:00
ViAnh
e4ba738c00 Use WeakHashMap to store views 2023-08-31 19:35:53 +03:00
ViAnh
b7f09243aa Avoid unnecessary child layout in webtoon recycler 2023-08-31 19:35:53 +03:00
ViAnh
50d4c41855 Fix webtoon under scale 2023-08-31 19:35:53 +03:00
67adc8b681 Fix widgets in dark theme 2023-08-31 19:28:25 +03:00
34fb4af9fe Fix color scheme preference 2023-08-31 19:11:29 +03:00
05241f73d9 Improve categories managing 2023-08-31 19:11:29 +03:00
d666e4b967 Fix small webtoon pages 2023-08-31 19:11:29 +03:00
b4bf607d3a Merge pull request #470 from Isira-Seneviratne/Data_classes 2023-08-31 09:17:03 +03:00
Isira Seneviratne
a417d5aaa9 Apply requested changes 2023-08-30 19:27:34 +05:30
Isira Seneviratne
ad3b5dde91 Convert more classes to data classes 2023-08-27 07:10:32 +05:30
60 changed files with 341 additions and 932 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 34
versionCode = 574
versionName = '6.0.1'
versionCode = 575
versionName = '6.0.2'
generatedDensities = []
testInstrumentationRunner "org.koitharu.kotatsu.HiltTestRunner"
ksp {

View File

@@ -20,25 +20,8 @@ interface ContentCache {
fun putRelatedManga(source: MangaSource, url: String, related: SafeDeferred<List<Manga>>)
class Key(
data class Key(
val source: MangaSource,
val url: String,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Key
if (source != other.source) return false
return url == other.url
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + url.hashCode()
return result
}
}
)
}

View File

@@ -4,7 +4,7 @@ import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
class MangaWithTags(
data class MangaWithTags(
@Embedded val manga: MangaEntity,
@Relation(
parentColumn = "manga_id",
@@ -12,21 +12,4 @@ class MangaWithTags(
associateBy = Junction(MangaTagsEntity::class)
)
val tags: List<TagEntity>,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaWithTags
if (manga != other.manga) return false
return tags == other.tags
}
override fun hashCode(): Int {
var result = manga.hashCode()
result = 31 * result + tags.hashCode()
return result
}
}
)

View File

@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.core.github
import java.util.*
class VersionId(
data class VersionId(
val major: Int,
val minor: Int,
val build: Int,
@@ -30,28 +30,6 @@ class VersionId(
return variantNumber.compareTo(other.variantNumber)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as VersionId
if (major != other.major) return false
if (minor != other.minor) return false
if (build != other.build) return false
if (variantType != other.variantType) return false
return variantNumber == other.variantNumber
}
override fun hashCode(): Int {
var result = major
result = 31 * result + minor
result = 31 * result + build
result = 31 * result + variantType.hashCode()
result = 31 * result + variantNumber
return result
}
private fun variantWeight(variantType: String) = when (variantType.lowercase(Locale.ROOT)) {
"a", "alpha" -> 1
"b", "beta" -> 2

View File

@@ -48,10 +48,10 @@ fun Manga.getPreferredBranch(history: MangaHistory?): String? {
if (groups.size == 1) {
return groups.keys.first()
}
val candidates = HashMap<String?, List<MangaChapter>>(groups.size)
for (locale in LocaleListCompat.getAdjustedDefault()) {
val displayLanguage = locale.getDisplayLanguage(locale)
val displayName = locale.getDisplayName(locale)
val candidates = HashMap<String?, List<MangaChapter>>(3)
for (branch in groups.keys) {
if (branch != null && (
branch.contains(displayLanguage, ignoreCase = true) ||
@@ -61,8 +61,11 @@ fun Manga.getPreferredBranch(history: MangaHistory?): String? {
candidates[branch] = groups[branch] ?: continue
}
}
if (candidates.isNotEmpty()) {
return candidates.maxBy { it.value.size }.key
}
}
return candidates.ifEmpty { groups }.maxByOrNull { it.value.size }?.key
return groups.maxByOrNull { it.value.size }?.key
}
val Manga.isLocal: Boolean

View File

@@ -8,7 +8,7 @@ import java.io.ObjectInputStream
import java.io.ObjectOutputStream
class CookieWrapper(
data class CookieWrapper(
val cookie: Cookie,
) {
@@ -66,17 +66,4 @@ class CookieWrapper(
fun key(): String {
return (if (cookie.secure) "https" else "http") + "://" + cookie.domain + cookie.path + "|" + cookie.name
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CookieWrapper
return cookie == other.cookie
}
override fun hashCode(): Int {
return cookie.hashCode()
}
}

View File

@@ -106,12 +106,7 @@ class TrimTransformation(
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TrimTransformation
return tolerance == other.tolerance
return this === other || (other is TrimTransformation && other.tolerance == tolerance)
}
override fun hashCode(): Int {

View File

@@ -20,38 +20,19 @@ sealed class DateTimeAgo {
override fun equals(other: Any?): Boolean = other === JustNow
}
class MinutesAgo(val minutes: Int) : DateTimeAgo() {
data class MinutesAgo(val minutes: Int) : DateTimeAgo() {
override fun format(resources: Resources): String {
return resources.getQuantityString(R.plurals.minutes_ago, minutes, minutes)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MinutesAgo
return minutes == other.minutes
}
override fun hashCode(): Int = minutes
override fun toString() = "minutes_ago_$minutes"
}
class HoursAgo(val hours: Int) : DateTimeAgo() {
data class HoursAgo(val hours: Int) : DateTimeAgo() {
override fun format(resources: Resources): String {
return resources.getQuantityString(R.plurals.hours_ago, hours, hours)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HoursAgo
return hours == other.hours
}
override fun hashCode(): Int = hours
override fun toString() = "hours_ago_$hours"
}
@@ -75,26 +56,15 @@ sealed class DateTimeAgo {
override fun equals(other: Any?): Boolean = other === Yesterday
}
class DaysAgo(val days: Int) : DateTimeAgo() {
data class DaysAgo(val days: Int) : DateTimeAgo() {
override fun format(resources: Resources): String {
return resources.getQuantityString(R.plurals.days_ago, days, days)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DaysAgo
return days == other.days
}
override fun hashCode(): Int = days
override fun toString() = "days_ago_$days"
}
class MonthsAgo(val months: Int) : DateTimeAgo() {
data class MonthsAgo(val months: Int) : DateTimeAgo() {
override fun format(resources: Resources): String {
return if (months == 0) {
resources.getString(R.string.this_month)
@@ -102,19 +72,6 @@ sealed class DateTimeAgo {
resources.getQuantityString(R.plurals.months_ago, months, months)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MonthsAgo
return months == other.months
}
override fun hashCode(): Int {
return months
}
}
class Absolute(private val date: Date) : DateTimeAgo() {

View File

@@ -139,39 +139,14 @@ class ChipsView @JvmOverloads constructor(
}
}
class ChipModel(
data class ChipModel(
@ColorRes val tint: Int,
val title: CharSequence,
@DrawableRes val icon: Int,
val isCheckable: Boolean,
val isChecked: Boolean,
val data: Any? = null,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ChipModel
if (tint != other.tint) return false
if (title != other.title) return false
if (icon != other.icon) return false
if (isCheckable != other.isCheckable) return false
if (isChecked != other.isChecked) return false
return data == other.data
}
override fun hashCode(): Int {
var result = tint.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + icon.hashCode()
result = 31 * result + isCheckable.hashCode()
result = 31 * result + isChecked.hashCode()
result = 31 * result + (data?.hashCode() ?: 0)
return result
}
}
)
fun interface OnChipClickListener {

View File

@@ -118,27 +118,10 @@ class SegmentedBarView @JvmOverloads constructor(
segmentsSizes.add(w)
}
class Segment(
data class Segment(
@FloatRange(from = 0.0, to = 1.0) val percent: Float,
@ColorInt val color: Int,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Segment
if (percent != other.percent) return false
return color == other.color
}
override fun hashCode(): Int {
var result = percent.hashCode()
result = 31 * result + color
return result
}
}
)
private class OutlineProvider : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {

View File

@@ -4,7 +4,7 @@ import android.text.format.DateUtils
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaChapter
class ChapterListItem(
data class ChapterListItem(
val chapter: MangaChapter,
val flags: Int,
private val uploadDateMs: Long,
@@ -66,24 +66,6 @@ class ChapterListItem(
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ChapterListItem
if (chapter != other.chapter) return false
if (flags != other.flags) return false
return uploadDateMs == other.uploadDateMs
}
override fun hashCode(): Int {
var result = chapter.hashCode()
result = 31 * result + flags
result = 31 * result + uploadDateMs.hashCode()
return result
}
companion object {
const val FLAG_UNREAD = 2

View File

@@ -3,35 +3,14 @@ package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.parsers.model.Manga
class HistoryInfo(
data class HistoryInfo(
val totalChapters: Int,
val currentChapter: Int,
val history: MangaHistory?,
val isIncognitoMode: Boolean,
) {
val isValid: Boolean
get() = totalChapters >= 0
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HistoryInfo
if (totalChapters != other.totalChapters) return false
if (currentChapter != other.currentChapter) return false
if (history != other.history) return false
return isIncognitoMode == other.isIncognitoMode
}
override fun hashCode(): Int {
var result = totalChapters
result = 31 * result + currentChapter
result = 31 * result + (history?.hashCode() ?: 0)
result = 31 * result + isIncognitoMode.hashCode()
return result
}
}
fun HistoryInfo(

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel
class MangaBranch(
data class MangaBranch(
val name: String?,
val count: Int,
val isSelected: Boolean,
@@ -21,24 +21,6 @@ class MangaBranch(
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaBranch
if (name != other.name) return false
if (count != other.count) return false
return isSelected == other.isSelected
}
override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + count
result = 31 * result + isSelected.hashCode()
return result
}
override fun toString(): String {
return "$name: $count"
}

View File

@@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
import java.util.Date
import java.util.UUID
class DownloadItemModel(
data class DownloadItemModel(
val id: UUID,
val workState: WorkInfo.State,
val isIndeterminate: Boolean,
@@ -64,38 +64,4 @@ class DownloadItemModel(
else -> super.getChangePayload(previousState)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DownloadItemModel
if (id != other.id) return false
if (workState != other.workState) return false
if (isIndeterminate != other.isIndeterminate) return false
if (isPaused != other.isPaused) return false
if (manga != other.manga) return false
if (error != other.error) return false
if (max != other.max) return false
if (totalChapters != other.totalChapters) return false
if (progress != other.progress) return false
if (eta != other.eta) return false
return timestamp == other.timestamp
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + workState.hashCode()
result = 31 * result + isIndeterminate.hashCode()
result = 31 * result + isPaused.hashCode()
result = 31 * result + manga.hashCode()
result = 31 * result + (error?.hashCode() ?: 0)
result = 31 * result + max
result = 31 * result + totalChapters
result = 31 * result + progress
result = 31 * result + eta.hashCode()
result = 31 * result + timestamp.hashCode()
return result
}
}

View File

@@ -2,24 +2,11 @@ package org.koitharu.kotatsu.explore.ui.model
import org.koitharu.kotatsu.list.ui.model.ListModel
class ExploreButtons(
data class ExploreButtons(
val isRandomLoading: Boolean,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is ExploreButtons
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ExploreButtons
return isRandomLoading == other.isRandomLoading
}
override fun hashCode(): Int {
return isRandomLoading.hashCode()
}
}

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.explore.ui.model
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaSource
class MangaSourceItem(
data class MangaSourceItem(
val source: MangaSource,
val isGrid: Boolean,
) : ListModel {
@@ -11,20 +11,4 @@ class MangaSourceItem(
override fun areItemsTheSame(other: ListModel): Boolean {
return other is MangaSourceItem && other.source == source
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaSourceItem
if (source != other.source) return false
return isGrid == other.isGrid
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + isGrid.hashCode()
return result
}
}

View File

@@ -6,7 +6,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
data class RecommendationsItem(
val manga: Manga
) : ListModel {
val summary: String = manga.tags.joinToString { it.title }
override fun areItemsTheSame(other: ListModel): Boolean {

View File

@@ -3,31 +3,10 @@ package org.koitharu.kotatsu.favourites.domain.model
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.find
class Cover(
data class Cover(
val url: String,
val source: String,
) {
val mangaSource: MangaSource?
get() = if (source.isEmpty()) null else MangaSource.entries.find(source)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Cover
if (url != other.url) return false
return source == other.source
}
override fun hashCode(): Int {
var result = url.hashCode()
result = 31 * result + source.hashCode()
return result
}
override fun toString(): String {
return "Cover(url='$url', source=$source)"
}
}

View File

@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding
import org.koitharu.kotatsu.favourites.ui.FavouritesActivity
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoriesAdapter
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
@@ -77,6 +78,14 @@ class FavouriteCategoriesActivity :
}
override fun onItemClick(item: FavouriteCategory, view: View) {
if (selectionController.onItemClick(item.id)) {
return
}
val intent = FavouritesActivity.newIntent(view.context, item)
startActivity(intent)
}
override fun onEditClick(item: FavouriteCategory, view: View) {
if (selectionController.onItemClick(item.id)) {
return
}
@@ -112,8 +121,8 @@ class FavouriteCategoriesActivity :
)
}
private fun onCategoriesChanged(categories: List<ListModel>) {
adapter.items = categories
private suspend fun onCategoriesChanged(categories: List<ListModel>) {
adapter.emit(categories)
invalidateOptionsMenu()
}
@@ -128,7 +137,14 @@ class FavouriteCategoriesActivity :
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder,
): Boolean = viewHolder.itemViewType == target.itemViewType
): Boolean {
if (viewHolder.itemViewType != target.itemViewType) {
return false
}
val fromPos = viewHolder.bindingAdapterPosition
val toPos = target.bindingAdapterPosition
return fromPos != toPos && fromPos != RecyclerView.NO_POSITION && toPos != RecyclerView.NO_POSITION
}
override fun canDropOver(
recyclerView: RecyclerView,
@@ -153,7 +169,8 @@ class FavouriteCategoriesActivity :
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
viewBinding.recyclerView.isNestedScrollingEnabled = actionState == ItemTouchHelper.ACTION_STATE_IDLE
viewBinding.recyclerView.isNestedScrollingEnabled =
actionState == ItemTouchHelper.ACTION_STATE_IDLE
}
}

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.favourites.ui.categories
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
@@ -7,4 +8,6 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
interface FavouriteCategoriesListListener : OnListItemClickListener<FavouriteCategory> {
fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean
fun onEditClick(item: FavouriteCategory, view: View)
}

View File

@@ -17,7 +17,7 @@ import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingState
import java.util.Collections
import org.koitharu.kotatsu.parsers.util.move
import javax.inject.Inject
@HiltViewModel
@@ -65,12 +65,11 @@ class FavouritesCategoriesViewModel @Inject constructor(
val prevJob = reorderJob
reorderJob = launchJob(Dispatchers.Default) {
prevJob?.join()
val items = categories.requireValue()
val ids = items.mapNotNullTo(ArrayList(items.size)) {
val snapshot = categories.requireValue().toMutableList()
snapshot.move(oldPos, newPos)
val ids = snapshot.mapNotNullTo(ArrayList(snapshot.size)) {
(it as? CategoryListModel)?.category?.id
}
Collections.swap(ids, oldPos, newPos)
ids.remove(0L)
repository.reorderCategories(ids)
}
}

View File

@@ -18,8 +18,8 @@ class CategoriesAdapter(
) : BaseListAdapter<ListModel>() {
init {
addDelegate(ListItemType.CATEGORY_LARGE ,categoryAD(coil, lifecycleOwner, onItemClickListener))
addDelegate(ListItemType.STATE_EMPTY ,emptyStateListAD(coil, lifecycleOwner, listListener))
addDelegate(ListItemType.STATE_LOADING ,loadingStateAD())
addDelegate(ListItemType.CATEGORY_LARGE, categoryAD(coil, lifecycleOwner, onItemClickListener))
addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listListener))
addDelegate(ListItemType.STATE_LOADING, loadingStateAD())
}
}

View File

@@ -34,8 +34,13 @@ fun categoryAD(
{ inflater, parent -> ItemCategoryBinding.inflate(inflater, parent, false) },
) {
val eventListener = object : OnClickListener, OnLongClickListener, OnTouchListener {
override fun onClick(v: View) = clickListener.onItemClick(item.category, itemView)
override fun onLongClick(v: View) = clickListener.onItemLongClick(item.category, itemView)
override fun onClick(v: View) = if (v.id == R.id.imageView_edit) {
clickListener.onEditClick(item.category, v)
} else {
clickListener.onItemClick(item.category, v)
}
override fun onLongClick(v: View) = clickListener.onItemLongClick(item.category, v)
override fun onTouch(v: View?, event: MotionEvent): Boolean = event.actionMasked == MotionEvent.ACTION_DOWN &&
clickListener.onDragHandleTouch(this@adapterDelegateViewBinding)
}
@@ -57,6 +62,7 @@ fun categoryAD(
val crossFadeDuration = context.getAnimationDuration(R.integer.config_defaultAnimTime).toInt()
itemView.setOnClickListener(eventListener)
itemView.setOnLongClickListener(eventListener)
binding.imageViewEdit.setOnClickListener(eventListener)
binding.imageViewHandle.setOnTouchListener(eventListener)
bind { payloads ->

View File

@@ -46,4 +46,8 @@ class CategoryListModel(
result = 31 * result + category.isVisibleInLibrary.hashCode()
return result
}
override fun toString(): String {
return "CategoryListModel(categoryId=${category.id})"
}
}

View File

@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.favourites.ui.container
import org.koitharu.kotatsu.list.ui.model.ListModel
class FavouriteTabModel(
data class FavouriteTabModel(
val id: Long,
val title: String,
) : ListModel {
@@ -10,20 +10,4 @@ class FavouriteTabModel(
override fun areItemsTheSame(other: ListModel): Boolean {
return other is FavouriteTabModel && other.id == id
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FavouriteTabModel
if (id != other.id) return false
return title == other.title
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + title.hashCode()
return result
}
}

View File

@@ -263,30 +263,11 @@ class FilterCoordinator @Inject constructor(
return result
}
private class TagsWrapper(
private data class TagsWrapper(
val tags: Set<MangaTag>,
val isLoading: Boolean,
val isError: Boolean,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TagsWrapper
if (tags != other.tags) return false
if (isLoading != other.isLoading) return false
return isError == other.isError
}
override fun hashCode(): Int {
var result = tags.hashCode()
result = 31 * result + isLoading.hashCode()
result = 31 * result + isError.hashCode()
return result
}
}
)
private class TagTitleComparator(lc: String?) : Comparator<MangaTag> {

View File

@@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.model.SortOrder
sealed interface FilterItem : ListModel {
class Sort(
data class Sort(
val order: SortOrder,
val isSelected: Boolean,
) : FilterItem {
@@ -24,25 +24,9 @@ sealed interface FilterItem : ListModel {
super.getChangePayload(previousState)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Sort
if (order != other.order) return false
return isSelected == other.isSelected
}
override fun hashCode(): Int {
var result = order.hashCode()
result = 31 * result + isSelected.hashCode()
return result
}
}
class Tag(
data class Tag(
val tag: MangaTag,
val isChecked: Boolean,
) : FilterItem {
@@ -58,43 +42,14 @@ sealed interface FilterItem : ListModel {
super.getChangePayload(previousState)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Tag
if (tag != other.tag) return false
return isChecked == other.isChecked
}
override fun hashCode(): Int {
var result = tag.hashCode()
result = 31 * result + isChecked.hashCode()
return result
}
}
class Error(
data class Error(
@StringRes val textResId: Int,
) : FilterItem {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is Error && textResId == other.textResId
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Error
return textResId == other.textResId
}
override fun hashCode(): Int {
return textResId
}
}
}

View File

@@ -3,24 +3,7 @@ package org.koitharu.kotatsu.filter.ui.model
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder
class FilterState(
data class FilterState(
val sortOrder: SortOrder?,
val tags: Set<MangaTag>,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FilterState
if (sortOrder != other.sortOrder) return false
return tags == other.tags
}
override fun hashCode(): Int {
var result = sortOrder?.hashCode() ?: 0
result = 31 * result + tags.hashCode()
return result
}
}
)

View File

@@ -77,7 +77,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) {
largeCoverUrl = null,
description = null,
)
LocalManga(root, manga)
LocalManga(manga, root)
}
override suspend fun getMangaInfo(): Manga? = runInterruptible(Dispatchers.IO) {

View File

@@ -94,7 +94,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) {
)
}
}
return LocalManga(root, manga)
return LocalManga(manga, root)
}
override suspend fun getMangaInfo(): Manga? = runInterruptible(Dispatchers.IO) {

View File

@@ -6,13 +6,11 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
import java.io.File
class LocalManga(
val file: File,
data class LocalManga(
val manga: Manga,
val file: File = manga.url.toUri().toFile(),
) {
constructor(manga: Manga) : this(manga.url.toUri().toFile(), manga)
var createdAt: Long = -1L
private set
get() {
@@ -31,22 +29,6 @@ class LocalManga(
return manga.tags.containsAll(tags)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LocalManga
if (manga != other.manga) return false
return file == other.file
}
override fun hashCode(): Int {
var result = manga.hashCode()
result = 31 * result + file.hashCode()
return result
}
override fun toString(): String {
return "LocalManga(${file.path}: ${manga.title})"
}

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.domain
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
class ReaderColorFilter(
data class ReaderColorFilter(
val brightness: Float,
val contrast: Float,
val isInverted: Boolean,
@@ -51,22 +51,4 @@ class ReaderColorFilter(
)
set(matrix)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ReaderColorFilter
if (brightness != other.brightness) return false
if (contrast != other.contrast) return false
return isInverted == other.isInverted
}
override fun hashCode(): Int {
var result = brightness.hashCode()
result = 31 * result + contrast.hashCode()
result = 31 * result + isInverted.hashCode()
return result
}
}

View File

@@ -12,7 +12,7 @@ class WebtoonFrameLayout @JvmOverloads constructor(
@AttrRes defStyleAttr: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr) {
private val target by lazy(LazyThreadSafetyMode.NONE) {
val target by lazy(LazyThreadSafetyMode.NONE) {
findViewById<WebtoonImageView>(R.id.ssiv)
}
@@ -24,4 +24,4 @@ class WebtoonFrameLayout @JvmOverloads constructor(
target.scrollBy(dy)
return target.getScroll() - oldScroll
}
}
}

View File

@@ -84,10 +84,22 @@ class WebtoonImageView @JvmOverloads constructor(
}
}
width = width.coerceAtLeast(suggestedMinimumWidth)
height = height.coerceIn(suggestedMinimumHeight, parentHeight())
height = height.coerceAtLeast(suggestedMinimumHeight).coerceAtMost(parentHeight())
setMeasuredDimension(width, height)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (oldh == h || oldw == 0 || oldh == 0 || scrollRange == SCROLL_UNKNOWN) return
computeScrollRange()
val container = parents.firstNotNullOfOrNull { it as? WebtoonFrameLayout } ?: return
val parentHeight = parentHeight()
if (scrollPos != 0 && container.bottom < parentHeight) {
scrollTo(scrollRange)
}
}
private fun scrollToInternal(pos: Int) {
scrollPos = pos
ct.set(sWidth / 2f, (height / 2f + pos.toFloat()) / minScale)

View File

@@ -2,16 +2,30 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.core.view.ViewCompat.TYPE_TOUCH
import androidx.core.view.forEach
import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.core.util.ext.findCenterViewPosition
import java.util.LinkedList
import java.util.WeakHashMap
class WebtoonRecyclerView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {
private var onPageScrollListeners: MutableList<OnPageScrollListener>? = null
private val detachedViews = WeakHashMap<View, Unit>()
override fun onChildDetachedFromWindow(child: View) {
super.onChildDetachedFromWindow(child)
detachedViews[child] = Unit
}
override fun onChildAttachedToWindow(child: View) {
super.onChildAttachedToWindow(child)
detachedViews.remove(child)
}
override fun startNestedScroll(axes: Int) = startNestedScroll(axes, TYPE_TOUCH)
@@ -98,6 +112,15 @@ class WebtoonRecyclerView @JvmOverloads constructor(
listeners.forEach { it.dispatchScroll(this, dy, centerPosition) }
}
fun relayoutChildren() {
forEach { child ->
(child as WebtoonFrameLayout).target.requestLayout()
}
detachedViews.keys.forEach { child ->
(child as WebtoonFrameLayout).target.requestLayout()
}
}
abstract class OnPageScrollListener {
private var lastPosition = NO_POSITION

View File

@@ -1,6 +1,6 @@
package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
@@ -15,7 +15,7 @@ import android.widget.OverScroller
import androidx.core.view.GestureDetectorCompat
private const val MAX_SCALE = 2.5f
private const val MIN_SCALE = 1f // under-scaling disabled due to buggy nested scroll
private const val MIN_SCALE = 0.5f
class WebtoonScalingFrame @JvmOverloads constructor(
context: Context,
@@ -23,7 +23,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
defStyles: Int = 0,
) : FrameLayout(context, attrs, defStyles), ScaleGestureDetector.OnScaleGestureListener {
private val targetChild by lazy(LazyThreadSafetyMode.NONE) { getChildAt(0) }
private val targetChild by lazy(LazyThreadSafetyMode.NONE) { getChildAt(0) as WebtoonRecyclerView }
private val scaleDetector = ScaleGestureDetector(context, this)
private val gestureDetector = GestureDetectorCompat(context, GestureListener())
@@ -40,7 +40,6 @@ class WebtoonScalingFrame @JvmOverloads constructor(
private var halfHeight = 0f
private val translateBounds = RectF()
private val targetHitRect = Rect()
private var pendingScroll = 0
var isZoomEnable = true
set(value) {
@@ -97,12 +96,11 @@ class WebtoonScalingFrame @JvmOverloads constructor(
if (newHeight != targetChild.height) {
targetChild.layoutParams.height = newHeight
targetChild.requestLayout()
targetChild.relayoutChildren()
}
if (scale < 1) {
targetChild.getHitRect(targetHitRect)
targetChild.scrollBy(0, pendingScroll)
pendingScroll = 0
}
}
@@ -124,7 +122,6 @@ class WebtoonScalingFrame @JvmOverloads constructor(
else -> 0f
}
pendingScroll = dy.toInt()
transformMatrix.postTranslate(dx, dy)
syncMatrixValues()
}
@@ -159,9 +156,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true
override fun onScaleEnd(p0: ScaleGestureDetector) {
pendingScroll = 0
}
override fun onScaleEnd(p0: ScaleGestureDetector) = Unit
private inner class GestureListener : GestureDetector.SimpleOnGestureListener(), Runnable {
@@ -175,7 +170,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
override fun onDoubleTap(e: MotionEvent): Boolean {
val newScale = if (scale != 1f) 1f else MAX_SCALE * 0.8f
ObjectAnimator.ofFloat(scale, newScale).run {
ValueAnimator.ofFloat(scale, newScale).run {
interpolator = AccelerateDecelerateInterpolator()
duration = 300
addUpdateListener {

View File

@@ -4,7 +4,7 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
class PageThumbnail(
data class PageThumbnail(
val isCurrent: Boolean,
val repository: MangaRepository,
val page: ReaderPage,
@@ -16,23 +16,4 @@ class PageThumbnail(
override fun areItemsTheSame(other: ListModel): Boolean {
return other is PageThumbnail && page == other.page
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as PageThumbnail
if (isCurrent != other.isCurrent) return false
if (repository != other.repository) return false
return page == other.page
}
override fun hashCode(): Int {
var result = isCurrent.hashCode()
result = 31 * result + repository.hashCode()
result = 31 * result + page.hashCode()
return result
}
}

View File

@@ -2,40 +2,17 @@ package org.koitharu.kotatsu.scrobbling.common.domain.model
import org.koitharu.kotatsu.list.ui.model.ListModel
class ScrobblerManga(
data class ScrobblerManga(
val id: Long,
val name: String,
val altName: String?,
val cover: String,
val url: String,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is ScrobblerManga && other.id == id
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ScrobblerManga
if (id != other.id) return false
if (name != other.name) return false
if (altName != other.altName) return false
if (cover != other.cover) return false
return url == other.url
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + altName.hashCode()
result = 31 * result + cover.hashCode()
result = 31 * result + url.hashCode()
return result
}
override fun toString(): String {
return "ScrobblerManga #$id \"$name\" $url"
}

View File

@@ -1,29 +1,8 @@
package org.koitharu.kotatsu.scrobbling.common.domain.model
class ScrobblerUser(
data class ScrobblerUser(
val id: Long,
val nickname: String,
val avatar: String,
val service: ScrobblerService,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ScrobblerUser
if (id != other.id) return false
if (nickname != other.nickname) return false
if (avatar != other.avatar) return false
return service == other.service
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + nickname.hashCode()
result = 31 * result + avatar.hashCode()
result = 31 * result + service.hashCode()
return result
}
}
)

View File

@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.scrobbling.common.domain.model
import org.koitharu.kotatsu.list.ui.model.ListModel
class ScrobblingInfo(
data class ScrobblingInfo(
val scrobbler: ScrobblerService,
val mangaId: Long,
val targetId: Long,
@@ -19,38 +19,4 @@ class ScrobblingInfo(
override fun areItemsTheSame(other: ListModel): Boolean {
return other is ScrobblingInfo && other.scrobbler == scrobbler
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ScrobblingInfo
if (scrobbler != other.scrobbler) return false
if (mangaId != other.mangaId) return false
if (targetId != other.targetId) return false
if (status != other.status) return false
if (chapter != other.chapter) return false
if (comment != other.comment) return false
if (rating != other.rating) return false
if (title != other.title) return false
if (coverUrl != other.coverUrl) return false
if (description != other.description) return false
return externalUrl == other.externalUrl
}
override fun hashCode(): Int {
var result = scrobbler.hashCode()
result = 31 * result + mangaId.hashCode()
result = 31 * result + targetId.hashCode()
result = 31 * result + (status?.hashCode() ?: 0)
result = 31 * result + chapter
result = 31 * result + (comment?.hashCode() ?: 0)
result = 31 * result + rating.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + coverUrl.hashCode()
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + externalUrl.hashCode()
return result
}
}

View File

@@ -4,7 +4,7 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.koitharu.kotatsu.list.ui.model.ListModel
class ScrobblerHint(
data class ScrobblerHint(
@DrawableRes val icon: Int,
@StringRes val textPrimary: Int,
@StringRes val textSecondary: Int,
@@ -15,26 +15,4 @@ class ScrobblerHint(
override fun areItemsTheSame(other: ListModel): Boolean {
return other is ScrobblerHint && other.textPrimary == textPrimary
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ScrobblerHint
if (icon != other.icon) return false
if (textPrimary != other.textPrimary) return false
if (textSecondary != other.textSecondary) return false
if (error != other.error) return false
return actionStringRes == other.actionStringRes
}
override fun hashCode(): Int {
var result = icon
result = 31 * result + textPrimary
result = 31 * result + textSecondary
result = 31 * result + (error?.hashCode() ?: 0)
result = 31 * result + actionStringRes
return result
}
}

View File

@@ -5,7 +5,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaItemModel
import org.koitharu.kotatsu.parsers.model.MangaSource
class MultiSearchListModel(
data class MultiSearchListModel(
val source: MangaSource,
val hasMore: Boolean,
val list: List<MangaItemModel>,
@@ -23,24 +23,4 @@ class MultiSearchListModel(
super.getChangePayload(previousState)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MultiSearchListModel
if (source != other.source) return false
if (hasMore != other.hasMore) return false
if (list != other.list) return false
return error == other.error
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + hasMore.hashCode()
result = 31 * result + list.hashCode()
result = 31 * result + (error?.hashCode() ?: 0)
return result
}
}

View File

@@ -5,14 +5,13 @@ import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel
import java.io.File
class DirectoryModel(
data class DirectoryModel(
val title: String?,
@StringRes val titleRes: Int,
val file: File?,
val isChecked: Boolean,
val isAvailable: Boolean,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is DirectoryModel && other.file == file && other.title == title && other.titleRes == titleRes
}
@@ -24,26 +23,4 @@ class DirectoryModel(
super.getChangePayload(previousState)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DirectoryModel
if (title != other.title) return false
if (titleRes != other.titleRes) return false
if (file != other.file) return false
if (isChecked != other.isChecked) return false
return isAvailable == other.isAvailable
}
override fun hashCode(): Int {
var result = title?.hashCode() ?: 0
result = 31 * result + titleRes
result = 31 * result + (file?.hashCode() ?: 0)
result = 31 * result + isChecked.hashCode()
result = 31 * result + isAvailable.hashCode()
return result
}
}

View File

@@ -1,51 +1,13 @@
package org.koitharu.kotatsu.settings.userdata
class StorageUsage(
data class StorageUsage(
val savedManga: Item,
val pagesCache: Item,
val otherCache: Item,
val available: Item,
) {
class Item(
data class Item(
val bytes: Long,
val percent: Float,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Item
if (bytes != other.bytes) return false
return percent == other.percent
}
override fun hashCode(): Int {
var result = bytes.hashCode()
result = 31 * result + percent.hashCode()
return result
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as StorageUsage
if (savedManga != other.savedManga) return false
if (pagesCache != other.pagesCache) return false
if (otherCache != other.otherCache) return false
return available == other.available
}
override fun hashCode(): Int {
var result = savedManga.hashCode()
result = 31 * result + pagesCache.hashCode()
result = 31 * result + otherCache.hashCode()
result = 31 * result + available.hashCode()
return result
}
)
}

View File

@@ -12,6 +12,7 @@ import android.view.ViewTreeObserver
import android.widget.HorizontalScrollView
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible
import androidx.core.view.updatePaddingRelative
import androidx.customview.view.AbsSavedState
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
@@ -52,7 +53,11 @@ class ThemeChooserPreference @JvmOverloads constructor(
binding.linear.removeAllViews()
for (theme in entries) {
val context = ContextThemeWrapper(context, theme.styleResId)
val item = ItemColorSchemeBinding.inflate(LayoutInflater.from(context), binding.linear, false)
val item =
ItemColorSchemeBinding.inflate(LayoutInflater.from(context), binding.linear, false)
if (binding.linear.childCount == 0) {
item.root.updatePaddingRelative(start = 0)
}
val isSelected = theme == currentValue
item.card.isChecked = isSelected
item.card.strokeWidth = if (isSelected) context.resources.getDimensionPixelSize(
@@ -76,7 +81,8 @@ class ThemeChooserPreference @JvmOverloads constructor(
}
binding.scrollView.viewTreeObserver.run {
scrollPersistListener?.let { removeOnScrollChangedListener(it) }
scrollPersistListener = ScrollPersistListener(WeakReference(binding.scrollView), lastScrollPosition)
scrollPersistListener =
ScrollPersistListener(WeakReference(binding.scrollView), lastScrollPosition)
addOnScrollChangedListener(scrollPersistListener)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -133,7 +139,7 @@ class ThemeChooserPreference @JvmOverloads constructor(
constructor(
superState: Parcelable,
scrollPosition: Int
scrollPosition: Int,
) : super(superState) {
this.scrollPosition = scrollPosition
}
@@ -151,7 +157,8 @@ class ThemeChooserPreference @JvmOverloads constructor(
@Suppress("unused")
@JvmField
val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
override fun createFromParcel(`in`: Parcel) = SavedState(`in`, SavedState::class.java.classLoader)
override fun createFromParcel(`in`: Parcel) =
SavedState(`in`, SavedState::class.java.classLoader)
override fun newArray(size: Int): Array<SavedState?> = arrayOfNulls(size)
}

View File

@@ -1,29 +1,8 @@
package org.koitharu.kotatsu.sync.domain
class SyncAuthResult(
data class SyncAuthResult(
val host: String,
val email: String,
val password: String,
val token: String,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as SyncAuthResult
if (host != other.host) return false
if (email != other.email) return false
if (password != other.password) return false
return token == other.token
}
override fun hashCode(): Int {
var result = host.hashCode()
result = 31 * result + email.hashCode()
result = 31 * result + password.hashCode()
result = 31 * result + token.hashCode()
return result
}
}
)

View File

@@ -3,31 +3,12 @@ package org.koitharu.kotatsu.tracker.domain.model
import java.util.*
import org.koitharu.kotatsu.parsers.model.Manga
class MangaTracking(
data class MangaTracking(
val manga: Manga,
val lastChapterId: Long,
val lastCheck: Date?,
) {
fun isEmpty(): Boolean {
return lastChapterId == 0L
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MangaTracking
if (manga != other.manga) return false
if (lastChapterId != other.lastChapterId) return false
return lastCheck == other.lastCheck
}
override fun hashCode(): Int {
var result = manga.hashCode()
result = 31 * result + lastChapterId.hashCode()
result = 31 * result + (lastCheck?.hashCode() ?: 0)
return result
}
}

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.tracker.ui.feed.model
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.Manga
class FeedItem(
data class FeedItem(
val id: Long,
val imageUrl: String,
val title: String,
@@ -11,32 +11,7 @@ class FeedItem(
val count: Int,
val isNew: Boolean,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is FeedItem && other.id == id
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FeedItem
if (id != other.id) return false
if (imageUrl != other.imageUrl) return false
if (title != other.title) return false
if (manga != other.manga) return false
if (count != other.count) return false
return isNew == other.isNew
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + imageUrl.hashCode()
result = 31 * result + title.hashCode()
result = 31 * result + manga.hashCode()
result = 31 * result + count
result = 31 * result + isNew.hashCode()
return result
}
}

View File

@@ -2,28 +2,7 @@ package org.koitharu.kotatsu.tracker.work
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
class TrackingItem(
data class TrackingItem(
val tracking: MangaTracking,
val channelId: String?,
) {
operator fun component1() = tracking
operator fun component2() = channelId
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TrackingItem
if (tracking != other.tracking) return false
return channelId == other.channelId
}
override fun hashCode(): Int {
var result = tracking.hashCode()
result = 31 * result + channelId.hashCode()
return result
}
}
)

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/appwidget_corner_radius_inner" />
<solid android:color="@android:color/system_accent2_100" />
</shape>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/appwidget_corner_radius_background" />
<solid android:color="@android:color/system_accent2_50" />
</shape>

View File

@@ -3,5 +3,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/appwidget_corner_radius_inner" />
<solid android:color="@color/kotatsu_primaryContainer" />
<solid android:color="?android:panelColorBackground" />
</shape>

View File

@@ -3,5 +3,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/appwidget_corner_radius_background" />
<solid android:color="@color/kotatsu_background" />
<solid android:color="?android:colorBackground" />
</shape>

View File

@@ -72,7 +72,7 @@
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodyLarge"
app:layout_constraintBottom_toTopOf="@id/textView_subtitle"
app:layout_constraintEnd_toStartOf="@id/imageView_handle"
app:layout_constraintEnd_toStartOf="@id/imageView_edit"
app:layout_constraintStart_toEndOf="@id/imageView_cover3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
@@ -120,10 +120,23 @@
app:layout_constraintTop_toTopOf="@id/textView_subtitle"
app:srcCompat="@drawable/ic_eye" />
<ImageView
android:id="@+id/imageView_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/edit"
android:padding="@dimen/margin_normal"
android:src="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/imageView_handle"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageView_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/reorder"
android:padding="@dimen/margin_normal"
android:src="@drawable/ic_reorder_handle"

View File

@@ -9,10 +9,11 @@
android:id="@+id/ssiv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="1dp"
app:panEnabled="false"
app:quickScaleEnabled="false"
app:zoomEnabled="false" />
<include layout="@layout/layout_page_info" />
</org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonFrameLayout>
</org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonFrameLayout>

View File

@@ -8,70 +8,53 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:orientation="horizontal"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:paddingVertical="8dp"
tools:ignore="PrivateResource">
<include layout="@layout/image_frame" />
<LinearLayout
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:clipChildren="false"
android:ellipsize="marquee"
android:labelFor="@id/scrollView"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
tools:ignore="LabelFor"
tools:text="@string/color_theme" />
<HorizontalScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:clipToPadding="false"
android:orientation="vertical">
android:descendantFocusability="blocksDescendants"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:id="@+id/linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:baselineAligned="true"
android:baselineAlignedChildIndex="0"
android:orientation="horizontal">
android:orientation="horizontal"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
<TextView
android:id="@android:id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="marquee"
android:labelFor="@id/seekbar"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceListItem"
tools:ignore="LabelFor" />
</HorizontalScrollView>
<TextView
android:id="@android:id/summary"
style="@style/PreferenceSummaryTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary" />
<TextView
android:id="@android:id/summary"
style="@style/PreferenceSummaryTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:singleLine="true"
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
<HorizontalScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:clipToPadding="false"
android:descendantFocusability="blocksDescendants"
android:paddingStart="0dp"
android:paddingEnd="16dp"
android:scrollbars="none"
tools:ignore="UnusedAttribute">
<LinearLayout
android:id="@+id/linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
</LinearLayout>
</LinearLayout>

View File

@@ -20,6 +20,7 @@
android:numColumns="auto_fit"
android:orientation="horizontal"
android:padding="4dp"
android:scrollbarStyle="insideOverlay"
tools:listitem="@layout/item_shelf" />
<TextView

View File

@@ -7,29 +7,46 @@
<item name="colorPrimary">@color/m3_sys_color_dynamic_dark_primary</item>
<item name="colorPrimaryContainer">@color/m3_sys_color_dynamic_dark_primary_container</item>
<item name="colorOnPrimary">@color/m3_sys_color_dynamic_dark_on_primary</item>
<item name="colorOnPrimaryContainer">@color/m3_sys_color_dynamic_dark_on_primary_container</item>
<item name="colorOnPrimaryContainer">@color/m3_sys_color_dynamic_dark_on_primary_container
</item>
<item name="colorPrimaryInverse">@color/m3_sys_color_dynamic_dark_inverse_primary</item>
<item name="colorSecondary">@color/m3_sys_color_dynamic_dark_secondary</item>
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_dark_secondary_container</item>
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_dark_secondary_container
</item>
<item name="colorOnSecondary">@color/m3_sys_color_dynamic_dark_on_secondary</item>
<item name="colorOnSecondaryContainer">@color/m3_sys_color_dynamic_dark_on_secondary_container</item>
<item name="colorOnSecondaryContainer">
@color/m3_sys_color_dynamic_dark_on_secondary_container
</item>
<item name="colorTertiary">@color/m3_sys_color_dynamic_dark_tertiary</item>
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_dark_tertiary_container</item>
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_dark_tertiary_container
</item>
<item name="colorOnTertiary">@color/m3_sys_color_dynamic_dark_on_tertiary</item>
<item name="colorOnTertiaryContainer">@color/m3_sys_color_dynamic_dark_on_tertiary_container</item>
<item name="colorOnTertiaryContainer">
@color/m3_sys_color_dynamic_dark_on_tertiary_container
</item>
<item name="colorSurface">@color/m3_sys_color_dynamic_dark_surface</item>
<item name="colorSurfaceDim">@color/m3_sys_color_dynamic_dark_surface_dim</item>
<item name="colorSurfaceBright">@color/m3_sys_color_dynamic_dark_surface_bright</item>
<item name="colorSurfaceContainerLowest">@color/m3_sys_color_dynamic_dark_surface_container_lowest</item>
<item name="colorSurfaceContainerLow">@color/m3_sys_color_dynamic_dark_surface_container_low</item>
<item name="colorSurfaceContainerLowest">
@color/m3_sys_color_dynamic_dark_surface_container_lowest
</item>
<item name="colorSurfaceContainerLow">
@color/m3_sys_color_dynamic_dark_surface_container_low
</item>
<item name="colorSurfaceContainer">@color/m3_sys_color_dynamic_dark_surface_container</item>
<item name="colorSurfaceContainerHigh">@color/m3_sys_color_dynamic_dark_surface_container_high</item>
<item name="colorSurfaceContainerHighest">@color/m3_sys_color_dynamic_dark_surface_container_highest</item>
<item name="colorSurfaceContainerHigh">
@color/m3_sys_color_dynamic_dark_surface_container_high
</item>
<item name="colorSurfaceContainerHighest">
@color/m3_sys_color_dynamic_dark_surface_container_highest
</item>
<item name="colorSurfaceVariant">@color/m3_sys_color_dynamic_dark_surface_variant</item>
<item name="colorOnSurface">@color/m3_sys_color_dynamic_dark_on_surface</item>
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_dark_on_surface_variant</item>
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_dark_on_surface_variant
</item>
<item name="colorSurfaceInverse">@color/m3_sys_color_dynamic_dark_inverse_surface</item>
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_dark_inverse_on_surface</item>
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_dark_inverse_on_surface
</item>
<item name="colorOnBackground">@color/m3_sys_color_dynamic_dark_on_background</item>
<item name="colorError">@color/m3_sys_color_dark_error</item>
<item name="colorErrorContainer">@color/m3_sys_color_dark_error_container</item>
@@ -42,26 +59,42 @@
<item name="m3ColorExploreButton">@android:color/system_neutral2_800</item>
<item name="m3ColorBottomMenuBackground">@android:color/system_neutral2_800</item>
<!-- Default Framework Text Colors. -->
<item name="android:textColorPrimary">@color/m3_dynamic_dark_default_color_primary_text</item>
<item name="android:textColorPrimaryInverse">@color/m3_dynamic_default_color_primary_text</item>
<item name="android:textColorSecondary">@color/m3_dynamic_dark_default_color_secondary_text</item>
<item name="android:textColorSecondaryInverse">@color/m3_dynamic_default_color_secondary_text</item>
<item name="android:textColorTertiary">@color/m3_dynamic_dark_default_color_secondary_text</item>
<item name="android:textColorTertiaryInverse">@color/m3_dynamic_default_color_secondary_text</item>
<item name="android:textColorPrimaryDisableOnly">@color/m3_dynamic_dark_primary_text_disable_only</item>
<item name="android:textColorPrimaryInverseDisableOnly">@color/m3_dynamic_primary_text_disable_only</item>
<item name="android:textColorPrimary">@color/m3_dynamic_dark_default_color_primary_text
</item>
<item name="android:textColorPrimaryInverse">@color/m3_dynamic_default_color_primary_text
</item>
<item name="android:textColorSecondary">
@color/m3_dynamic_dark_default_color_secondary_text
</item>
<item name="android:textColorSecondaryInverse">
@color/m3_dynamic_default_color_secondary_text
</item>
<item name="android:textColorTertiary">@color/m3_dynamic_dark_default_color_secondary_text
</item>
<item name="android:textColorTertiaryInverse">
@color/m3_dynamic_default_color_secondary_text
</item>
<item name="android:textColorPrimaryDisableOnly">
@color/m3_dynamic_dark_primary_text_disable_only
</item>
<item name="android:textColorPrimaryInverseDisableOnly">
@color/m3_dynamic_primary_text_disable_only
</item>
<item name="android:textColorHint">@color/m3_dynamic_dark_hint_foreground</item>
<item name="android:textColorHintInverse">@color/m3_dynamic_hint_foreground</item>
<item name="android:textColorHighlight">@color/m3_dynamic_dark_highlighted_text</item>
<item name="android:textColorHighlightInverse">@color/m3_dynamic_highlighted_text</item>
<item name="android:textColorAlertDialogListItem">@color/m3_dynamic_dark_default_color_primary_text</item>
<item name="android:textColorAlertDialogListItem">
@color/m3_dynamic_dark_default_color_primary_text
</item>
<!-- Fixes -->
<item name="bottomNavigationStyle">@style/Widget.Kotatsu.BottomNavigationView</item>
</style>
<style name="Theme.Kotatsu.AppWidgetContainer" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:colorBackground">@color/m3_sys_color_dynamic_dark_secondary_container</item>
<item name="android:panelColorBackground">@color/m3_sys_color_dynamic_dark_inverse_primary</item>
<item name="android:colorBackground">@color/m3_ref_palette_dynamic_secondary20</item>
<item name="android:panelColorBackground">@color/m3_ref_palette_dynamic_secondary40</item>
<item name="colorTertiary">@color/m3_ref_palette_dynamic_secondary40</item>
</style>
</resources>

View File

@@ -13,31 +13,51 @@
<item name="isMaterial3DynamicColorApplied">true</item>
<!-- Color palettes -->
<item name="colorPrimary">@color/m3_sys_color_dynamic_light_primary</item>
<item name="colorPrimaryContainer">@color/m3_sys_color_dynamic_light_primary_container</item>
<item name="colorPrimaryContainer">@color/m3_sys_color_dynamic_light_primary_container
</item>
<item name="colorOnPrimary">@color/m3_sys_color_dynamic_light_on_primary</item>
<item name="colorOnPrimaryContainer">@color/m3_sys_color_dynamic_light_on_primary_container</item>
<item name="colorOnPrimaryContainer">
@color/m3_sys_color_dynamic_light_on_primary_container
</item>
<item name="colorPrimaryInverse">@color/m3_sys_color_dynamic_light_inverse_primary</item>
<item name="colorSecondary">@color/m3_sys_color_dynamic_light_secondary</item>
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_light_secondary_container</item>
<item name="colorSecondaryContainer">@color/m3_sys_color_dynamic_light_secondary_container
</item>
<item name="colorOnSecondary">@color/m3_sys_color_dynamic_light_on_secondary</item>
<item name="colorOnSecondaryContainer">@color/m3_sys_color_dynamic_light_on_secondary_container</item>
<item name="colorOnSecondaryContainer">
@color/m3_sys_color_dynamic_light_on_secondary_container
</item>
<item name="colorTertiary">@color/m3_sys_color_dynamic_light_tertiary</item>
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_light_tertiary_container</item>
<item name="colorTertiaryContainer">@color/m3_sys_color_dynamic_light_tertiary_container
</item>
<item name="colorOnTertiary">@color/m3_sys_color_dynamic_light_on_tertiary</item>
<item name="colorOnTertiaryContainer">@color/m3_sys_color_dynamic_light_on_tertiary_container</item>
<item name="colorOnTertiaryContainer">
@color/m3_sys_color_dynamic_light_on_tertiary_container
</item>
<item name="colorSurface">@color/m3_sys_color_dynamic_light_surface</item>
<item name="colorSurfaceDim">@color/m3_sys_color_dynamic_light_surface_dim</item>
<item name="colorSurfaceBright">@color/m3_sys_color_dynamic_light_surface_bright</item>
<item name="colorSurfaceContainerLowest">@color/m3_sys_color_dynamic_light_surface_container_lowest</item>
<item name="colorSurfaceContainerLow">@color/m3_sys_color_dynamic_light_surface_container_low</item>
<item name="colorSurfaceContainer">@color/m3_sys_color_dynamic_light_surface_container</item>
<item name="colorSurfaceContainerHigh">@color/m3_sys_color_dynamic_light_surface_container_high</item>
<item name="colorSurfaceContainerHighest">@color/m3_sys_color_dynamic_light_surface_container_highest</item>
<item name="colorSurfaceContainerLowest">
@color/m3_sys_color_dynamic_light_surface_container_lowest
</item>
<item name="colorSurfaceContainerLow">
@color/m3_sys_color_dynamic_light_surface_container_low
</item>
<item name="colorSurfaceContainer">@color/m3_sys_color_dynamic_light_surface_container
</item>
<item name="colorSurfaceContainerHigh">
@color/m3_sys_color_dynamic_light_surface_container_high
</item>
<item name="colorSurfaceContainerHighest">
@color/m3_sys_color_dynamic_light_surface_container_highest
</item>
<item name="colorSurfaceVariant">@color/m3_sys_color_dynamic_light_surface_variant</item>
<item name="colorOnSurface">@color/m3_sys_color_dynamic_light_on_surface</item>
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_light_on_surface_variant</item>
<item name="colorOnSurfaceVariant">@color/m3_sys_color_dynamic_light_on_surface_variant
</item>
<item name="colorSurfaceInverse">@color/m3_sys_color_dynamic_light_inverse_surface</item>
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_light_inverse_on_surface</item>
<item name="colorOnSurfaceInverse">@color/m3_sys_color_dynamic_light_inverse_on_surface
</item>
<item name="colorOnBackground">@color/m3_sys_color_dynamic_light_on_background</item>
<item name="colorError">@color/m3_sys_color_light_error</item>
<item name="colorErrorContainer">@color/m3_sys_color_light_error_container</item>
@@ -51,25 +71,40 @@
<item name="m3ColorBottomMenuBackground">@android:color/system_neutral2_100</item>
<!-- Default Framework Text Colors. -->
<item name="android:textColorPrimary">@color/m3_dynamic_default_color_primary_text</item>
<item name="android:textColorPrimaryInverse">@color/m3_dynamic_dark_default_color_primary_text</item>
<item name="android:textColorSecondary">@color/m3_dynamic_default_color_secondary_text</item>
<item name="android:textColorSecondaryInverse">@color/m3_dynamic_dark_default_color_secondary_text</item>
<item name="android:textColorPrimaryInverse">
@color/m3_dynamic_dark_default_color_primary_text
</item>
<item name="android:textColorSecondary">@color/m3_dynamic_default_color_secondary_text
</item>
<item name="android:textColorSecondaryInverse">
@color/m3_dynamic_dark_default_color_secondary_text
</item>
<item name="android:textColorTertiary">@color/m3_dynamic_default_color_secondary_text</item>
<item name="android:textColorTertiaryInverse">@color/m3_dynamic_dark_default_color_secondary_text</item>
<item name="android:textColorPrimaryDisableOnly">@color/m3_dynamic_primary_text_disable_only</item>
<item name="android:textColorPrimaryInverseDisableOnly">@color/m3_dynamic_dark_primary_text_disable_only</item>
<item name="android:textColorTertiaryInverse">
@color/m3_dynamic_dark_default_color_secondary_text
</item>
<item name="android:textColorPrimaryDisableOnly">
@color/m3_dynamic_primary_text_disable_only
</item>
<item name="android:textColorPrimaryInverseDisableOnly">
@color/m3_dynamic_dark_primary_text_disable_only
</item>
<item name="android:textColorHint">@color/m3_dynamic_hint_foreground</item>
<item name="android:textColorHintInverse">@color/m3_dynamic_dark_hint_foreground</item>
<item name="android:textColorHighlight">@color/m3_dynamic_highlighted_text</item>
<item name="android:textColorHighlightInverse">@color/m3_dynamic_dark_highlighted_text</item>
<item name="android:textColorAlertDialogListItem">@color/m3_dynamic_default_color_primary_text</item>
<item name="android:textColorHighlightInverse">@color/m3_dynamic_dark_highlighted_text
</item>
<item name="android:textColorAlertDialogListItem">
@color/m3_dynamic_default_color_primary_text
</item>
<!-- Fixes -->
<item name="bottomNavigationStyle">@style/Widget.Kotatsu.BottomNavigationView</item>
</style>
<style name="Theme.Kotatsu.AppWidgetContainer" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:colorBackground">@color/m3_sys_color_dynamic_light_secondary_container</item>
<item name="android:panelColorBackground">@color/m3_sys_color_dynamic_light_inverse_primary</item>
<item name="android:colorBackground">@color/m3_ref_palette_dynamic_secondary95</item>
<item name="android:panelColorBackground">@color/m3_ref_palette_dynamic_secondary90</item>
<item name="colorTertiary">@color/m3_ref_palette_dynamic_secondary70</item>
</style>
</resources>

View File

@@ -75,23 +75,31 @@
<item name="textInputStyle">@style/Widget.Material3.TextInputLayout.OutlinedBox</item>
<item name="toolbarStyle">@style/Widget.Material3.Toolbar</item>
<item name="appBarLayoutStyle">@style/Widget.Kotatsu.AppBarLayout</item>
<item name="bottomNavigationStyle">@style/Widget.Kotatsu.BottomNavigationView.ColoredIndicators</item>
<item name="bottomNavigationStyle">
@style/Widget.Kotatsu.BottomNavigationView.ColoredIndicators
</item>
<item name="tabStyle">@style/Widget.Kotatsu.Tabs</item>
<item name="materialCardViewStyle">@style/Widget.Kotatsu.MaterialCardView.Filled</item>
<item name="recyclerViewStyle">@style/Widget.Kotatsu.RecyclerView</item>
<item name="fastScrollerStyle">@style/Widget.Kotatsu.FastScroller</item>
<item name="listItemTextViewStyle">@style/Widget.Kotatsu.ListItemTextView</item>
<item name="materialSwitchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
<item name="materialSwitchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch
</item>
<item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.M3</item>
<item name="collapsingToolbarLayoutStyle">@style/Widget.Kotatsu.CollapsingToolbar</item>
<item name="circularProgressIndicatorStyle">@style/Widget.Kotatsu.CircularProgressIndicator</item>
<item name="linearProgressIndicatorStyle">@style/Widget.Kotatsu.LinearProgressIndicator</item>
<item name="circularProgressIndicatorStyle">
@style/Widget.Kotatsu.CircularProgressIndicator
</item>
<item name="linearProgressIndicatorStyle">@style/Widget.Kotatsu.LinearProgressIndicator
</item>
<item name="bottomSheetDragHandleStyle">@style/Widget.Kotatsu.BottomSheet.DragHandle</item>
<!-- Text appearance -->
<item name="actionMenuTextAppearance">@style/TextAppearance.Kotatsu.Menu</item>
<item name="android:textAppearanceListItem">?attr/textAppearanceBodyLarge</item>
<item name="android:textAppearanceListItemSecondary">@style/TextAppearance.Kotatsu.Preference.Secondary</item>
<item name="android:textAppearanceListItemSecondary">
@style/TextAppearance.Kotatsu.Preference.Secondary
</item>
</style>
@@ -106,7 +114,7 @@
<style name="ThemeOverlay.Kotatsu.Amoled" parent="" />
<style name="Theme.Kotatsu.AppWidgetContainer" parent="@style/Theme.MaterialComponents.Light">
<item name="android:colorBackground">@android:color/transparent</item>
<item name="android:colorBackground">@color/kotatsu_background</item>
<item name="android:panelColorBackground">@color/kotatsu_primaryContainer</item>
</style>
</resources>