From de9e773d2cd5749096712a9f3a7c3784df3a1cb0 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 24 Feb 2020 19:58:16 +0200 Subject: [PATCH] Refactor reader --- .../koitharu/kotatsu/core/local/CbzFilter.kt | 3 +- .../core/parser/LocalMangaRepository.kt | 16 ++++- .../ui/download/DownloadNotification.kt | 6 +- .../kotatsu/ui/reader/BaseReaderFragment.kt | 34 +++++++++ .../kotatsu/ui/reader/ReaderActivity.kt | 72 ++++++++++--------- .../kotatsu/ui/reader/ReaderPresenter.kt | 16 +++++ .../koitharu/kotatsu/ui/reader/ReaderView.kt | 13 ++-- .../ui/reader/{ => standard}/PageHolder.kt | 3 +- .../ui/reader/{ => standard}/PagesAdapter.kt | 6 +- .../reader/standard/StandardReaderFragment.kt | 56 +++++++++++++++ app/src/main/res/layout/activity_reader.xml | 8 +-- .../res/layout/fragment_reader_standard.xml | 6 ++ app/src/main/res/values/strings.xml | 1 + 13 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt rename app/src/main/java/org/koitharu/kotatsu/ui/reader/{ => standard}/PageHolder.kt (95%) rename app/src/main/java/org/koitharu/kotatsu/ui/reader/{ => standard}/PagesAdapter.kt (66%) create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt create mode 100644 app/src/main/res/layout/fragment_reader_standard.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/core/local/CbzFilter.kt b/app/src/main/java/org/koitharu/kotatsu/core/local/CbzFilter.kt index 5dda18a9d..0cdd9b914 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/local/CbzFilter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/local/CbzFilter.kt @@ -5,5 +5,6 @@ import java.io.FilenameFilter class CbzFilter : FilenameFilter { - override fun accept(dir: File, name: String) = name.endsWith(".cbz", ignoreCase = true) + override fun accept(dir: File, name: String) = + name.endsWith(".cbz", ignoreCase = true) || name.endsWith(".zip", ignoreCase = true) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt index 8cce1324d..f947ececd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/LocalMangaRepository.kt @@ -16,6 +16,7 @@ import org.koitharu.kotatsu.utils.ext.readText import org.koitharu.kotatsu.utils.ext.safe import java.io.File import java.util.* +import java.util.zip.ZipEntry import java.util.zip.ZipFile class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaRepository(loaderContext) { @@ -68,7 +69,11 @@ class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaReposit x.copy( source = MangaSource.LOCAL, url = fileUri, - coverUrl = zipUri(file, it.getCoverEntry() ?: zip.entries().nextElement().name), + coverUrl = zipUri( + file, + entryName = it.getCoverEntry() + ?: findFirstEntry(zip.entries())?.name.orEmpty() + ), chapters = x.chapters?.map { c -> c.copy(url = fileUri) } ) } @@ -79,7 +84,7 @@ class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaReposit title = title, url = fileUri, source = MangaSource.LOCAL, - coverUrl = zipUri(file, zip.entries().nextElement().name), + coverUrl = zipUri(file, findFirstEntry(zip.entries())?.name.orEmpty()), chapters = listOf( MangaChapter( id = file.absolutePath.longHashCode(), @@ -101,6 +106,13 @@ class LocalMangaRepository(loaderContext: MangaLoaderContext) : BaseMangaReposit private fun zipUri(file: File, entryName: String) = Uri.fromParts("cbz", file.path, entryName).toString() + private fun findFirstEntry(entries: Enumeration): ZipEntry? { + val list = entries.toList() + .filterNot { it.isDirectory } + .sortedWith(compareBy(AlphanumComparator()) { x -> x.name }) + return list.firstOrNull() + } + companion object { fun isFileSupported(name: String): Boolean { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt index 0c2fcba88..c9ef0eb81 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/download/DownloadNotification.kt @@ -36,7 +36,6 @@ class DownloadNotification(private val context: Context) { builder.setContentText(context.getString(R.string.manga_downloading_)) builder.setProgress(1, 0, true) builder.setSmallIcon(android.R.drawable.stat_sys_download) - builder.setSubText(context.getText(R.string.preparing_)) builder.setLargeIcon(null) } @@ -50,19 +49,18 @@ class DownloadNotification(private val context: Context) { chapter * PROGRESS_STEP + (page / pagesTotal.toFloat() * PROGRESS_STEP).roundToInt() val percent = (progress / max.toFloat() * 100).roundToInt() builder.setProgress(max, progress, false) - builder.setSubText("$percent%") + builder.setContentText(context.getString(R.string.downloading_d_percent, percent)) } fun setPostProcessing() { builder.setProgress(1, 0, true) - builder.setSubText(context.getString(R.string.processing_)) + builder.setContentText(context.getString(R.string.processing_)) } fun setDone() { builder.setProgress(0, 0, false) builder.setContentText(context.getString(R.string.download_complete)) builder.setSmallIcon(android.R.drawable.stat_sys_download_done) - builder.setSubText(null) } fun update(id: Int = NOTIFICATION_ID) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt new file mode 100644 index 000000000..37269140c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/BaseReaderFragment.kt @@ -0,0 +1,34 @@ +package org.koitharu.kotatsu.ui.reader + +import android.net.Uri +import androidx.annotation.LayoutRes +import org.koitharu.kotatsu.core.model.MangaPage +import org.koitharu.kotatsu.ui.common.BaseFragment + +abstract class BaseReaderFragment(@LayoutRes contentLayoutId: Int) : BaseFragment(contentLayoutId), ReaderView { + + abstract val hasItems: Boolean + + abstract val currentPageIndex: Int + + abstract val pages: List + + abstract fun setCurrentPage(index: Int, smooth: Boolean) + + val currentPage get() = pages.getOrNull(currentPageIndex) + + /** + * Handled by activity + */ + override fun onLoadingStateChanged(isLoading: Boolean) = Unit + + /** + * Handled by activity + */ + override fun onError(e: Exception) = Unit + + /** + * Handled by activity + */ + override fun onPageSaved(uri: Uri?) = Unit +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt index 10b371f4d..2ebb0e951 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt @@ -12,6 +12,7 @@ import android.view.MotionEvent import android.widget.Toast import androidx.core.view.isVisible 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 @@ -22,6 +23,7 @@ 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.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.utils.GridTouchHelper @@ -32,14 +34,15 @@ import org.koitharu.kotatsu.utils.ext.* class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnChapterChangeListener, GridTouchHelper.OnGridTouchListener, OnPageSelectListener { - private val presenter by moxyPresenter(factory = ::ReaderPresenter) + private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance) private lateinit var state: ReaderState - private lateinit var loader: PageLoader - private lateinit var adapter: PagesAdapter private lateinit var touchHelper: GridTouchHelper + private val reader + get() = supportFragmentManager.findFragmentById(R.id.container) as? BaseReaderFragment + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_reader) @@ -67,23 +70,22 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh insets } - loader = PageLoader() - adapter = PagesAdapter(loader) - pager.adapter = adapter - pager.offscreenPageLimit = 2 + if (reader == null) { + supportFragmentManager.commit { + replace(R.id.container, StandardReaderFragment()) + } + } + if (savedInstanceState?.containsKey(MvpDelegate.MOXY_DELEGATE_TAGS_KEY) != true) { presenter.loadChapter(state) } } - override fun onDestroy() { - loader.dispose() - super.onDestroy() - } - override fun onPause() { - state = state.copy(page = pager.currentItem) - presenter.saveState(state) + reader?.let { + state = state.copy(page = it.currentPageIndex) + presenter.saveState(state) + } super.onPause() } @@ -102,9 +104,9 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh true } R.id.action_pages_thumbs -> { - if (adapter.hasItems) { + if (reader?.hasItems == true) { PagesThumbnailsSheet.show( - supportFragmentManager, adapter.items, + supportFragmentManager, reader!!.pages, state.chapter?.name ?: title?.toString().orEmpty() ) } else { @@ -113,10 +115,13 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh true } R.id.action_save_page -> { - if (adapter.hasItems) { + if (reader?.hasItems == true) { requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) { if (it) { - presenter.savePage(contentResolver, adapter.getItem(pager.currentItem)) + presenter.savePage( + resolver = contentResolver, + page = reader?.currentPage ?: return@requestPermission + ) } } } else { @@ -128,8 +133,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh } override fun onPagesReady(pages: List, index: Int) { - adapter.replaceData(pages) - pager.setCurrentItem(index, false) + } override fun onLoadingStateChanged(isLoading: Boolean) { @@ -141,7 +145,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh setTitle(R.string.error_occurred) setMessage(e.message) setPositiveButton(R.string.close, null) - if (!adapter.hasItems) { + if (reader?.hasItems != true) { setOnDismissListener { finish() } @@ -164,20 +168,22 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh } GridTouchHelper.AREA_TOP, GridTouchHelper.AREA_LEFT -> { - pager.setCurrentItem(pager.currentItem - 1, true) + reader?.let { + it.setCurrentPage(it.currentPageIndex - 1, true) + } } GridTouchHelper.AREA_BOTTOM, GridTouchHelper.AREA_RIGHT -> { - pager.setCurrentItem(pager.currentItem + 1, true) + reader?.let { + it.setCurrentPage(it.currentPageIndex + 1, true) + } } } } override fun onProcessTouch(rawX: Int, rawY: Int): Boolean { - return if (appbar_top.hasGlobalPoint(rawX, rawY) || appbar_bottom.hasGlobalPoint( - rawX, - rawY - ) + return if (appbar_top.hasGlobalPoint(rawX, rawY) + || appbar_bottom.hasGlobalPoint(rawX, rawY) ) { false } else { @@ -201,20 +207,22 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh } override fun onPageSelected(page: MangaPage) { - val index = adapter.items.indexOfFirst { x -> x.id == page.id } - if (index != -1) { - pager.setCurrentItem(index, false) + reader?.let { + val index = it.pages.indexOfFirst { x -> x.id == page.id } + if (index != -1) { + it.setCurrentPage(index, false) + } } } override fun onPageSaved(uri: Uri?) { if (uri != null) { - Snackbar.make(pager, R.string.page_saved, Snackbar.LENGTH_LONG) + Snackbar.make(container, R.string.page_saved, Snackbar.LENGTH_LONG) .setAction(R.string.share) { ShareHelper.shareImage(this, uri) }.show() } else { - Snackbar.make(pager, R.string.error_occurred, Snackbar.LENGTH_SHORT).show() + Snackbar.make(container, R.string.error_occurred, Snackbar.LENGTH_SHORT).show() } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt index d4cf69085..4c5472ab1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderPresenter.kt @@ -84,4 +84,20 @@ class ReaderPresenter : BasePresenter() { } } + override fun onDestroy() { + instance = null + super.onDestroy() + } + + companion object { + + private var instance: ReaderPresenter? = null + + fun getInstance(): ReaderPresenter = instance ?: synchronized(this) { + ReaderPresenter().also { + instance = it + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt index 6cacba9ee..f7e52a877 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderView.kt @@ -2,22 +2,21 @@ package org.koitharu.kotatsu.ui.reader import android.net.Uri import moxy.MvpView -import moxy.viewstate.strategy.AddToEndSingleStrategy -import moxy.viewstate.strategy.OneExecutionStateStrategy -import moxy.viewstate.strategy.StateStrategyType +import moxy.viewstate.strategy.alias.AddToEndSingle +import moxy.viewstate.strategy.alias.OneExecution import org.koitharu.kotatsu.core.model.MangaPage interface ReaderView : MvpView { - @StateStrategyType(AddToEndSingleStrategy::class) + @AddToEndSingle fun onPagesReady(pages: List, index: Int) - @StateStrategyType(AddToEndSingleStrategy::class) + @AddToEndSingle fun onLoadingStateChanged(isLoading: Boolean) - @StateStrategyType(OneExecutionStateStrategy::class) + @OneExecution fun onError(e: Exception) - @StateStrategyType(OneExecutionStateStrategy::class) + @OneExecution fun onPageSaved(uri: Uri?) } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt similarity index 95% rename from app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt rename to app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt index 873d90504..d36b63b3e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.ui.reader +package org.koitharu.kotatsu.ui.reader.standard import android.view.ViewGroup import androidx.core.net.toUri @@ -10,6 +10,7 @@ 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 PageHolder(parent: ViewGroup, private val loader: PageLoader) : diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagesAdapter.kt similarity index 66% rename from app/src/main/java/org/koitharu/kotatsu/ui/reader/PagesAdapter.kt rename to app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagesAdapter.kt index 4b2359c95..88c3aecaa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/PagesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagesAdapter.kt @@ -1,12 +1,14 @@ -package org.koitharu.kotatsu.ui.reader +package org.koitharu.kotatsu.ui.reader.standard 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 PagesAdapter(private val loader: PageLoader) : BaseRecyclerAdapter() { - override fun onCreateViewHolder(parent: ViewGroup) = PageHolder(parent, loader) + override fun onCreateViewHolder(parent: ViewGroup) = + PageHolder(parent, loader) override fun onGetItemId(item: MangaPage) = item.id diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt new file mode 100644 index 000000000..acb331a26 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/StandardReaderFragment.kt @@ -0,0 +1,56 @@ +package org.koitharu.kotatsu.ui.reader.standard + +import android.os.Bundle +import android.view.View +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.ui.reader.BaseReaderFragment +import org.koitharu.kotatsu.ui.reader.PageLoader +import org.koitharu.kotatsu.ui.reader.ReaderPresenter + +class StandardReaderFragment : BaseReaderFragment(R.layout.fragment_reader_standard) { + + private val presenter by moxyPresenter(factory = ReaderPresenter.Companion::getInstance) + + private var adapter: PagesAdapter? = 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 = PagesAdapter(loader) + pager.adapter = adapter + pager.offscreenPageLimit = 2 + } + + override fun onPagesReady(pages: List, index: Int) { + adapter?.let { + it.replaceData(pages) + pager.setCurrentItem(index, false) + } + } + + override fun onDestroy() { + loader.dispose() + super.onDestroy() + } + + override val hasItems: Boolean + get() = adapter?.hasItems == true + + override val currentPageIndex: Int + get() = pager.currentItem + + override val pages: List + get() = adapter?.items.orEmpty() + + override fun setCurrentPage(index: Int, smooth: Boolean) { + pager.setCurrentItem(index, smooth) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index 8fa48533b..0453a7e2b 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -4,11 +4,11 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/rootLayout" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:keepScreenOn="true"> - diff --git a/app/src/main/res/layout/fragment_reader_standard.xml b/app/src/main/res/layout/fragment_reader_standard.xml new file mode 100644 index 000000000..dce13cd82 --- /dev/null +++ b/app/src/main/res/layout/fragment_reader_standard.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 64f0e1da6..06a32053f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -79,4 +79,5 @@ Clear pages cache Cache B|kB|MB|GB|TB + Downloading: %d%% \ No newline at end of file