Update chapters list, show already downloaded chapters #95

pull/104/head
Koitharu 4 years ago
parent c7dc05be5a
commit 5758eed77b
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -162,7 +162,7 @@ class MangaDexRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
url = id, url = id,
scanlator = relations["scanlation_group"]?.getStringOrNull("name"), scanlator = relations["scanlation_group"]?.getStringOrNull("name"),
uploadDate = dateFormat.tryParse(attrs.getString("publishAt")), uploadDate = dateFormat.tryParse(attrs.getString("publishAt")),
branch = locale.displayName.toTitleCase(locale), branch = locale.getDisplayName(locale).toTitleCase(locale),
source = source, source = source,
) )
} }

@ -9,8 +9,8 @@ import androidx.appcompat.view.ActionMode
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.BaseFragment
@ -51,12 +51,7 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
chaptersAdapter = ChaptersAdapter(this) chaptersAdapter = ChaptersAdapter(this)
selectionDecoration = ChaptersSelectionDecoration(view.context) selectionDecoration = ChaptersSelectionDecoration(view.context)
with(binding.recyclerViewChapters) { with(binding.recyclerViewChapters) {
addItemDecoration( addItemDecoration(MaterialDividerItemDecoration(view.context, RecyclerView.VERTICAL))
DividerItemDecoration(
view.context,
RecyclerView.VERTICAL
)
)
addItemDecoration(selectionDecoration!!) addItemDecoration(selectionDecoration!!)
setHasFixedSize(true) setHasFixedSize(true)
adapter = chaptersAdapter adapter = chaptersAdapter
@ -117,7 +112,7 @@ class ChaptersFragment : BaseFragment<FragmentChaptersBinding>(),
} }
return return
} }
if (item.isMissing) { if (item.hasFlag(ChapterListItem.FLAG_MISSING)) {
(activity as? DetailsActivity)?.showChapterMissingDialog(item.chapter.id) (activity as? DetailsActivity)?.showChapterMissingDialog(item.chapter.id)
return return
} }

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.details.ui package org.koitharu.kotatsu.details.ui
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.asFlow import androidx.lifecycle.asFlow
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -18,15 +19,14 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.history.domain.ChapterExtra
import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.history.domain.HistoryRepository
import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.iterator
import org.koitharu.kotatsu.utils.ext.mapToSet import org.koitharu.kotatsu.utils.ext.mapToSet
import org.koitharu.kotatsu.utils.ext.toTitleCase import org.koitharu.kotatsu.utils.ext.toTitleCase
import java.io.IOException import java.io.IOException
import java.util.*
class DetailsViewModel( class DetailsViewModel(
intent: MangaIntent, intent: MangaIntent,
@ -60,16 +60,6 @@ class DetailsViewModel(
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, 0) }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, 0)
private val remoteManga = MutableStateFlow<Manga?>(null) private val remoteManga = MutableStateFlow<Manga?>(null)
/*private val remoteManga = mangaData.mapLatest {
if (it?.source == MangaSource.LOCAL) {
runCatching {
val m = localMangaRepository.getRemoteManga(it) ?: return@mapLatest null
MangaRepository(m.source).getDetails(m)
}.getOrNull()
} else {
null
}
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)*/
private val chaptersReversed = settings.observe() private val chaptersReversed = settings.observe()
.filter { it == AppSettings.KEY_REVERSE_CHAPTERS } .filter { it == AppSettings.KEY_REVERSE_CHAPTERS }
@ -109,10 +99,10 @@ class DetailsViewModel(
selectedBranch selectedBranch
) { chapters, sourceManga, currentId, newCount, branch -> ) { chapters, sourceManga, currentId, newCount, branch ->
val sourceChapters = sourceManga?.chapters val sourceChapters = sourceManga?.chapters
if (sourceChapters.isNullOrEmpty()) { if (sourceManga?.source != MangaSource.LOCAL && !sourceChapters.isNullOrEmpty()) {
mapChapters(chapters, currentId, newCount, branch)
} else {
mapChaptersWithSource(chapters, sourceChapters, currentId, newCount, branch) mapChaptersWithSource(chapters, sourceChapters, currentId, newCount, branch)
} else {
mapChapters(chapters, sourceChapters, currentId, newCount, branch)
} }
}.combine(chaptersReversed) { list, reversed -> }.combine(chaptersReversed) { list, reversed ->
if (reversed) list.asReversed() else list if (reversed) list.asReversed() else list
@ -132,12 +122,14 @@ class DetailsViewModel(
predictBranch(manga.chapters) predictBranch(manga.chapters)
} }
mangaData.value = manga mangaData.value = manga
if (manga.source == MangaSource.LOCAL) { remoteManga.value = runCatching {
remoteManga.value = runCatching { if (manga.source == MangaSource.LOCAL) {
val m = localMangaRepository.getRemoteManga(manga) ?: return@runCatching null val m = localMangaRepository.getRemoteManga(manga) ?: return@runCatching null
MangaRepository(m.source).getDetails(m) MangaRepository(m.source).getDetails(m)
}.getOrNull() } else {
} localMangaRepository.findSavedManga(manga)
}
}.getOrNull()
} }
} }
@ -166,6 +158,7 @@ class DetailsViewModel(
private fun mapChapters( private fun mapChapters(
chapters: List<MangaChapter>, chapters: List<MangaChapter>,
downloadedChapters: List<MangaChapter>?,
currentId: Long?, currentId: Long?,
newCount: Int, newCount: Int,
branch: String?, branch: String?,
@ -174,19 +167,18 @@ class DetailsViewModel(
val dateFormat = settings.dateFormat() val dateFormat = settings.dateFormat()
val currentIndex = chapters.indexOfFirst { it.id == currentId } val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount val firstNewIndex = chapters.size - newCount
val downloadedIds = downloadedChapters?.mapToSet { it.id }
for (i in chapters.indices) { for (i in chapters.indices) {
val chapter = chapters[i] val chapter = chapters[i]
if (chapter.branch != branch) { if (chapter.branch != branch) {
continue continue
} }
result += chapter.toListItem( result += chapter.toListItem(
extra = when { isCurrent = i == currentIndex,
i >= firstNewIndex -> ChapterExtra.NEW isUnread = i > currentIndex,
i == currentIndex -> ChapterExtra.CURRENT isNew = i >= firstNewIndex,
i < currentIndex -> ChapterExtra.READ
else -> ChapterExtra.UNREAD
},
isMissing = false, isMissing = false,
isDownloaded = downloadedIds?.contains(chapter.id) == true,
dateFormat = dateFormat, dateFormat = dateFormat,
) )
} }
@ -212,29 +204,32 @@ class DetailsViewModel(
} }
val localChapter = chaptersMap.remove(chapter.id) val localChapter = chaptersMap.remove(chapter.id)
result += localChapter?.toListItem( result += localChapter?.toListItem(
extra = when { isCurrent = i == currentIndex,
i >= firstNewIndex -> ChapterExtra.NEW isUnread = i > currentIndex,
i == currentIndex -> ChapterExtra.CURRENT isNew = i >= firstNewIndex,
i < currentIndex -> ChapterExtra.READ
else -> ChapterExtra.UNREAD
},
isMissing = false, isMissing = false,
isDownloaded = false,
dateFormat = dateFormat, dateFormat = dateFormat,
) ?: chapter.toListItem( ) ?: chapter.toListItem(
extra = when { isCurrent = i == currentIndex,
i >= firstNewIndex -> ChapterExtra.NEW isUnread = i > currentIndex,
i == currentIndex -> ChapterExtra.CURRENT isNew = i >= firstNewIndex,
i < currentIndex -> ChapterExtra.READ
else -> ChapterExtra.UNREAD
},
isMissing = true, isMissing = true,
isDownloaded = false,
dateFormat = dateFormat, dateFormat = dateFormat,
) )
} }
if (chaptersMap.isNotEmpty()) { // some chapters on device but not online source if (chaptersMap.isNotEmpty()) { // some chapters on device but not online source
result.ensureCapacity(result.size + chaptersMap.size) result.ensureCapacity(result.size + chaptersMap.size)
chaptersMap.values.mapTo(result) { chaptersMap.values.mapTo(result) {
it.toListItem(ChapterExtra.UNREAD, false, dateFormat) it.toListItem(
isCurrent = false,
isUnread = true,
isNew = false,
isMissing = false,
isDownloaded = false,
dateFormat = dateFormat,
)
} }
result.sortBy { it.chapter.number } result.sortBy { it.chapter.number }
} }
@ -246,14 +241,15 @@ class DetailsViewModel(
return null return null
} }
val groups = chapters.groupBy { it.branch } val groups = chapters.groupBy { it.branch }
val locale = Locale.getDefault() for (locale in LocaleListCompat.getAdjustedDefault()) {
var language = locale.displayLanguage.toTitleCase(locale) var language = locale.getDisplayLanguage(locale).toTitleCase(locale)
if (groups.containsKey(language)) { if (groups.containsKey(language)) {
return language return language
} }
language = locale.displayName.toTitleCase(locale) language = locale.getDisplayName(locale).toTitleCase(locale)
if (groups.containsKey(language)) { if (groups.containsKey(language)) {
return language return language
}
} }
return groups.maxByOrNull { it.value.size }?.key return groups.maxByOrNull { it.value.size }?.key
} }

