Refactor chapters mapping

This commit is contained in:
Koitharu
2023-05-22 18:08:05 +03:00
parent 84567767a0
commit a8f5714b35
12 changed files with 200 additions and 218 deletions

View File

@@ -32,6 +32,9 @@ class ChaptersBottomSheetMediator(
override fun onExpansionStateChanged(headerBar: BottomSheetHeaderBar, isExpanded: Boolean) {
isEnabled = isExpanded
if (!isExpanded) {
unlock()
}
}
override fun onLayoutChange(

View File

@@ -73,10 +73,6 @@ class ChaptersFragment :
if (selectionController?.onItemClick(item.chapter.id) == true) {
return
}
if (item.hasFlag(ChapterListItem.FLAG_MISSING)) {
(activity as? DetailsActivity)?.showChapterMissingDialog(item.chapter.id)
return
}
startActivity(
ReaderActivity.newIntent(
context = view.context,
@@ -193,7 +189,7 @@ class ChaptersFragment :
private fun onChaptersChanged(list: List<ChapterListItem>) {
val adapter = chaptersAdapter ?: return
if (adapter.itemCount == 0) {
val position = list.indexOfFirst { it.hasFlag(ChapterListItem.FLAG_CURRENT) } - 1
val position = list.indexOfFirst { it.isCurrent } - 1
if (position > 0) {
val offset = (resources.getDimensionPixelSize(R.dimen.chapter_list_item_height) * 0.6).roundToInt()
adapter.setItems(

View File

@@ -0,0 +1,60 @@
package org.koitharu.kotatsu.details.ui
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.parsers.model.Manga
fun mapChapters(
remoteManga: Manga?,
localManga: Manga?,
history: MangaHistory?,
newCount: Int,
branch: String?,
): List<ChapterListItem> {
val remoteChapters = remoteManga?.getChapters(branch).orEmpty()
val localChapters = localManga?.getChapters(branch).orEmpty()
if (remoteChapters.isEmpty() && localChapters.isEmpty()) {
return emptyList()
}
val currentId = history?.chapterId ?: 0L
val newFrom = if (newCount == 0 || remoteChapters.isEmpty()) Int.MAX_VALUE else remoteChapters.size - newCount
val chaptersSize = maxOf(remoteChapters.size, localChapters.size)
val ids = buildSet(chaptersSize) {
remoteChapters.mapTo(this) { it.id }
localChapters.mapTo(this) { it.id }
}
val result = ArrayList<ChapterListItem>(chaptersSize)
val localMap = if (localChapters.isNotEmpty()) {
localChapters.associateByTo(LinkedHashMap(localChapters.size)) { it.id }
} else {
null
}
var isUnread = currentId !in ids
for (chapter in remoteChapters) {
val local = localMap?.remove(chapter.id)
if (chapter.id == currentId) {
isUnread = true
}
result += chapter.toListItem(
isCurrent = chapter.id == currentId,
isUnread = isUnread,
isNew = isUnread && result.size >= newFrom,
isDownloaded = local != null,
)
}
if (!localMap.isNullOrEmpty()) {
for (chapter in localMap.values) {
if (chapter.id == currentId) {
isUnread = true
}
result += chapter.toListItem(
isCurrent = chapter.id == currentId,
isUnread = isUnread,
isNew = false,
isDownloaded = false,
)
}
}
return result
}

View File

@@ -19,7 +19,6 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.lifecycle.Observer
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
@@ -45,7 +44,6 @@ import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
import javax.inject.Inject
@@ -244,33 +242,6 @@ class DetailsActivity :
viewBadge.counter = newChapters
}
fun showChapterMissingDialog(chapterId: Long) {
val remoteManga = viewModel.getRemoteManga()
if (remoteManga == null) {
val snackbar = makeSnackbar(getString(R.string.chapter_is_missing), Snackbar.LENGTH_SHORT)
snackbar.show()
return
}
MaterialAlertDialogBuilder(this).apply {
setMessage(R.string.chapter_is_missing_text)
setTitle(R.string.chapter_is_missing)
setNegativeButton(android.R.string.cancel, null)
setPositiveButton(R.string.read) { _, _ ->
startActivity(
ReaderActivity.newIntent(
context = this@DetailsActivity,
manga = remoteManga,
state = ReaderState(chapterId, 0, 0),
),
)
}
setNeutralButton(R.string.download) { _, _ ->
viewModel.download(setOf(chapterId))
}
setCancelable(true)
}.show()
}
private fun showBranchPopupMenu() {
var dialog: DialogInterface? = null
val listener = OnListItemClickListener<MangaBranch> { item, _ ->
@@ -291,7 +262,8 @@ class DetailsActivity :
val manga = viewModel.manga.value ?: return
val chapterId = viewModel.historyInfo.value?.history?.chapterId
if (chapterId != null && manga.chapters?.none { x -> x.id == chapterId } == true) {
showChapterMissingDialog(chapterId)
val snackbar = makeSnackbar(getString(R.string.chapter_is_missing), Snackbar.LENGTH_SHORT)
snackbar.show()
} else {
startActivity(
ReaderActivity.newIntent(
@@ -339,7 +311,7 @@ class DetailsActivity :
}
if (!isCalled) {
isCalled = true
val item = value.find { it.hasFlag(ChapterListItem.FLAG_CURRENT) } ?: value.first()
val item = value.find { it.isCurrent } ?: value.first()
MangaPrefetchService.prefetchPages(context, item.chapter)
}
}

View File

@@ -78,6 +78,13 @@ class DetailsViewModel @Inject constructor(
val onShowToast = SingleLiveEvent<Int>()
val onDownloadStarted = SingleLiveEvent<Unit>()
private val mangaData = combine(
delegate.onlineManga,
delegate.localManga,
) { o, l ->
o ?: l
}.stateIn(viewModelScope, SharingStarted.Lazily, null)
private val history = historyRepository.observeOne(delegate.mangaId)
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
@@ -98,16 +105,16 @@ class DetailsViewModel @Inject constructor(
private val chaptersReversed = settings.observeAsFlow(AppSettings.KEY_REVERSE_CHAPTERS) { chaptersReverse }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, false)
val manga = delegate.manga.filterNotNull().asLiveData(viewModelScope.coroutineContext)
val manga = mangaData.filterNotNull().asLiveData(viewModelScope.coroutineContext)
val favouriteCategories = favourite.asLiveData(viewModelScope.coroutineContext)
val newChaptersCount = newChapters.asLiveData(viewModelScope.coroutineContext)
val isChaptersReversed = chaptersReversed.asLiveData(viewModelScope.coroutineContext)
val historyInfo: LiveData<HistoryInfo> = combine(
delegate.manga,
mangaData,
delegate.selectedBranch,
history,
historyRepository.observeShouldSkip(delegate.manga),
historyRepository.observeShouldSkip(mangaData),
) { m, b, h, im ->
HistoryInfo(m, b, h, im)
}.asFlowLiveData(
@@ -115,28 +122,21 @@ class DetailsViewModel @Inject constructor(
defaultValue = HistoryInfo(null, null, null, false),
)
val bookmarks = delegate.manga.flatMapLatest {
val bookmarks = mangaData.flatMapLatest {
if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList())
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
val localSize = combine(
delegate.manga,
delegate.relatedManga,
) { m1, m2 ->
val url = when {
m1?.source == MangaSource.LOCAL -> m1.url
m2?.source == MangaSource.LOCAL -> m2.url
else -> null
}
if (url != null) {
val file = url.toUri().toFileOrNull()
file?.computeSize() ?: 0L
} else {
0L
}
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, 0)
val localSize = delegate.localManga
.map {
if (it != null) {
val file = it.url.toUri().toFileOrNull()
file?.computeSize() ?: 0L
} else {
0L
}
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, 0)
val description = delegate.manga
val description = mangaData
.distinctUntilChangedBy { it?.description.orEmpty() }
.transformLatest {
val description = it?.description
@@ -159,10 +159,12 @@ class DetailsViewModel @Inject constructor(
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
val branches: LiveData<List<MangaBranch>> = combine(
delegate.manga,
delegate.onlineManga,
delegate.localManga,
delegate.selectedBranch,
) { m, b ->
val chapters = m?.chapters ?: return@combine emptyList()
) { m, l, b ->
val chapters = concat(m?.chapters, l?.chapters)
if (chapters.isEmpty()) return@combine emptyList()
chapters.groupBy { x -> x.branch }
.map { x -> MangaBranch(x.key, x.value.size, x.key == b) }
.sortedWith(BranchComparator())
@@ -172,21 +174,24 @@ class DetailsViewModel @Inject constructor(
.asFlowLiveData(viewModelScope.coroutineContext, null)
val isChaptersEmpty: LiveData<Boolean> = combine(
delegate.manga,
delegate.onlineManga,
delegate.localManga,
isLoading.asFlow(),
) { m, loading ->
m != null && m.chapters.isNullOrEmpty() && !loading
) { manga, local, loading ->
(manga != null && manga.chapters.isNullOrEmpty()) &&
(local != null && local.chapters.isNullOrEmpty()) &&
!loading
}.asFlowLiveData(viewModelScope.coroutineContext, false)
val chapters = combine(
combine(
delegate.manga,
delegate.relatedManga,
delegate.onlineManga,
delegate.localManga,
history,
delegate.selectedBranch,
newChapters,
) { manga, related, history, branch, news ->
delegate.mapChapters(manga, related, history, news, branch)
) { manga, local, history, branch, news ->
mapChapters(manga, local, history, news, branch)
},
chaptersReversed,
chaptersQuery,
@@ -211,7 +216,7 @@ class DetailsViewModel @Inject constructor(
}
fun deleteLocal() {
val m = delegate.manga.value
val m = delegate.localManga.value
if (m == null) {
onShowToast.call(R.string.file_not_found)
return
@@ -244,7 +249,7 @@ class DetailsViewModel @Inject constructor(
}
fun getRemoteManga(): Manga? {
return delegate.relatedManga.value?.takeUnless { it.source == MangaSource.LOCAL }
return delegate.onlineManga.value
}
fun performChapterSearch(query: String?) {
@@ -274,7 +279,7 @@ class DetailsViewModel @Inject constructor(
fun markChapterAsCurrent(chapterId: Long) {
launchJob(Dispatchers.Default) {
val manga = checkNotNull(delegate.manga.value)
val manga = checkNotNull(mangaData.value)
val chapters = checkNotNull(manga.getChapters(selectedBranchValue))
val chapterIndex = chapters.indexOfFirst { it.id == chapterId }
check(chapterIndex in chapters.indices) { "Chapter not found" }
@@ -286,7 +291,7 @@ class DetailsViewModel @Inject constructor(
fun download(chaptersIds: Set<Long>?) {
launchJob(Dispatchers.Default) {
downloadScheduler.schedule(
getRemoteManga() ?: checkNotNull(manga.value),
delegate.onlineManga.value ?: checkNotNull(manga.value),
chaptersIds,
)
onDownloadStarted.emitCall(Unit)
@@ -308,7 +313,7 @@ class DetailsViewModel @Inject constructor(
private suspend fun onDownloadComplete(downloadedManga: LocalManga?) {
downloadedManga ?: return
val currentManga = delegate.manga.value ?: return
val currentManga = mangaData.value ?: return
if (currentManga.id != downloadedManga.manga.id) {
return
}
@@ -319,7 +324,7 @@ class DetailsViewModel @Inject constructor(
runCatchingCancellable {
localMangaRepository.getDetails(downloadedManga.manga)
}.onSuccess {
delegate.relatedManga.value = it
delegate.publishManga(it)
}.onFailure {
it.printStackTraceDebug()
}
@@ -348,4 +353,18 @@ class DetailsViewModel @Inject constructor(
}
return scrobbler
}
private fun <T> concat(a: List<T>?, b: List<T>?): List<T> {
return when {
a == null && b == null -> emptyList<T>()
a == null && b != null -> b
a != null && b == null -> a
a != null && b != null -> buildList<T>(a.size + b.size) {
addAll(a)
addAll(b)
}
else -> error("This shouldn't have happened")
}
}
}

View File

@@ -1,21 +1,23 @@
package org.koitharu.kotatsu.details.ui
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.ViewModelLifecycle
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.koitharu.kotatsu.core.model.MangaHistory
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import org.koitharu.kotatsu.core.model.getPreferredBranch
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaIntent
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope
import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.util.ext.printStackTraceDebug
@@ -24,31 +26,44 @@ import javax.inject.Inject
@ViewModelScoped
class MangaDetailsDelegate @Inject constructor(
savedStateHandle: SavedStateHandle,
lifecycle: ViewModelLifecycle,
private val mangaDataRepository: MangaDataRepository,
private val historyRepository: HistoryRepository,
private val localMangaRepository: LocalMangaRepository,
private val mangaRepositoryFactory: MangaRepository.Factory,
networkState: NetworkState,
) {
private val viewModelScope = RetainedLifecycleCoroutineScope(lifecycle)
private val intent = MangaIntent(savedStateHandle)
private val mangaData = MutableStateFlow(intent.manga)
private val onlineMangaStateFlow = MutableStateFlow<Manga?>(null)
private val localMangaStateFlow = MutableStateFlow<Manga?>(null)
val onlineManga = combine(
onlineMangaStateFlow,
networkState,
) { m, s -> m.takeIf { s } }
.stateIn(viewModelScope, SharingStarted.Lazily, null)
val localManga = localMangaStateFlow.asStateFlow()
val selectedBranch = MutableStateFlow<String?>(null)
// Remote manga for saved and saved for remote
val relatedManga = MutableStateFlow<Manga?>(null)
val manga: StateFlow<Manga?>
get() = mangaData
val mangaId = intent.manga?.id ?: intent.mangaId
init {
intent.manga?.let {
publishManga(it)
}
}
suspend fun doLoad() {
var manga = mangaDataRepository.resolveIntent(intent) ?: throw NotFoundException("Cannot find manga", "")
mangaData.value = manga
publishManga(manga)
manga = mangaRepositoryFactory.create(manga.source).getDetails(manga)
// find default branch
val hist = historyRepository.getOne(manga)
selectedBranch.value = manga.getPreferredBranch(hist)
mangaData.value = manga
relatedManga.value = runCatchingCancellable {
publishManga(manga)
runCatchingCancellable {
if (manga.source == MangaSource.LOCAL) {
val m = localMangaRepository.getRemoteManga(manga) ?: return@runCatchingCancellable null
mangaRepositoryFactory.create(m.source).getDetails(m)
@@ -57,106 +72,18 @@ class MangaDetailsDelegate @Inject constructor(
}
}.onFailure { error ->
error.printStackTraceDebug()
}.getOrNull()
}.onSuccess {
if (it != null) {
publishManga(it)
}
}
}
fun mapChapters(
manga: Manga?,
related: Manga?,
history: MangaHistory?,
newCount: Int,
branch: String?,
): List<ChapterListItem> {
val chapters = manga?.chapters ?: return emptyList()
val relatedChapters = related?.chapters
return if (related?.source != MangaSource.LOCAL && !relatedChapters.isNullOrEmpty()) {
mapChaptersWithSource(chapters, relatedChapters, history?.chapterId, newCount, branch)
fun publishManga(manga: Manga) {
if (manga.source == MangaSource.LOCAL) {
localMangaStateFlow
} else {
mapChapters(chapters, relatedChapters, history?.chapterId, newCount, branch)
}
}
private fun mapChapters(
chapters: List<MangaChapter>,
downloadedChapters: List<MangaChapter>?,
currentId: Long?,
newCount: Int,
branch: String?,
): List<ChapterListItem> {
val result = ArrayList<ChapterListItem>(chapters.size)
val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount
val downloadedIds = downloadedChapters?.mapTo(HashSet(downloadedChapters.size)) { it.id }
for (i in chapters.indices) {
val chapter = chapters[i]
if (chapter.branch != branch) {
continue
}
result += chapter.toListItem(
isCurrent = i == currentIndex,
isUnread = i > currentIndex,
isNew = i >= firstNewIndex,
isMissing = false,
isDownloaded = downloadedIds?.contains(chapter.id) == true,
)
}
if (result.size < chapters.size / 2) {
result.trimToSize()
}
return result
}
private fun mapChaptersWithSource(
chapters: List<MangaChapter>,
sourceChapters: List<MangaChapter>,
currentId: Long?,
newCount: Int,
branch: String?,
): List<ChapterListItem> {
val chaptersMap = chapters.associateByTo(HashMap(chapters.size)) { it.id }
val result = ArrayList<ChapterListItem>(sourceChapters.size)
val currentIndex = sourceChapters.indexOfFirst { it.id == currentId }
val firstNewIndex = sourceChapters.size - newCount
for (i in sourceChapters.indices) {
val chapter = sourceChapters[i]
val localChapter = chaptersMap.remove(chapter.id)
if (chapter.branch != branch) {
continue
}
result += localChapter?.toListItem(
isCurrent = i == currentIndex,
isUnread = i > currentIndex,
isNew = i >= firstNewIndex,
isMissing = false,
isDownloaded = false,
) ?: chapter.toListItem(
isCurrent = i == currentIndex,
isUnread = i > currentIndex,
isNew = i >= firstNewIndex,
isMissing = true,
isDownloaded = false,
)
}
if (chaptersMap.isNotEmpty()) { // some chapters on device but not online source
result.ensureCapacity(result.size + chaptersMap.size)
chaptersMap.values.mapNotNullTo(result) {
if (it.branch == branch) {
it.toListItem(
isCurrent = false,
isUnread = true,
isNew = false,
isMissing = false,
isDownloaded = false,
)
} else {
null
}
}
result.sortBy { it.chapter.number }
}
if (result.size < sourceChapters.size / 2) {
result.trimToSize()
}
return result
onlineMangaStateFlow
}.value = manga
}
}

View File

@@ -9,11 +9,7 @@ import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemChapterBinding
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_MISSING
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_UNREAD
import com.google.android.material.R as materialR
fun chapterListItemAD(
clickListener: OnListItemClickListener<ChapterListItem>,
@@ -31,15 +27,15 @@ fun chapterListItemAD(
binding.textViewNumber.text = item.chapter.number.toString()
binding.textViewDescription.textAndVisible = item.description()
}
when (item.status) {
FLAG_UNREAD -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
binding.textViewNumber.setTextColor(context.getThemeColor(com.google.android.material.R.attr.colorOnTertiary))
when {
item.isCurrent -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_primary)
binding.textViewNumber.setTextColor(context.getThemeColor(materialR.attr.colorOnPrimary))
}
FLAG_CURRENT -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_accent)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse))
item.isUnread -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_default)
binding.textViewNumber.setTextColor(context.getThemeColor(materialR.attr.colorOnTertiary))
}
else -> {
@@ -47,12 +43,7 @@ fun chapterListItemAD(
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
}
}
val isMissing = item.hasFlag(FLAG_MISSING)
binding.textViewTitle.alpha = if (isMissing) 0.3f else 1f
binding.textViewDescription.alpha = if (isMissing) 0.3f else 1f
binding.textViewNumber.alpha = if (isMissing) 0.3f else 1f
binding.imageViewDownloaded.isVisible = item.hasFlag(FLAG_DOWNLOADED)
binding.imageViewNew.isVisible = item.hasFlag(FLAG_NEW)
binding.imageViewDownloaded.isVisible = item.isDownloaded
binding.imageViewNew.isVisible = item.isNew
}
}

View File

@@ -22,12 +22,17 @@ class ChapterListItem(
return field
}
val status: Int
get() = flags and MASK_STATUS
val isCurrent: Boolean
get() = hasFlag(FLAG_CURRENT)
fun hasFlag(flag: Int): Boolean {
return (flags and flag) == flag
}
val isUnread: Boolean
get() = hasFlag(FLAG_UNREAD)
val isDownloaded: Boolean
get() = hasFlag(FLAG_DOWNLOADED)
val isNew: Boolean
get() = hasFlag(FLAG_NEW)
fun description(): CharSequence? {
val scanlator = chapter.scanlator?.takeUnless { it.isBlank() }
@@ -38,6 +43,10 @@ class ChapterListItem(
}
}
private fun hasFlag(flag: Int): Boolean {
return (flags and flag) == flag
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -46,9 +55,7 @@ class ChapterListItem(
if (chapter != other.chapter) return false
if (flags != other.flags) return false
if (uploadDateMs != other.uploadDateMs) return false
return true
return uploadDateMs == other.uploadDateMs
}
override fun hashCode(): Int {
@@ -63,8 +70,6 @@ class ChapterListItem(
const val FLAG_UNREAD = 2
const val FLAG_CURRENT = 4
const val FLAG_NEW = 8
const val FLAG_MISSING = 16
const val FLAG_DOWNLOADED = 32
const val MASK_STATUS = FLAG_UNREAD or FLAG_CURRENT
}
}

View File

@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_CURRENT
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_DOWNLOADED
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_MISSING
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_NEW
import org.koitharu.kotatsu.details.ui.model.ChapterListItem.Companion.FLAG_UNREAD
import org.koitharu.kotatsu.parsers.model.MangaChapter
@@ -11,14 +10,12 @@ fun MangaChapter.toListItem(
isCurrent: Boolean,
isUnread: Boolean,
isNew: Boolean,
isMissing: Boolean,
isDownloaded: Boolean,
): ChapterListItem {
var flags = 0
if (isCurrent) flags = flags or FLAG_CURRENT
if (isUnread) flags = flags or FLAG_UNREAD
if (isNew) flags = flags or FLAG_NEW
if (isMissing) flags = flags or FLAG_MISSING
if (isDownloaded) flags = flags or FLAG_DOWNLOADED
return ChapterListItem(
chapter = this,

View File

@@ -46,7 +46,6 @@ class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemC
isCurrent = index == currentPosition,
isUnread = index > currentPosition,
isNew = false,
isMissing = false,
isDownloaded = false,
)
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="?colorPrimary" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>

View File

@@ -47,6 +47,7 @@
<string name="theme">Theme</string>
<string name="light">Light</string>
<string name="dark">Dark</string>
<!-- Should be as abstract as possible -->
<string name="automatic">Follow system</string>
<string name="pages">Pages</string>
<string name="clear">Clear</string>
@@ -397,6 +398,7 @@
<string name="pause">Pause</string>
<string name="resume">Resume</string>
<string name="paused">Paused</string>
<!-- Menu item; action to remove completed items -->
<string name="remove_completed">Remove completed</string>
<string name="cancel_all">Cancel all</string>
<string name="downloads_wifi_only">Download only via Wi-Fi</string>