Update history repository
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.model
|
||||
|
||||
data class MangaInfo <E>(
|
||||
val manga: Manga,
|
||||
val extra: E
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.koitharu.kotatsu.domain
|
||||
|
||||
enum class ChapterExtra {
|
||||
|
||||
READ, CURRENT, UNREAD, NEW
|
||||
}
|
||||
@@ -11,44 +11,15 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import java.io.Closeable
|
||||
import java.util.*
|
||||
|
||||
class HistoryRepository() : KoinComponent,
|
||||
MangaRepository, Closeable {
|
||||
class HistoryRepository() : KoinComponent {
|
||||
|
||||
private val db: MangaDatabase by inject()
|
||||
|
||||
override val sortOrders: Set<SortOrder> = setOf(SortOrder.NEWEST, SortOrder.POPULARITY)
|
||||
|
||||
override val isSearchAvailable = false
|
||||
|
||||
override suspend fun getList(
|
||||
offset: Int,
|
||||
query: String?,
|
||||
sortOrder: SortOrder?,
|
||||
tag: MangaTag?
|
||||
): List<Manga> = getHistory(offset, query, sortOrder, tag).map { x -> x.manga }
|
||||
|
||||
suspend fun getHistory(
|
||||
offset: Int,
|
||||
query: String? = null,
|
||||
sortOrder: SortOrder? = null,
|
||||
tag: MangaTag? = null
|
||||
): List<MangaInfo<MangaHistory>> {
|
||||
suspend fun getList(offset: Int) : List<Manga> {
|
||||
val entities = db.historyDao().getAll(offset, 20, "updated_by")
|
||||
return entities.map { x -> MangaInfo(x.manga.toManga(), x.history.toMangaHistory()) }
|
||||
return entities.map { it.manga.toManga() }
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga {
|
||||
throw UnsupportedOperationException("History repository does not support getDetails() method")
|
||||
}
|
||||
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
throw UnsupportedOperationException("History repository does not support getPages() method")
|
||||
}
|
||||
|
||||
override suspend fun getPageFullUrl(page: MangaPage) = page.url
|
||||
|
||||
override suspend fun getTags() = emptySet<MangaTag>()
|
||||
|
||||
suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int) {
|
||||
val dao = db.historyDao()
|
||||
val entity = HistoryEntity(
|
||||
@@ -66,7 +37,7 @@ class HistoryRepository() : KoinComponent,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getHistory(manga: Manga): MangaHistory? {
|
||||
suspend fun getOne(manga: Manga): MangaHistory? {
|
||||
return db.historyDao().getOneOrNull(manga.id)?.let {
|
||||
MangaHistory(
|
||||
createdAt = Date(it.createdAt),
|
||||
@@ -80,8 +51,4 @@ class HistoryRepository() : KoinComponent,
|
||||
suspend fun clear() {
|
||||
db.historyDao().clear()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
db.close()
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koitharu.kotatsu.utils.ext.replaceWith
|
||||
|
||||
abstract class BaseRecyclerAdapter<T>(private val onItemClickListener: OnRecyclerItemClickListener<T>? = null) :
|
||||
RecyclerView.Adapter<BaseViewHolder<T>>(),
|
||||
abstract class BaseRecyclerAdapter<T, E>(private val onItemClickListener: OnRecyclerItemClickListener<T>? = null) :
|
||||
RecyclerView.Adapter<BaseViewHolder<T, E>>(),
|
||||
KoinComponent {
|
||||
|
||||
private val dataSet = ArrayList<T>()
|
||||
@@ -16,8 +16,9 @@ abstract class BaseRecyclerAdapter<T>(private val onItemClickListener: OnRecycle
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BaseViewHolder<T>, position: Int) {
|
||||
holder.bind(dataSet[position])
|
||||
override fun onBindViewHolder(holder: BaseViewHolder<T, E>, position: Int) {
|
||||
val item = dataSet[position]
|
||||
holder.bind(item, getExtra(item, position))
|
||||
}
|
||||
|
||||
fun getItem(position: Int) = dataSet[position]
|
||||
@@ -64,13 +65,16 @@ abstract class BaseRecyclerAdapter<T>(private val onItemClickListener: OnRecycle
|
||||
|
||||
final override fun getItemCount() = dataSet.size
|
||||
|
||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<T> {
|
||||
return onCreateViewHolder(parent).setOnItemClickListener(onItemClickListener).also(this::onViewHolderCreated)
|
||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<T, E> {
|
||||
return onCreateViewHolder(parent).setOnItemClickListener(onItemClickListener)
|
||||
.also(this::onViewHolderCreated)
|
||||
}
|
||||
|
||||
protected open fun onViewHolderCreated(holder: BaseViewHolder<T>) = Unit
|
||||
protected abstract fun getExtra(item: T, position: Int): E
|
||||
|
||||
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<T>
|
||||
protected open fun onViewHolderCreated(holder: BaseViewHolder<T, E>) = Unit
|
||||
|
||||
protected abstract fun onCreateViewHolder(parent: ViewGroup): BaseViewHolder<T, E>
|
||||
|
||||
protected abstract fun onGetItemId(item: T): Long
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import kotlinx.android.extensions.LayoutContainer
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koitharu.kotatsu.utils.ext.inflate
|
||||
|
||||
abstract class BaseViewHolder<T> protected constructor(view: View) :
|
||||
abstract class BaseViewHolder<T, E> protected constructor(view: View) :
|
||||
RecyclerView.ViewHolder(view), LayoutContainer, KoinComponent {
|
||||
|
||||
constructor(parent: ViewGroup, @LayoutRes resId: Int) : this(parent.inflate(resId))
|
||||
@@ -21,14 +21,14 @@ abstract class BaseViewHolder<T> protected constructor(view: View) :
|
||||
|
||||
val context get() = itemView.context!!
|
||||
|
||||
fun bind(data: T) {
|
||||
fun bind(data: T, extra: E) {
|
||||
boundData = data
|
||||
onBind(data)
|
||||
onBind(data, extra)
|
||||
}
|
||||
|
||||
fun requireData() = boundData ?: throw IllegalStateException("Calling requireData() before bind()")
|
||||
|
||||
fun setOnItemClickListener(listener: OnRecyclerItemClickListener<T>?): BaseViewHolder<T> {
|
||||
fun setOnItemClickListener(listener: OnRecyclerItemClickListener<T>?): BaseViewHolder<T, E> {
|
||||
if (listener != null) {
|
||||
itemView.setOnClickListener {
|
||||
listener.onItemClick(boundData ?: return@setOnClickListener, adapterPosition, it)
|
||||
@@ -40,5 +40,5 @@ abstract class BaseViewHolder<T> protected constructor(view: View) :
|
||||
return this
|
||||
}
|
||||
|
||||
abstract fun onBind(data: T)
|
||||
abstract fun onBind(data: T, extra: E)
|
||||
}
|
||||
@@ -4,12 +4,33 @@ import android.view.ViewGroup
|
||||
import kotlinx.android.synthetic.main.item_chapter.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.domain.ChapterExtra
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.utils.ext.getThemeColor
|
||||
|
||||
class ChapterHolder(parent: ViewGroup) : BaseViewHolder<MangaChapter>(parent, R.layout.item_chapter) {
|
||||
class ChapterHolder(parent: ViewGroup) :
|
||||
BaseViewHolder<MangaChapter, ChapterExtra>(parent, R.layout.item_chapter) {
|
||||
|
||||
override fun onBind(data: MangaChapter) {
|
||||
override fun onBind(data: MangaChapter, extra: ChapterExtra) {
|
||||
textView_title.text = data.name
|
||||
textView_number.text = data.number.toString()
|
||||
when (extra) {
|
||||
ChapterExtra.UNREAD -> {
|
||||
textView_number.setBackgroundResource(R.drawable.bg_badge_default)
|
||||
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorSecondaryInverse))
|
||||
}
|
||||
ChapterExtra.READ -> {
|
||||
textView_number.setBackgroundResource(R.drawable.bg_badge_outline)
|
||||
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorTertiary))
|
||||
}
|
||||
ChapterExtra.CURRENT -> {
|
||||
textView_number.setBackgroundResource(R.drawable.bg_badge_outline_accent)
|
||||
textView_number.setTextColor(context.getThemeColor(R.attr.colorAccent))
|
||||
}
|
||||
ChapterExtra.NEW -> {
|
||||
textView_number.setBackgroundResource(R.drawable.bg_badge_accent)
|
||||
textView_number.setTextColor(context.getThemeColor(android.R.attr.textColorPrimaryInverse))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,32 @@
|
||||
package org.koitharu.kotatsu.ui.details
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.domain.ChapterExtra
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||
|
||||
class ChaptersAdapter(onItemClickListener: OnRecyclerItemClickListener<MangaChapter>) :
|
||||
BaseRecyclerAdapter<MangaChapter>(onItemClickListener) {
|
||||
BaseRecyclerAdapter<MangaChapter, ChapterExtra>(onItemClickListener) {
|
||||
|
||||
var currentChapterPosition = RecyclerView.NO_POSITION
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = ChapterHolder(parent)
|
||||
|
||||
override fun onGetItemId(item: MangaChapter) = item.id
|
||||
|
||||
override fun getExtra(item: MangaChapter, position: Int): ChapterExtra = when {
|
||||
currentChapterPosition == RecyclerView.NO_POSITION -> ChapterExtra.UNREAD
|
||||
currentChapterPosition == position -> ChapterExtra.CURRENT
|
||||
currentChapterPosition < position -> ChapterExtra.UNREAD
|
||||
currentChapterPosition > position -> ChapterExtra.READ
|
||||
else -> ChapterExtra.UNREAD
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.fragment_chapters.*
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderActivity
|
||||
@@ -21,7 +21,7 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
|
||||
@Suppress("unused")
|
||||
private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter }
|
||||
|
||||
private var data: MangaInfo<MangaHistory?>? = null
|
||||
private var manga: Manga? = null
|
||||
|
||||
private lateinit var adapter: ChaptersAdapter
|
||||
|
||||
@@ -37,9 +37,9 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
|
||||
recyclerView_chapters.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
|
||||
this.data = data
|
||||
adapter.replaceData(data.manga.chapters.orEmpty())
|
||||
override fun onMangaUpdated(manga: Manga) {
|
||||
this.manga = manga
|
||||
adapter.replaceData(manga.chapters.orEmpty())
|
||||
}
|
||||
|
||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
@@ -50,11 +50,17 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
|
||||
|
||||
}
|
||||
|
||||
override fun onHistoryChanged(history: MangaHistory?) {
|
||||
adapter.currentChapterPosition = history?.let {
|
||||
manga?.chapters?.indexOfFirst { x -> x.id == it.chapterId }
|
||||
} ?: RecyclerView.NO_POSITION
|
||||
}
|
||||
|
||||
override fun onItemClick(item: MangaChapter, position: Int, view: View) {
|
||||
startActivity(
|
||||
ReaderActivity.newIntent(
|
||||
context ?: return,
|
||||
data?.manga ?: return,
|
||||
manga ?: return,
|
||||
item.id
|
||||
)
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.ui.common.BaseActivity
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
@@ -25,13 +24,23 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
|
||||
tabs.setupWithViewPager(pager)
|
||||
intent?.getParcelableExtra<Manga>(EXTRA_MANGA)?.let {
|
||||
presenter.loadDetails(it)
|
||||
presenter.loadHistory(it)
|
||||
} ?: finish()
|
||||
}
|
||||
|
||||
override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
|
||||
title = data.manga.title
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
intent?.getParcelableExtra<Manga>(EXTRA_MANGA)?.let {
|
||||
presenter.loadHistory(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMangaUpdated(manga: Manga) {
|
||||
title = manga.title
|
||||
}
|
||||
|
||||
override fun onHistoryChanged(history: MangaHistory?) = Unit
|
||||
|
||||
override fun onLoadingStateChanged(isLoading: Boolean) = Unit
|
||||
|
||||
override fun onError(e: Exception) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class MangaDetailsAdapter(private val resources: Resources, fm: FragmentManager) : FragmentPagerAdapter(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
class MangaDetailsAdapter(private val resources: Resources, fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
|
||||
override fun getCount() = 2
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderActivity
|
||||
import org.koitharu.kotatsu.utils.ext.setChips
|
||||
@@ -19,46 +18,34 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
|
||||
@Suppress("unused")
|
||||
private val presenter by moxyPresenter { (activity as MangaDetailsActivity).presenter }
|
||||
|
||||
override fun onMangaUpdated(data: MangaInfo<MangaHistory?>) {
|
||||
imageView_cover.load(data.manga.largeCoverUrl ?: data.manga.coverUrl)
|
||||
textView_title.text = data.manga.title
|
||||
textView_subtitle.text = data.manga.localizedTitle
|
||||
textView_description.text = data.manga.description?.parseAsHtml()
|
||||
if (data.manga.rating == Manga.NO_RATING) {
|
||||
private var manga: Manga? = null
|
||||
private var history: MangaHistory? = null
|
||||
|
||||
override fun onMangaUpdated(manga: Manga) {
|
||||
this.manga = manga
|
||||
imageView_cover.load(manga.largeCoverUrl ?: manga.coverUrl)
|
||||
textView_title.text = manga.title
|
||||
textView_subtitle.text = manga.localizedTitle
|
||||
textView_description.text = manga.description?.parseAsHtml()
|
||||
if (manga.rating == Manga.NO_RATING) {
|
||||
ratingBar.isVisible = false
|
||||
} else {
|
||||
ratingBar.progress = (ratingBar.max * data.manga.rating).roundToInt()
|
||||
ratingBar.progress = (ratingBar.max * manga.rating).roundToInt()
|
||||
ratingBar.isVisible = true
|
||||
}
|
||||
chips_tags.setChips(data.manga.tags) {
|
||||
chips_tags.setChips(manga.tags) {
|
||||
create(
|
||||
text = it.title,
|
||||
iconRes = R.drawable.ic_chip_tag,
|
||||
tag = it
|
||||
)
|
||||
}
|
||||
if (data.manga.chapters.isNullOrEmpty()) {
|
||||
button_read.isEnabled = false
|
||||
} else {
|
||||
button_read.isEnabled = true
|
||||
if (data.extra == null) {
|
||||
button_read.setText(R.string.read)
|
||||
button_read.setIconResource(R.drawable.ic_read)
|
||||
} else {
|
||||
button_read.setText(R.string.continue_)
|
||||
button_read.setIconResource(R.drawable.ic_play)
|
||||
}
|
||||
val chapterId = data.extra?.chapterId ?: data.manga.chapters.first().id
|
||||
button_read.setOnClickListener {
|
||||
startActivity(
|
||||
ReaderActivity.newIntent(
|
||||
context ?: return@setOnClickListener,
|
||||
data.manga,
|
||||
chapterId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
updateReadButton()
|
||||
}
|
||||
|
||||
override fun onHistoryChanged(history: MangaHistory?) {
|
||||
this.history = history
|
||||
updateReadButton()
|
||||
}
|
||||
|
||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
@@ -68,4 +55,28 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
|
||||
override fun onError(e: Exception) {
|
||||
|
||||
}
|
||||
|
||||
private fun updateReadButton() {
|
||||
if (manga?.chapters.isNullOrEmpty()) {
|
||||
button_read.isEnabled = false
|
||||
} else {
|
||||
button_read.isEnabled = true
|
||||
if (history == null) {
|
||||
button_read.setText(R.string.read)
|
||||
button_read.setIconResource(R.drawable.ic_read)
|
||||
} else {
|
||||
button_read.setText(R.string.continue_)
|
||||
button_read.setIconResource(R.drawable.ic_play)
|
||||
}
|
||||
button_read.setOnClickListener {
|
||||
startActivity(
|
||||
ReaderActivity.newIntent(
|
||||
context ?: return@setOnClickListener,
|
||||
manga ?: return@setOnClickListener,
|
||||
history
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
package org.koitharu.kotatsu.ui.details
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import moxy.InjectViewState
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||
import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||
@@ -15,24 +13,25 @@ import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||
@InjectViewState
|
||||
class MangaDetailsPresenter : BasePresenter<MangaDetailsView>() {
|
||||
|
||||
private lateinit var historyRepository: HistoryRepository
|
||||
|
||||
private var isLoaded = false
|
||||
|
||||
fun loadDetails(manga: Manga) {
|
||||
if (isLoaded) {
|
||||
override fun onFirstViewAttach() {
|
||||
historyRepository = HistoryRepository()
|
||||
super.onFirstViewAttach()
|
||||
}
|
||||
|
||||
fun loadDetails(manga: Manga, force: Boolean = false) {
|
||||
if (!force && isLoaded) {
|
||||
return
|
||||
}
|
||||
viewState.onMangaUpdated(MangaInfo(manga, null))
|
||||
viewState.onMangaUpdated(manga)
|
||||
launch {
|
||||
try {
|
||||
viewState.onLoadingStateChanged(true)
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
val details = async {
|
||||
MangaProviderFactory.create(manga.source).getDetails(manga)
|
||||
}
|
||||
val history = async {
|
||||
HistoryRepository().use { it.getHistory(manga) }
|
||||
}
|
||||
MangaInfo(details.await(), history.await())
|
||||
MangaProviderFactory.create(manga.source).getDetails(manga)
|
||||
}
|
||||
viewState.onMangaUpdated(data)
|
||||
isLoaded = true
|
||||
@@ -46,4 +45,19 @@ class MangaDetailsPresenter : BasePresenter<MangaDetailsView>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadHistory(manga: Manga) {
|
||||
launch {
|
||||
try {
|
||||
val history = withContext(Dispatchers.IO) {
|
||||
historyRepository.getOne(manga)
|
||||
}
|
||||
viewState.onHistoryChanged(history)
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,20 @@ import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.OneExecutionStateStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
|
||||
interface MangaDetailsView : MvpView {
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onMangaUpdated(data: MangaInfo<MangaHistory?>)
|
||||
fun onMangaUpdated(manga: Manga)
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onLoadingStateChanged(isLoading: Boolean)
|
||||
|
||||
@StateStrategyType(OneExecutionStateStrategy::class)
|
||||
fun onError(e: Exception)
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onHistoryChanged(history: MangaHistory?)
|
||||
}
|
||||
@@ -5,17 +5,18 @@ import coil.api.load
|
||||
import coil.request.RequestDisposable
|
||||
import kotlinx.android.synthetic.main.item_manga_grid.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
|
||||
class MangaGridHolder<E>(parent: ViewGroup) : BaseViewHolder<MangaInfo<E>>(parent, R.layout.item_manga_grid) {
|
||||
class MangaGridHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_grid) {
|
||||
|
||||
private var coverRequest: RequestDisposable? = null
|
||||
|
||||
override fun onBind(data: MangaInfo<E>) {
|
||||
override fun onBind(data: Manga, extra: MangaHistory?) {
|
||||
coverRequest?.dispose()
|
||||
textView_title.text = data.manga.title
|
||||
coverRequest = imageView_cover.load(data.manga.coverUrl) {
|
||||
textView_title.text = data.title
|
||||
coverRequest = imageView_cover.load(data.coverUrl) {
|
||||
crossfade(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
package org.koitharu.kotatsu.ui.main.list
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||
|
||||
class MangaListAdapter<E>(onItemClickListener: OnRecyclerItemClickListener<MangaInfo<E>>) :
|
||||
BaseRecyclerAdapter<MangaInfo<E>>(onItemClickListener) {
|
||||
class MangaListAdapter(onItemClickListener: OnRecyclerItemClickListener<Manga>) :
|
||||
BaseRecyclerAdapter<Manga, MangaHistory?>(onItemClickListener) {
|
||||
|
||||
var listMode: ListMode = ListMode.LIST
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = when(listMode) {
|
||||
ListMode.LIST -> MangaListHolder<E>(parent)
|
||||
ListMode.DETAILED_LIST -> MangaListDetailsHolder<E>(parent)
|
||||
ListMode.LIST -> MangaListHolder(parent)
|
||||
ListMode.DETAILED_LIST -> MangaListDetailsHolder(parent)
|
||||
ListMode.GRID -> MangaGridHolder(parent)
|
||||
}
|
||||
|
||||
override fun onGetItemId(item: MangaInfo<E>) = item.manga.id
|
||||
override fun onGetItemId(item: Manga) = item.id
|
||||
|
||||
override fun getExtra(item: Manga, position: Int): MangaHistory? = null
|
||||
}
|
||||
@@ -8,30 +8,30 @@ import coil.request.RequestDisposable
|
||||
import kotlinx.android.synthetic.main.item_manga_list_details.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MangaListDetailsHolder<E>(parent: ViewGroup) : BaseViewHolder<MangaInfo<E>>(parent, R.layout.item_manga_list_details) {
|
||||
class MangaListDetailsHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list_details) {
|
||||
|
||||
private var coverRequest: RequestDisposable? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBind(data: MangaInfo<E>) {
|
||||
override fun onBind(data: Manga, extra: MangaHistory?) {
|
||||
coverRequest?.dispose()
|
||||
textView_title.text = data.manga.title
|
||||
textView_subtitle.textAndVisible = data.manga.localizedTitle
|
||||
coverRequest = imageView_cover.load(data.manga.coverUrl) {
|
||||
textView_title.text = data.title
|
||||
textView_subtitle.textAndVisible = data.localizedTitle
|
||||
coverRequest = imageView_cover.load(data.coverUrl) {
|
||||
crossfade(true)
|
||||
}
|
||||
if(data.manga.rating == Manga.NO_RATING) {
|
||||
if(data.rating == Manga.NO_RATING) {
|
||||
textView_rating.isVisible = false
|
||||
} else {
|
||||
textView_rating.text = "${(data.manga.rating * 10).roundToInt()}/10"
|
||||
textView_rating.text = "${(data.rating * 10).roundToInt()}/10"
|
||||
textView_rating.isVisible = true
|
||||
}
|
||||
textView_tags.text = data.manga.tags.joinToString(", ") {
|
||||
textView_tags.text = data.tags.joinToString(", ") {
|
||||
it.title
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_list.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.ui.common.BaseFragment
|
||||
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||
@@ -27,9 +27,9 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.hasItems
|
||||
|
||||
abstract class MangaListFragment <E> : BaseFragment(R.layout.fragment_list), MangaListView<E>,
|
||||
PaginationScrollListener.Callback, OnRecyclerItemClickListener<MangaInfo<E>> {
|
||||
PaginationScrollListener.Callback, OnRecyclerItemClickListener<Manga> {
|
||||
|
||||
private lateinit var adapter: MangaListAdapter<E>
|
||||
private lateinit var adapter: MangaListAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -73,16 +73,16 @@ abstract class MangaListFragment <E> : BaseFragment(R.layout.fragment_list), Man
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onItemClick(item: MangaInfo<E>, position: Int, view: View) {
|
||||
startActivity(MangaDetailsActivity.newIntent(context ?: return, item.manga))
|
||||
override fun onItemClick(item: Manga, position: Int, view: View) {
|
||||
startActivity(MangaDetailsActivity.newIntent(context ?: return, item))
|
||||
}
|
||||
|
||||
override fun onListChanged(list: List<MangaInfo<E>>) {
|
||||
override fun onListChanged(list: List<Manga>) {
|
||||
adapter.replaceData(list)
|
||||
layout_holder.isVisible = list.isEmpty()
|
||||
}
|
||||
|
||||
override fun onListAppended(list: List<MangaInfo<E>>) {
|
||||
override fun onListAppended(list: List<Manga>) {
|
||||
adapter.appendData(list)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,20 @@ import coil.api.load
|
||||
import coil.request.RequestDisposable
|
||||
import kotlinx.android.synthetic.main.item_manga_list.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||
|
||||
class MangaListHolder<E>(parent: ViewGroup) : BaseViewHolder<MangaInfo<E>>(parent, R.layout.item_manga_list) {
|
||||
class MangaListHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list) {
|
||||
|
||||
private var coverRequest: RequestDisposable? = null
|
||||
|
||||
override fun onBind(data: MangaInfo<E>) {
|
||||
override fun onBind(data: Manga, extra: MangaHistory?) {
|
||||
coverRequest?.dispose()
|
||||
textView_title.text = data.manga.title
|
||||
textView_subtitle.textAndVisible = data.manga.localizedTitle
|
||||
coverRequest = imageView_cover.load(data.manga.coverUrl) {
|
||||
textView_title.text = data.title
|
||||
textView_subtitle.textAndVisible = data.localizedTitle
|
||||
coverRequest = imageView_cover.load(data.coverUrl) {
|
||||
crossfade(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@ package org.koitharu.kotatsu.ui.main.list
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.*
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
|
||||
interface MangaListView<E> : MvpView {
|
||||
|
||||
@StateStrategyType(AddToEndSingleTagStrategy::class, tag = "content")
|
||||
fun onListChanged(list: List<MangaInfo<E>>)
|
||||
fun onListChanged(list: List<Manga>)
|
||||
|
||||
@StateStrategyType(AddToEndStrategy::class, tag = "content")
|
||||
fun onListAppended(list: List<MangaInfo<E>>)
|
||||
fun onListAppended(list: List<Manga>)
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onLoadingChanged(isLoading: Boolean)
|
||||
|
||||
@@ -4,7 +4,6 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import moxy.InjectViewState
|
||||
import okhttp3.internal.closeQuietly
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.domain.HistoryRepository
|
||||
@@ -26,7 +25,7 @@ class HistoryListPresenter : BasePresenter<MangaListView<MangaHistory>>() {
|
||||
viewState.onLoadingChanged(true)
|
||||
try {
|
||||
val list = withContext(Dispatchers.IO) {
|
||||
repository.getHistory(offset = offset)
|
||||
repository.getList(offset = offset)
|
||||
}
|
||||
if (offset == 0) {
|
||||
viewState.onListChanged(list)
|
||||
@@ -62,9 +61,4 @@ class HistoryListPresenter : BasePresenter<MangaListView<MangaHistory>>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
repository.closeQuietly()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import moxy.InjectViewState
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.MangaInfo
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||
import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||
@@ -21,7 +20,6 @@ class RemoteListPresenter : BasePresenter<MangaListView<Unit>>() {
|
||||
val list = withContext(Dispatchers.IO) {
|
||||
MangaProviderFactory.create(source)
|
||||
.getList(offset)
|
||||
.map { MangaInfo(it, Unit) }
|
||||
}
|
||||
if (offset == 0) {
|
||||
viewState.onListChanged(list)
|
||||
|
||||
@@ -11,17 +11,17 @@ import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
class PageHolder(parent: ViewGroup, private val loader: PageLoader) : BaseViewHolder<MangaPage>(parent, R.layout.item_page),
|
||||
class PageHolder(parent: ViewGroup, private val loader: PageLoader) : BaseViewHolder<MangaPage, Unit>(parent, R.layout.item_page),
|
||||
SubsamplingScaleImageView.OnImageEventListener {
|
||||
|
||||
init {
|
||||
ssiv.setOnImageEventListener(this)
|
||||
button_retry.setOnClickListener {
|
||||
onBind(boundData ?: return@setOnClickListener)
|
||||
onBind(boundData ?: return@setOnClickListener, Unit)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(data: MangaPage) {
|
||||
override fun onBind(data: MangaPage, extra: Unit) {
|
||||
layout_error.isVisible = false
|
||||
progressBar.show()
|
||||
ssiv.recycle()
|
||||
|
||||
@@ -5,9 +5,11 @@ import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
|
||||
class PagesAdapter(private val loader: PageLoader) : BaseRecyclerAdapter<MangaPage>() {
|
||||
class PagesAdapter(private val loader: PageLoader) : BaseRecyclerAdapter<MangaPage, Unit>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) = PageHolder(parent, loader)
|
||||
|
||||
override fun onGetItemId(item: MangaPage) = item.id
|
||||
|
||||
override fun getExtra(item: MangaPage, position: Int) = Unit
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.ui.common.BaseActivity
|
||||
import org.koitharu.kotatsu.utils.ext.showDialog
|
||||
@@ -18,12 +20,7 @@ class ReaderActivity : BaseActivity(), ReaderView {
|
||||
|
||||
private val presenter by moxyPresenter { ReaderPresenter() }
|
||||
|
||||
private val manga by lazy(LazyThreadSafetyMode.NONE) {
|
||||
intent.getParcelableExtra<Manga>(EXTRA_MANGA)!!
|
||||
}
|
||||
private val chapterId by lazy(LazyThreadSafetyMode.NONE) {
|
||||
intent.getLongExtra(EXTRA_CHAPTER_ID, 0L)
|
||||
}
|
||||
private lateinit var state: ReaderState
|
||||
|
||||
private lateinit var loader: PageLoader
|
||||
private lateinit var adapter: PagesAdapter
|
||||
@@ -34,21 +31,24 @@ class ReaderActivity : BaseActivity(), ReaderView {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
bottomBar.inflateMenu(R.menu.opt_reader_bottom)
|
||||
|
||||
val chapter = manga.chapters?.find { x -> x.id == chapterId }
|
||||
if (chapter == null) {
|
||||
// TODO
|
||||
finish()
|
||||
return
|
||||
}
|
||||
title = chapter.name
|
||||
manga.chapters?.run {
|
||||
supportActionBar?.subtitle = getString(R.string.chapter_d_of_d, chapter.number, size)
|
||||
state = savedInstanceState?.getParcelable<ReaderState>(EXTRA_STATE)
|
||||
?: intent.getParcelableExtra<ReaderState>(EXTRA_STATE)
|
||||
?: let {
|
||||
Toast.makeText(this, R.string.error_occurred, Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
title = state.chapter?.name ?: state.manga.title
|
||||
state.manga.chapters?.run {
|
||||
supportActionBar?.subtitle =
|
||||
getString(R.string.chapter_d_of_d, state.chapter?.number ?: 0, size)
|
||||
}
|
||||
|
||||
loader = PageLoader(this)
|
||||
adapter = PagesAdapter(loader)
|
||||
pager.adapter = adapter
|
||||
presenter.loadChapter(chapter)
|
||||
presenter.loadChapter(state)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -57,7 +57,8 @@ class ReaderActivity : BaseActivity(), ReaderView {
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
presenter.addToHistory(manga, chapterId, pager.currentItem)
|
||||
state = state.copy(page = pager.currentItem)
|
||||
presenter.saveState(state)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
@@ -66,16 +67,17 @@ class ReaderActivity : BaseActivity(), ReaderView {
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when(item.itemId) {
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_chapters -> {
|
||||
ChaptersDialog.show(supportFragmentManager, manga.chapters.orEmpty())
|
||||
ChaptersDialog.show(supportFragmentManager, state.manga.chapters.orEmpty())
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onPagesReady(pages: List<MangaPage>) {
|
||||
override fun onPagesReady(pages: List<MangaPage>, index: Int) {
|
||||
adapter.replaceData(pages)
|
||||
pager.setCurrentItem(index, false)
|
||||
}
|
||||
|
||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
@@ -92,11 +94,32 @@ class ReaderActivity : BaseActivity(), ReaderView {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_MANGA = "manga"
|
||||
private const val EXTRA_CHAPTER_ID = "chapter_id"
|
||||
private const val EXTRA_STATE = "state"
|
||||
|
||||
fun newIntent(context: Context, manga: Manga, chapterId: Long) = Intent(context, ReaderActivity::class.java)
|
||||
.putExtra(EXTRA_MANGA, manga)
|
||||
.putExtra(EXTRA_CHAPTER_ID, chapterId)
|
||||
fun newIntent(context: Context, state: ReaderState) =
|
||||
Intent(context, ReaderActivity::class.java)
|
||||
.putExtra(EXTRA_STATE, state)
|
||||
|
||||
fun newIntent(context: Context, manga: Manga, chapterId: Long = -1) = newIntent(
|
||||
context, ReaderState(
|
||||
manga = manga,
|
||||
chapterId = if (chapterId == -1L) manga.chapters?.firstOrNull()?.id
|
||||
?: -1 else chapterId,
|
||||
page = 0
|
||||
)
|
||||
)
|
||||
|
||||
fun newIntent(context: Context, manga: Manga, history: MangaHistory?) =
|
||||
if (history == null) {
|
||||
newIntent(context, manga)
|
||||
} else {
|
||||
newIntent(
|
||||
context, ReaderState(
|
||||
manga = manga,
|
||||
chapterId = history.chapterId,
|
||||
page = history.page
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,18 @@ import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||
@InjectViewState
|
||||
class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
|
||||
fun loadChapter(chapter: MangaChapter) {
|
||||
fun loadChapter(state: ReaderState) {
|
||||
launch {
|
||||
viewState.onLoadingStateChanged(isLoading = true)
|
||||
try {
|
||||
val pages = withContext(Dispatchers.IO) {
|
||||
MangaProviderFactory.create(chapter.source).getPages(chapter)
|
||||
val repo = MangaProviderFactory.create(state.manga.source)
|
||||
val chapter = state.chapter ?: repo.getDetails(state.manga).chapters
|
||||
?.first { it.id == state.chapterId }
|
||||
?: throw RuntimeException("Chapter ${state.chapterId} not found")
|
||||
repo.getPages(chapter)
|
||||
}
|
||||
viewState.onPagesReady(pages)
|
||||
viewState.onPagesReady(pages, state.page)
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace()
|
||||
@@ -33,11 +37,13 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
}
|
||||
}
|
||||
|
||||
fun addToHistory(manga: Manga, chapterId: Long, page: Int) {
|
||||
fun saveState(state: ReaderState) {
|
||||
launch(Dispatchers.IO) {
|
||||
HistoryRepository().use {
|
||||
it.addOrUpdate(manga, chapterId, page)
|
||||
}
|
||||
HistoryRepository().addOrUpdate(
|
||||
manga = state.manga,
|
||||
chapterId = state.chapterId,
|
||||
page = state.page
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.koitharu.kotatsu.ui.reader
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.IgnoredOnParcel
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
||||
|
||||
@Parcelize
|
||||
data class ReaderState(
|
||||
val manga: Manga,
|
||||
val chapterId: Long,
|
||||
val page: Int
|
||||
) : Parcelable {
|
||||
|
||||
@IgnoredOnParcel
|
||||
val chapter: MangaChapter? by lazy {
|
||||
manga.chapters?.find { it.id == chapterId }
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import org.koitharu.kotatsu.core.model.MangaPage
|
||||
interface ReaderView : MvpView {
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onPagesReady(pages: List<MangaPage>)
|
||||
fun onPagesReady(pages: List<MangaPage>, index: Int)
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
fun onLoadingStateChanged(isLoading: Boolean)
|
||||
|
||||
11
app/src/main/res/drawable/bg_badge_accent.xml
Normal file
11
app/src/main/res/drawable/bg_badge_accent.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="5dp" />
|
||||
<solid android:color="?colorAccent" />
|
||||
<padding
|
||||
android:bottom="2dp"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp" />
|
||||
</shape>
|
||||
13
app/src/main/res/drawable/bg_badge_outline.xml
Normal file
13
app/src/main/res/drawable/bg_badge_outline.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="5dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="?android:textColorTertiary" />
|
||||
<padding
|
||||
android:bottom="2dp"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp" />
|
||||
</shape>
|
||||
13
app/src/main/res/drawable/bg_badge_outline_accent.xml
Normal file
13
app/src/main/res/drawable/bg_badge_outline_accent.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="5dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="?android:colorAccent" />
|
||||
<padding
|
||||
android:bottom="2dp"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp" />
|
||||
</shape>
|
||||
@@ -27,25 +27,27 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:maxLines="3"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/imageView_cover"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
tools:text="@tools:sample/lorem[20]" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintEnd_toEndOf="@id/textView_title"
|
||||
app:layout_constraintStart_toStartOf="@id/textView_title"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_title"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
tools:text="@tools:sample/lorem[20]" />
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/ratingBar"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:id="@+id/textView_number"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_round_rect"
|
||||
android:background="@drawable/bg_badge_default"
|
||||
android:gravity="center"
|
||||
android:minWidth="26dp"
|
||||
android:textColor="?android:textColorSecondaryInverse"
|
||||
|
||||
Reference in New Issue
Block a user