@ -1,11 +1,17 @@
package org.koitharu.kotatsu.details.ui.adapter package org.koitharu.kotatsu.details.ui.adapter
import android.view.View
import androidx.core.view.isVisible
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemChapterBinding import org.koitharu.kotatsu.databinding.ItemChapterBinding
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.history.domain.ChapterExtra 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.utils.ext.getThemeColor import org.koitharu.kotatsu.utils.ext.getThemeColor
import org.koitharu.kotatsu.utils.ext.textAndVisible import org.koitharu.kotatsu.utils.ext.textAndVisible
@ -15,37 +21,40 @@ fun chapterListItemAD(
{ inflater, parent -> ItemChapterBinding.inflate(inflater, parent, false) } { inflater, parent -> ItemChapterBinding.inflate(inflater, parent, false) }
) { ) {
itemView.setOnClickListener { val eventListener = object : View.OnClickListener, View.OnLongClickListener {
clickListener.onItemClick(item, it) override fun onClick(v: View) = clickListener.onItemClick(item, v)
} override fun onLongClick(v: View) = clickListener.onItemLongClick(item, v)
itemView.setOnLongClickListener {
clickListener.onItemLongClick(item, it)
} }
bind { itemView.setOnClickListener(eventListener)
binding.textViewTitle.text = item.chapter.name itemView.setOnLongClickListener(eventListener)
binding.textViewNumber.text = item.chapter.number.toString()
binding.textViewDescription.textAndVisible = item.description() bind { payloads ->
when (item.extra) { if (payloads.isEmpty()) {
ChapterExtra.UNREAD -> { binding.textViewTitle.text = item.chapter.name
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.setBackgroundResource(R.drawable.bg_badge_default)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse)) binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse))
} }
ChapterExtra.READ -> { FLAG_CURRENT -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_outline)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
}
ChapterExtra.CURRENT -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_outline_accent)
binding.textViewNumber.setTextColor(context.getThemeColor(androidx.appcompat.R.attr.colorAccent))
}
ChapterExtra.NEW -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_accent) binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_accent)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse)) binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse))
} }
else -> {
binding.textViewNumber.setBackgroundResource(R.drawable.bg_badge_outline)
binding.textViewNumber.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
}
} }
binding.textViewTitle.alpha = if (item.isMissing) 0.3f else 1f val isMissing = item.hasFlag(FLAG_MISSING)
binding.textViewDescription.alpha = if (item.isMissing) 0.3f else 1f binding.textViewTitle.alpha = if (isMissing) 0.3f else 1f
binding.textViewNumber.alpha = if (item.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)
} }
} }

