Webtoon reader
This commit is contained in:
@@ -7,7 +7,7 @@ import org.koitharu.kotatsu.core.db.entity.*
|
||||
@Database(
|
||||
entities = [
|
||||
MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class,
|
||||
FavouriteCategoryEntity::class, FavouriteEntity::class
|
||||
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class
|
||||
], version = 1
|
||||
)
|
||||
abstract class MangaDatabase : RoomDatabase() {
|
||||
@@ -20,5 +20,7 @@ abstract class MangaDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun favouritesDao(): FavouritesDao
|
||||
|
||||
abstract fun preferencesDao(): PreferencesDao
|
||||
|
||||
abstract fun favouriteCategoriesDao(): FavouriteCategoriesDao
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.koitharu.kotatsu.core.db
|
||||
|
||||
import androidx.room.*
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
|
||||
|
||||
@Dao
|
||||
abstract class PreferencesDao {
|
||||
|
||||
@Query("SELECT * FROM preferences WHERE manga_id = :mangaId")
|
||||
abstract suspend fun find(mangaId: Long): MangaPrefsEntity?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
abstract suspend fun insert(pref: MangaPrefsEntity): Long
|
||||
|
||||
@Update
|
||||
abstract suspend fun update(pref: MangaPrefsEntity): Int
|
||||
|
||||
@Transaction
|
||||
open suspend fun upsert(pref: MangaPrefsEntity) {
|
||||
if (update(pref) == 0) {
|
||||
insert(pref)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ data class MangaEntity(
|
||||
altTitle = manga.altTitle,
|
||||
rating = manga.rating,
|
||||
state = manga.state?.name,
|
||||
// tags = manga.tags.map(TagEntity.Companion::fromMangaTag),
|
||||
title = manga.title
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.core.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "preferences")
|
||||
data class MangaPrefsEntity(
|
||||
@PrimaryKey(autoGenerate = false)
|
||||
@ColumnInfo(name = "manga_id") val mangaId: Long,
|
||||
@ColumnInfo(name = "mode") val mode: Int
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
enum class ReaderMode(val id: Int) {
|
||||
|
||||
UNKNOWN(0),
|
||||
STANDARD(1),
|
||||
WEBTOON(2);
|
||||
|
||||
companion object {
|
||||
|
||||
fun valueOf(id: Int) = values().firstOrNull { it.id == id }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.koitharu.kotatsu.domain
|
||||
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
|
||||
class MangaPreferencesRepository : KoinComponent {
|
||||
|
||||
private val db: MangaDatabase by inject()
|
||||
|
||||
suspend fun saveData(mangaId: Long, mode: ReaderMode) {
|
||||
db.preferencesDao().upsert(
|
||||
MangaPrefsEntity(
|
||||
mangaId = mangaId,
|
||||
mode = mode.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getReaderMode(mangaId: Long): ReaderMode {
|
||||
return db.preferencesDao().find(mangaId)?.let { ReaderMode.valueOf(it.mode) }
|
||||
?: ReaderMode.UNKNOWN
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,14 @@ import org.koitharu.kotatsu.ui.common.AlertDialogFragment
|
||||
|
||||
class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), View.OnClickListener {
|
||||
|
||||
private val setting by inject<AppSettings>()
|
||||
private val settings by inject<AppSettings>()
|
||||
|
||||
private lateinit var mode: ListMode
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = settings.listMode
|
||||
}
|
||||
|
||||
override fun onBuildDialog(builder: AlertDialog.Builder) {
|
||||
builder.setTitle(R.string.list_mode)
|
||||
@@ -22,7 +29,6 @@ class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), Vie
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val mode = setting.listMode
|
||||
button_list.isChecked = mode == ListMode.LIST
|
||||
button_list_detailed.isChecked = mode == ListMode.DETAILED_LIST
|
||||
button_grid.isChecked = mode == ListMode.GRID
|
||||
@@ -35,10 +41,13 @@ class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), Vie
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_ok -> dismiss()
|
||||
R.id.button_list -> setting.listMode = ListMode.LIST
|
||||
R.id.button_list_detailed -> setting.listMode = ListMode.DETAILED_LIST
|
||||
R.id.button_grid -> setting.listMode = ListMode.GRID
|
||||
R.id.button_ok -> {
|
||||
settings.listMode = mode
|
||||
dismiss()
|
||||
}
|
||||
R.id.button_list -> mode = ListMode.LIST
|
||||
R.id.button_list_detailed -> mode = ListMode.DETAILED_LIST
|
||||
R.id.button_grid -> mode = ListMode.GRID
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,24 +15,25 @@ import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.commit
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import moxy.MvpDelegate
|
||||
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.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.ui.common.BaseFullscreenActivity
|
||||
import org.koitharu.kotatsu.ui.reader.standard.StandardReaderFragment
|
||||
import org.koitharu.kotatsu.ui.reader.thumbnails.OnPageSelectListener
|
||||
import org.koitharu.kotatsu.ui.reader.thumbnails.PagesThumbnailsSheet
|
||||
import org.koitharu.kotatsu.ui.reader.wetoon.WebtoonReaderFragment
|
||||
import org.koitharu.kotatsu.utils.GridTouchHelper
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.anim.Motion
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnChapterChangeListener,
|
||||
GridTouchHelper.OnGridTouchListener, OnPageSelectListener {
|
||||
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback {
|
||||
|
||||
private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance)
|
||||
|
||||
@@ -70,14 +71,22 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
insets
|
||||
}
|
||||
|
||||
if (reader == null) {
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.container, StandardReaderFragment())
|
||||
}
|
||||
}
|
||||
presenter.loadChapter(state)
|
||||
}
|
||||
|
||||
if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) {
|
||||
presenter.loadChapter(state)
|
||||
override fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState) {
|
||||
val currentReader = reader
|
||||
when (mode) {
|
||||
ReaderMode.WEBTOON -> if (currentReader !is WebtoonReaderFragment) {
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.container, WebtoonReaderFragment())
|
||||
}
|
||||
}
|
||||
else -> if (currentReader !is StandardReaderFragment) {
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.container, StandardReaderFragment())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,9 +103,20 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putParcelable(EXTRA_STATE, state)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_settings -> {
|
||||
ReaderConfigDialog.show(supportFragmentManager)
|
||||
ReaderConfigDialog.show(
|
||||
supportFragmentManager, when (reader) {
|
||||
is StandardReaderFragment -> ReaderMode.STANDARD
|
||||
is WebtoonReaderFragment -> ReaderMode.WEBTOON
|
||||
else -> ReaderMode.UNKNOWN
|
||||
}
|
||||
)
|
||||
true
|
||||
}
|
||||
R.id.action_chapters -> {
|
||||
@@ -136,10 +156,6 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onPagesReady(pages: List<MangaPage>, index: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
layout_loading.isVisible = isLoading
|
||||
}
|
||||
@@ -202,12 +218,11 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
}
|
||||
|
||||
override fun onChapterChanged(chapter: MangaChapter) {
|
||||
presenter.loadChapter(
|
||||
state.copy(
|
||||
chapterId = chapter.id,
|
||||
page = 0
|
||||
)
|
||||
state = state.copy(
|
||||
chapterId = chapter.id,
|
||||
page = 0
|
||||
)
|
||||
presenter.loadChapter(state)
|
||||
}
|
||||
|
||||
override fun onPageSelected(page: MangaPage) {
|
||||
@@ -219,6 +234,14 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReaderModeChanged(mode: ReaderMode) {
|
||||
reader?.let {
|
||||
state = state.copy(page = it.currentPageIndex)
|
||||
}
|
||||
presenter.saveState(state, mode)
|
||||
recreate()
|
||||
}
|
||||
|
||||
override fun onPageSaved(uri: Uri?) {
|
||||
if (uri != null) {
|
||||
Snackbar.make(container, R.string.page_saved, Snackbar.LENGTH_LONG)
|
||||
|
||||
@@ -4,33 +4,65 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import kotlinx.android.synthetic.main.dialog_list_mode.button_ok
|
||||
import kotlinx.android.synthetic.main.dialog_reader_config.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.ui.common.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
class ReaderConfigDialog : AlertDialogFragment(R.layout.dialog_reader_config),
|
||||
View.OnClickListener {
|
||||
|
||||
private lateinit var mode: ReaderMode
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = arguments?.getInt(ARG_MODE, ReaderMode.UNKNOWN.id)
|
||||
?.let { ReaderMode.valueOf(it) }
|
||||
?.takeUnless { it == ReaderMode.UNKNOWN }
|
||||
?: ReaderMode.STANDARD
|
||||
}
|
||||
|
||||
override fun onBuildDialog(builder: AlertDialog.Builder) {
|
||||
builder//.setTitle(R.string.list_mode)
|
||||
builder.setTitle(R.string.read_mode)
|
||||
.setCancelable(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
button_standard.isChecked = mode == ReaderMode.STANDARD
|
||||
button_webtoon.isChecked = mode == ReaderMode.WEBTOON
|
||||
|
||||
button_ok.setOnClickListener(this)
|
||||
button_standard.setOnClickListener(this)
|
||||
button_webtoon.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_ok -> dismiss()
|
||||
|
||||
R.id.button_ok -> {
|
||||
((parentFragment as? Callback)
|
||||
?: (activity as? Callback))?.onReaderModeChanged(mode)
|
||||
dismiss()
|
||||
}
|
||||
R.id.button_standard -> mode = ReaderMode.STANDARD
|
||||
R.id.button_webtoon -> mode = ReaderMode.WEBTOON
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
|
||||
fun onReaderModeChanged(mode: ReaderMode)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "ReaderConfigDialog"
|
||||
private const val ARG_MODE = "mode"
|
||||
|
||||
fun show(fm: FragmentManager) = ReaderConfigDialog().show(fm, TAG)
|
||||
fun show(fm: FragmentManager, mode: ReaderMode) = ReaderConfigDialog().withArgs(1) {
|
||||
putInt(ARG_MODE, mode.id)
|
||||
}.show(fm, TAG)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.domain.MangaPreferencesRepository
|
||||
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||
import org.koitharu.kotatsu.domain.history.HistoryRepository
|
||||
import org.koitharu.kotatsu.ui.common.BasePresenter
|
||||
@@ -27,14 +29,14 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
presenterScope.launch {
|
||||
viewState.onLoadingStateChanged(isLoading = true)
|
||||
try {
|
||||
val pages = withContext(Dispatchers.IO) {
|
||||
val (pages, mode) = withContext(Dispatchers.IO) {
|
||||
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)
|
||||
repo.getPages(chapter) to MangaPreferencesRepository().getReaderMode(state.manga.id)
|
||||
}
|
||||
viewState.onPagesReady(pages, state.page)
|
||||
viewState.onInitReader(pages, mode, state)
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace()
|
||||
@@ -46,13 +48,19 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
}
|
||||
}
|
||||
|
||||
fun saveState(state: ReaderState) {
|
||||
fun saveState(state: ReaderState, mode: ReaderMode? = null) {
|
||||
presenterScope.launch(Dispatchers.IO) {
|
||||
HistoryRepository().addOrUpdate(
|
||||
manga = state.manga,
|
||||
chapterId = state.chapterId,
|
||||
page = state.page
|
||||
)
|
||||
if (mode != null) {
|
||||
MangaPreferencesRepository().saveData(
|
||||
mangaId = state.manga.id,
|
||||
mode = mode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import moxy.MvpView
|
||||
import moxy.viewstate.strategy.alias.AddToEndSingle
|
||||
import moxy.viewstate.strategy.alias.OneExecution
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
|
||||
interface ReaderView : MvpView {
|
||||
|
||||
@AddToEndSingle
|
||||
fun onPagesReady(pages: List<MangaPage>, index: Int)
|
||||
fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState)
|
||||
|
||||
@AddToEndSingle
|
||||
fun onLoadingStateChanged(isLoading: Boolean)
|
||||
|
||||
@@ -6,9 +6,11 @@ import kotlinx.android.synthetic.main.fragment_reader_standard.*
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.ui.reader.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.ui.reader.PageLoader
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderPresenter
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderState
|
||||
|
||||
class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_standard) {
|
||||
|
||||
@@ -29,10 +31,10 @@ class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_stand
|
||||
pager.offscreenPageLimit = 2
|
||||
}
|
||||
|
||||
override fun onPagesReady(pages: List<MangaPage>, index: Int) {
|
||||
override fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState) {
|
||||
adapter?.let {
|
||||
it.replaceData(pages)
|
||||
pager.setCurrentItem(index, false)
|
||||
pager.setCurrentItem(state.page, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.koitharu.kotatsu.ui.reader.wetoon
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||
import org.koitharu.kotatsu.ui.reader.PageLoader
|
||||
|
||||
class WebtoonAdapter(private val loader: PageLoader) : BaseRecyclerAdapter<MangaPage, Unit>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup) =
|
||||
WebtoonHolder(parent, loader)
|
||||
|
||||
override fun onGetItemId(item: MangaPage) = item.id
|
||||
|
||||
override fun getExtra(item: MangaPage, position: Int) = Unit
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.koitharu.kotatsu.ui.reader.wetoon
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import kotlinx.android.synthetic.main.item_page.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.ui.reader.PageLoader
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
|
||||
|
||||
class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) :
|
||||
BaseViewHolder<MangaPage, Unit>(parent, R.layout.item_page),
|
||||
SubsamplingScaleImageView.OnImageEventListener, CoroutineScope by loader {
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
init {
|
||||
ssiv.setOnImageEventListener(this)
|
||||
button_retry.setOnClickListener {
|
||||
doLoad(boundData ?: return@setOnClickListener, force = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(data: MangaPage, extra: Unit) {
|
||||
doLoad(data, force = false)
|
||||
}
|
||||
|
||||
private fun doLoad(data: MangaPage, force: Boolean) {
|
||||
job?.cancel()
|
||||
job = launch {
|
||||
layout_error.isVisible = false
|
||||
progressBar.isVisible = true
|
||||
ssiv.recycle()
|
||||
try {
|
||||
val uri = withContext(Dispatchers.IO) {
|
||||
loader.loadFile(data.url, force)
|
||||
}.toUri()
|
||||
ssiv.setImage(ImageSource.uri(uri))
|
||||
} catch (e: CancellationException) {
|
||||
//do nothing
|
||||
} catch (e: Exception) {
|
||||
onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReady() {
|
||||
ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM)
|
||||
ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat()
|
||||
ssiv.setScaleAndCenter(
|
||||
ssiv.minScale,
|
||||
PointF(ssiv.sWidth / 2f, 0f)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onImageLoadError(e: Exception) = onError(e)
|
||||
|
||||
override fun onImageLoaded() {
|
||||
progressBar.isVisible = false
|
||||
}
|
||||
|
||||
override fun onTileLoadError(e: Exception?) = Unit
|
||||
|
||||
override fun onPreviewReleased() = Unit
|
||||
|
||||
override fun onPreviewLoadError(e: Exception?) = Unit
|
||||
|
||||
private fun onError(e: Throwable) {
|
||||
textView_error.text = e.getDisplayMessage(context.resources)
|
||||
layout_error.isVisible = true
|
||||
progressBar.isVisible = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.koitharu.kotatsu.ui.reader.wetoon
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import kotlinx.android.synthetic.main.fragment_reader_webtoon.*
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.ui.reader.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.ui.reader.PageLoader
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderPresenter
|
||||
import org.koitharu.kotatsu.ui.reader.ReaderState
|
||||
import org.koitharu.kotatsu.utils.ext.firstItem
|
||||
|
||||
class WebtoonReaderFragment : BaseReaderFragment(R.layout.fragment_reader_webtoon) {
|
||||
|
||||
private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance)
|
||||
|
||||
private var adapter: WebtoonAdapter? = null
|
||||
private lateinit var loader: PageLoader
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
loader = PageLoader()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
adapter = WebtoonAdapter(loader)
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
override fun onInitReader(pages: List<MangaPage>, mode: ReaderMode, state: ReaderState) {
|
||||
adapter?.let {
|
||||
it.replaceData(pages)
|
||||
recyclerView.firstItem = state.page
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
loader.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override val hasItems: Boolean
|
||||
get() = adapter?.hasItems == true
|
||||
|
||||
override val currentPageIndex: Int
|
||||
get() = recyclerView.firstItem
|
||||
|
||||
override val pages: List<MangaPage>
|
||||
get() = adapter?.items.orEmpty()
|
||||
|
||||
override fun setCurrentPage(index: Int, smooth: Boolean) {
|
||||
if (smooth) {
|
||||
recyclerView.smoothScrollToPosition(index)
|
||||
} else {
|
||||
recyclerView.firstItem = index
|
||||
}
|
||||
}
|
||||
}
|
||||
9
app/src/main/res/layout/fragment_reader_webtoon.xml
Normal file
9
app/src/main/res/layout/fragment_reader_webtoon.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
@@ -82,4 +82,5 @@
|
||||
<string name="downloading_d_percent">Downloading: %d%%</string>
|
||||
<string name="standard">Standard</string>
|
||||
<string name="webtoon">Webtoon</string>
|
||||
<string name="read_mode">Read mode</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user