@ -33,8 +33,8 @@ class ChaptersAdapter(
} }
override fun getChangePayload(oldItem: ChapterListItem, newItem: ChapterListItem): Any? { override fun getChangePayload(oldItem: ChapterListItem, newItem: ChapterListItem): Any? {
if (oldItem.extra != newItem.extra && oldItem.chapter == newItem.chapter) { if (oldItem.flags != newItem.flags && oldItem.chapter == newItem.chapter) {
return newItem.extra return newItem.flags
} }
return null return null
} }

@ -4,20 +4,14 @@ import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Rect import android.graphics.Rect
import androidx.collection.ArraySet
import androidx.core.content.ContextCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.utils.ext.getThemeColor import org.koitharu.kotatsu.utils.ext.getThemeColor
import org.koitharu.kotatsu.utils.ext.resolveDp
class ChaptersSelectionDecoration(context: Context) : RecyclerView.ItemDecoration() { class ChaptersSelectionDecoration(context: Context) : RecyclerView.ItemDecoration() {
private val icon = ContextCompat.getDrawable(context, R.drawable.ic_check)
private val padding = context.resources.resolveDp(16)
private val bounds = Rect() private val bounds = Rect()
private val selection = ArraySet<Long>() private val selection = HashSet<Long>()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
init { init {
@ -54,7 +48,6 @@ class ChaptersSelectionDecoration(context: Context) : RecyclerView.ItemDecoratio
} }
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
icon ?: return
canvas.save() canvas.save()
if (parent.clipToPadding) { if (parent.clipToPadding) {
canvas.clipRect( canvas.clipRect(
@ -73,36 +66,4 @@ class ChaptersSelectionDecoration(context: Context) : RecyclerView.ItemDecoratio
} }
canvas.restore() canvas.restore()
} }
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
icon ?: return
canvas.save()
val left: Int
val right: Int
if (parent.clipToPadding) {
left = parent.paddingLeft
right = parent.width - parent.paddingRight
canvas.clipRect(
left, parent.paddingTop, right,
parent.height - parent.paddingBottom
)
} else {
left = 0
right = parent.width
}
for (child in parent.children) {
val itemId = parent.getChildItemId(child)
if (itemId in selection) {
parent.getDecoratedBoundsWithMargins(child, bounds)
bounds.offset(child.translationX.toInt(), child.translationY.toInt())
val hh = (bounds.height() - icon.intrinsicHeight) / 2
val top: Int = bounds.top + hh
val bottom: Int = bounds.bottom - hh
icon.setBounds(right - icon.intrinsicWidth - padding, top, right - padding, bottom)
icon.draw(canvas)
}
}
canvas.restore()
}
} }

@ -1,15 +1,20 @@
package org.koitharu.kotatsu.details.ui.model package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.history.domain.ChapterExtra
data class ChapterListItem( class ChapterListItem(
val chapter: MangaChapter, val chapter: MangaChapter,
val extra: ChapterExtra, val flags: Int,
val isMissing: Boolean,
val uploadDate: String?, val uploadDate: String?,
) { ) {
val status: Int
get() = flags and MASK_STATUS
fun hasFlag(flag: Int): Boolean {
return (flags and flag) == flag
}
fun description(): CharSequence? { fun description(): CharSequence? {
val scanlator = chapter.scanlator?.takeUnless { it.isBlank() } val scanlator = chapter.scanlator?.takeUnless { it.isBlank() }
return when { return when {
@ -18,4 +23,35 @@ data class ChapterListItem(
else -> uploadDate else -> uploadDate
} }
} }
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
if (uploadDate != other.uploadDate) return false
return true
}
override fun hashCode(): Int {
var result = chapter.hashCode()
result = 31 * result + flags
result = 31 * result + uploadDate.hashCode()
return result
}
companion object {
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
}
} }

@ -1,16 +1,30 @@
package org.koitharu.kotatsu.details.ui.model package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.history.domain.ChapterExtra 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 java.text.DateFormat import java.text.DateFormat
fun MangaChapter.toListItem( fun MangaChapter.toListItem(
extra: ChapterExtra, isCurrent: Boolean,
isUnread: Boolean,
isNew: Boolean,
isMissing: Boolean, isMissing: Boolean,
isDownloaded: Boolean,
dateFormat: DateFormat, dateFormat: DateFormat,
) = ChapterListItem( ): ChapterListItem {
chapter = this, var flags = 0
extra = extra, if (isCurrent) flags = flags or FLAG_CURRENT
isMissing = isMissing, if (isUnread) flags = flags or FLAG_UNREAD
uploadDate = if (uploadDate != 0L) dateFormat.format(uploadDate) else null 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,
flags = flags,
uploadDate = if (uploadDate != 0L) dateFormat.format(uploadDate) else null
)
}

@ -1,6 +0,0 @@
package org.koitharu.kotatsu.history.domain
enum class ChapterExtra {
READ, CURRENT, UNREAD, NEW
}

@ -19,7 +19,6 @@ import org.koitharu.kotatsu.databinding.DialogChaptersBinding
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.history.domain.ChapterExtra
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(), class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
@ -51,12 +50,11 @@ class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply { binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply {
setItems(chapters.mapIndexed { index, chapter -> setItems(chapters.mapIndexed { index, chapter ->
chapter.toListItem( chapter.toListItem(
when { isCurrent = index == currentPosition,
index < currentPosition -> ChapterExtra.READ isUnread = index > currentPosition,
index == currentPosition -> ChapterExtra.CURRENT isNew = false,
else -> ChapterExtra.UNREAD
},
isMissing = false, isMissing = false,
isDownloaded = false,
dateFormat = dateFormat, dateFormat = dateFormat,
) )
}) { }) {

@ -11,6 +11,12 @@ fun LocaleListCompat.toList(): List<Locale> {
return list return list
} }
operator fun LocaleListCompat.iterator() = object : Iterator<Locale> {
private var index = 0
override fun hasNext(): Boolean = index < size()
override fun next(): Locale = get(index++)
}
inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo( inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo(
destination: C, destination: C,
block: (Locale) -> R, block: (Locale) -> R,

@ -8,7 +8,7 @@ class JSONIterator(private val array: JSONArray) : Iterator<JSONObject> {
private val total = array.length() private val total = array.length()
private var index = 0 private var index = 0
override fun hasNext() = index < total - 1 override fun hasNext() = index < total
override fun next(): JSONObject = array.getJSONObject(index++) override fun next(): JSONObject = array.getJSONObject(index++)
} }

@ -7,7 +7,7 @@ class JSONStringIterator(private val array: JSONArray) : Iterator<String> {
private val total = array.length() private val total = array.length()
private var index = 0 private var index = 0
override fun hasNext() = index < total - 1 override fun hasNext() = index < total
override fun next(): String = array.getString(index++) override fun next(): String = array.getString(index++)
} }

@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7.25,12.5L4.75,9L3.5,9v6h1.25v-3.5L7.3,15h1.2L8.5,9L7.25,9zM9.5,15h4v-1.25L11,13.75v-1.11h2.5v-1.26L11,11.38v-1.12h2.5L13.5,9h-4zM19.25,9v4.5h-1.12L18.13,9.99h-1.25v3.52h-1.13L15.75,9L14.5,9v5c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1L20.5,9h-1.25z" />
</vector>

@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M14 12.8C13.5 12.31 12.78 12 12 12C10.34 12 9 13.34 9 15C9 16.31 9.84 17.41 11 17.82C11.07 15.67 12.27 13.8 14 12.8M11.09 19H5V5H16.17L19 7.83V12.35C19.75 12.61 20.42 13 21 13.54V7L17 3H5C3.89 3 3 3.9 3 5V19C3 20.1 3.89 21 5 21H11.81C11.46 20.39 11.21 19.72 11.09 19M6 10H15V6H6V10M15.75 21L13 18L14.16 16.84L15.75 18.43L19.34 14.84L20.5 16.25L15.75 21" />
</vector>

@ -1,61 +1,66 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/chapter_list_item_height" android:layout_height="@dimen/chapter_list_item_height"
android:background="?selectableItemBackground"> android:background="?selectableItemBackground"
android:baselineAligned="false"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView <TextView
android:id="@+id/textView_number" android:id="@+id/textView_number"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:background="@drawable/bg_badge_default" android:background="@drawable/bg_badge_default"
android:gravity="center" android:gravity="center"
android:minWidth="26dp" android:minWidth="26dp"
android:textAlignment="center" android:textAlignment="center"
android:textColor="?attr/colorOnTertiary" tools:text="13"
app:layout_constraintBottom_toBottomOf="parent" tools:textColor="?attr/colorOnPrimary" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="13" />
<TextView <LinearLayout
android:id="@+id/textView_title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_marginVertical="8dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_toEndOf="@id/textView_number" android:layout_weight="1"
android:ellipsize="end" android:orientation="vertical">
android:maxLines="1"
android:textAppearance="?attr/textAppearanceBodyMedium"
app:layout_constraintBottom_toTopOf="@+id/textView_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView_number"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginBottom="8dp"
tools:text="@tools:sample/lorem[15]" />
<TextView <TextView
android:id="@+id/textView_description" android:id="@+id/textView_title"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodyMedium"
tools:text="@tools:sample/lorem[15]" />
<TextView
android:id="@+id/textView_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodySmall"
tools:text="05.10.2021 • Scanlator" />
</LinearLayout>
<ImageView
android:id="@+id/imageView_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_new" />
<ImageView
android:id="@+id/imageView_downloaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_marginBottom="8dp" android:src="@drawable/ic_save_ok" />
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodySmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView_number"
app:layout_constraintTop_toBottomOf="@+id/textView_title"
tools:text="05.10.2021 • Scanlator" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>
Loading…
Cancel
Save