From 90e0e0b72a0dc20019a9e5b18417e61cd451c393 Mon Sep 17 00:00:00 2001 From: len Date: Sun, 23 Oct 2016 18:59:25 +0200 Subject: [PATCH] Webtoon reader now shows download progress. Keep the progress bar until the image is decoded --- .../viewer/base/PageDecodeErrorLayout.kt | 3 +- .../ui/reader/viewer/pager/PageView.kt | 117 +++++----- .../reader/viewer/pager/PagerReaderAdapter.kt | 8 +- .../reader/viewer/webtoon/WebtoonAdapter.kt | 2 +- .../ui/reader/viewer/webtoon/WebtoonHolder.kt | 202 ++++++++++-------- app/src/main/res/layout/item_pager_reader.xml | 1 - .../main/res/layout/item_webtoon_reader.xml | 38 ++-- 7 files changed, 209 insertions(+), 162 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/PageDecodeErrorLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/PageDecodeErrorLayout.kt index 50a608390c..91bd719fb9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/PageDecodeErrorLayout.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/PageDecodeErrorLayout.kt @@ -29,7 +29,7 @@ class PageDecodeErrorLayout(context: Context) : LinearLayout(context) { init { orientation = LinearLayout.VERTICAL - setGravity(Gravity.CENTER) + gravity = Gravity.CENTER } constructor(context: Context, page: Page, theme: Int, retryListener: () -> Unit) : this(context) { @@ -47,7 +47,6 @@ class PageDecodeErrorLayout(context: Context) : LinearLayout(context) { layoutParams = ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) setText(R.string.action_retry) setOnClickListener { - removeAllViews() retryListener() } addView(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PageView.kt index c3d9bd7c7e..8791efecba 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PageView.kt @@ -12,9 +12,6 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout -import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_CENTER -import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_LEFT -import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader.Companion.ALIGN_RIGHT import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader import eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical.VerticalReader import kotlinx.android.synthetic.main.chapter_image.view.* @@ -33,8 +30,12 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? /** * Page of a chapter. */ - var page: Page? = null - private set + lateinit var page: Page + + /** + * Subscription for status changes of the page. + */ + private var statusSubscription: Subscription? = null /** * Subscription for progress changes of the page. @@ -42,11 +43,11 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? private var progressSubscription: Subscription? = null /** - * Subscription for status changes of the page. + * Layout of decode error. */ - private var statusSubscription: Subscription? = null + private var decodeErrorLayout: PageDecodeErrorLayout? = null - fun initialize(reader: PagerReader, page: Page?) { + fun initialize(reader: PagerReader, page: Page) { val activity = reader.activity as ReaderActivity when (activity.readerTheme) { @@ -71,19 +72,11 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? setOnTouchListener { v, motionEvent -> reader.gestureDetector.onTouchEvent(motionEvent) } setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() { override fun onReady() { - when (reader.zoomType) { - ALIGN_LEFT -> setScaleAndCenter(scale, PointF(0f, 0f)) - ALIGN_RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0f)) - ALIGN_CENTER -> { - val newCenter = center - newCenter.y = 0f - setScaleAndCenter(scale, newCenter) - } - } + onImageDecoded(reader) } override fun onImageLoadError(e: Exception) { - onImageDecodeError(activity) + onImageDecodeError(reader) } }) } @@ -95,21 +88,15 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? true } - if (page != null) { - this.page = page - observeStatus() - } + this.page = page + observeStatus() } - fun cleanup() { + override fun onDetachedFromWindow() { unsubscribeProgress() unsubscribeStatus() image_view.setOnTouchListener(null) image_view.setOnImageEventListener(null) - } - - override fun onDetachedFromWindow() { - cleanup() super.onDetachedFromWindow() } @@ -120,7 +107,6 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? */ private fun observeStatus() { statusSubscription?.unsubscribe() - val page = page ?: return val statusSubject = SerializedSubject(PublishSubject.create()) page.setStatusSubject(statusSubject) @@ -135,7 +121,6 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? */ private fun observeProgress() { progressSubscription?.unsubscribe() - val page = page ?: return progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS) .map { page.progress } @@ -154,18 +139,18 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? */ private fun processStatus(status: Int) { when (status) { - Page.QUEUE -> hideError() - Page.LOAD_PAGE -> onLoading() + Page.QUEUE -> setQueued() + Page.LOAD_PAGE -> setLoading() Page.DOWNLOAD_IMAGE -> { observeProgress() - onDownloading() + setDownloading() } Page.READY -> { - onReady() + setImage() unsubscribeProgress() } Page.ERROR -> { - onError() + setError() unsubscribeProgress() } } @@ -175,7 +160,7 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? * Unsubscribes from the status subscription. */ private fun unsubscribeStatus() { - page?.setStatusSubject(null) + page.setStatusSubject(null) statusSubscription?.unsubscribe() statusSubscription = null } @@ -188,10 +173,23 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? progressSubscription = null } + /** + * Called when the page is queued. + */ + private fun setQueued() { + progress_container.visibility = View.VISIBLE + progress_text.visibility = View.INVISIBLE + retry_button.visibility = View.GONE + decodeErrorLayout?.let { + removeView(it) + decodeErrorLayout = null + } + } + /** * Called when the page is loading. */ - private fun onLoading() { + private fun setLoading() { progress_container.visibility = View.VISIBLE progress_text.visibility = View.VISIBLE progress_text.setText(R.string.downloading) @@ -200,7 +198,7 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? /** * Called when the page is downloading. */ - private fun onDownloading() { + private fun setDownloading() { progress_container.visibility = View.VISIBLE progress_text.visibility = View.VISIBLE } @@ -208,42 +206,51 @@ class PageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? /** * Called when the page is ready. */ - private fun onReady() { - page?.imagePath?.let { path -> - if (File(path).exists()) { - image_view.setImage(ImageSource.uri(path)) - progress_container.visibility = View.GONE - } else { - page?.status = Page.ERROR - } + private fun setImage() { + val path = page.imagePath + if (path != null && File(path).exists()) { + progress_text.visibility = View.INVISIBLE + image_view.setImage(ImageSource.uri(path)) + } else { + page.status = Page.ERROR } } /** * Called when the page has an error. */ - private fun onError() { + private fun setError() { progress_container.visibility = View.GONE retry_button.visibility = View.VISIBLE } /** - * Hides the error layout. + * Called when the image is decoded and going to be displayed. */ - private fun hideError() { - retry_button.visibility = View.GONE + private fun onImageDecoded(reader: PagerReader) { + progress_container.visibility = View.GONE + + with(image_view) { + when (reader.zoomType) { + PagerReader.ALIGN_LEFT -> setScaleAndCenter(scale, PointF(0f, 0f)) + PagerReader.ALIGN_RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0f)) + PagerReader.ALIGN_CENTER -> setScaleAndCenter(scale, center.apply { y = 0f }) + } + } } /** * Called when an image fails to decode. */ - private fun onImageDecodeError(activity: ReaderActivity) { - page?.let { page -> - val errorLayout = PageDecodeErrorLayout(context, page, activity.readerTheme, - { activity.presenter.retryPage(page) }) + private fun onImageDecodeError(reader: PagerReader) { + if (decodeErrorLayout != null || !reader.isAdded) return - addView(errorLayout) - } + val activity = reader.activity as ReaderActivity + + decodeErrorLayout = PageDecodeErrorLayout(context, page, activity.readerTheme, + { activity.presenter.retryPage(page) }) + + addView(decodeErrorLayout) } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReaderAdapter.kt index fe3fadfc0c..48f13e1528 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReaderAdapter.kt @@ -15,7 +15,7 @@ class PagerReaderAdapter(private val reader: PagerReader) : ViewPagerAdapter() { /** * Pages stored in the adapter. */ - var pages: List? = null + var pages: List = emptyList() set(value) { field = value notifyDataSetChanged() @@ -23,17 +23,15 @@ class PagerReaderAdapter(private val reader: PagerReader) : ViewPagerAdapter() { override fun createView(container: ViewGroup, position: Int): View { val view = container.inflate(R.layout.item_pager_reader) as PageView - view.initialize(reader, pages?.getOrNull(position)) + view.initialize(reader, pages[position]) return view } /** * Returns the number of pages. - * - * @return the number of pages or 0 if the list is null. */ override fun getCount(): Int { - return pages?.size ?: 0 + return pages.size } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt index 6fc8233435..697f92d5cf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt @@ -72,7 +72,7 @@ class WebtoonAdapter(val fragment: WebtoonReader) : RecyclerView.Adapter if (event.action == MotionEvent.ACTION_UP) { readerActivity.presenter.retryPage(page) @@ -92,13 +92,22 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) * @param page the page to bind. */ fun onSetValues(page: Page) { + this.page = page + observeStatus() + } + + /** + * Called when the view is recycled and added to the view pool. + */ + fun onRecycle() { + unsubscribeStatus() + unsubscribeProgress() decodeErrorLayout?.let { (view as ViewGroup).removeView(it) decodeErrorLayout = null } - - this.page = page - observeStatus() + view.image_view.recycle() + view.progress_container.visibility = View.VISIBLE } /** @@ -107,17 +116,35 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) * @see processStatus */ private fun observeStatus() { - page?.let { page -> - val statusSubject = SerializedSubject(PublishSubject.create()) - page.setStatusSubject(statusSubject) + val page = page ?: return - statusSubscription?.unsubscribe() - statusSubscription = statusSubject.startWith(page.status) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { processStatus(it) } + val statusSubject = SerializedSubject(PublishSubject.create()) + page.setStatusSubject(statusSubject) - webtoonReader.subscriptions.add(statusSubscription) - } + statusSubscription?.unsubscribe() + statusSubscription = statusSubject.startWith(page.status) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { processStatus(it) } + + webtoonReader.subscriptions.add(statusSubscription) + } + + /** + * Observes the progress of the page and updates view. + */ + private fun observeProgress() { + progressSubscription?.unsubscribe() + + val page = page ?: return + + progressSubscription = Observable.interval(100, TimeUnit.MILLISECONDS) + .map { page.progress } + .distinctUntilChanged() + .onBackpressureLatest() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { progress -> + view.progress_text.text = view.context.getString(R.string.download_progress, progress) + } } /** @@ -127,104 +154,109 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) */ private fun processStatus(status: Int) { when (status) { - Page.QUEUE -> onQueue() - Page.LOAD_PAGE -> onLoading() - Page.DOWNLOAD_IMAGE -> onLoading() - Page.READY -> onReady() - Page.ERROR -> onError() + Page.QUEUE -> setQueued() + Page.LOAD_PAGE -> setLoading() + Page.DOWNLOAD_IMAGE -> { + observeProgress() + setDownloading() + } + Page.READY -> { + setImage() + unsubscribeProgress() + } + Page.ERROR -> { + setError() + unsubscribeProgress() + } } } /** * Unsubscribes from the status subscription. */ - fun unsubscribeStatus() { + private fun unsubscribeStatus() { + page?.setStatusSubject(null) statusSubscription?.unsubscribe() statusSubscription = null } + /** + * Unsubscribes from the progress subscription. + */ + private fun unsubscribeProgress() { + progressSubscription?.unsubscribe() + progressSubscription = null + } + + /** + * Called when the page is queued. + */ + private fun setQueued() = with(view) { + progress_container.visibility = View.VISIBLE + progress_text.visibility = View.INVISIBLE + retry_button.visibility = View.GONE + decodeErrorLayout?.let { + (view as ViewGroup).removeView(it) + decodeErrorLayout = null + } + } + /** * Called when the page is loading. */ - private fun onLoading() { - setRetryButtonVisible(false) - setImageVisible(false) - setProgressVisible(true) + private fun setLoading() = with(view) { + progress_container.visibility = View.VISIBLE + progress_text.visibility = View.VISIBLE + progress_text.setText(R.string.downloading) + } + + /** + * Called when the page is downloading + */ + private fun setDownloading() = with(view) { + progress_container.visibility = View.VISIBLE + progress_text.visibility = View.VISIBLE } /** * Called when the page is ready. */ - private fun onReady() { - setRetryButtonVisible(false) - setProgressVisible(false) - setImageVisible(true) - - page?.imagePath?.let { path -> - if (File(path).exists()) { - view.image_view.setImage(ImageSource.uri(path)) - view.progress.visibility = View.GONE - } else { - page?.status = Page.ERROR - } + private fun setImage() = with(view) { + val path = page?.imagePath + if (path != null && File(path).exists()) { + progress_text.visibility = View.INVISIBLE + image_view.setImage(ImageSource.uri(path)) + } else { + page?.status = Page.ERROR } } /** * Called when the page has an error. */ - private fun onError() { - setImageVisible(false) - setProgressVisible(false) - setRetryButtonVisible(true) + private fun setError() = with(view) { + progress_container.visibility = View.GONE + retry_button.visibility = View.VISIBLE } /** - * Called when the page is queued. + * Called when the image is decoded and going to be displayed. */ - private fun onQueue() { - setImageVisible(false) - setRetryButtonVisible(false) - setProgressVisible(false) + private fun onImageDecoded() { + view.progress_container.visibility = View.GONE } /** * Called when the image fails to decode. */ private fun onImageDecodeError() { - page?.let { page -> - decodeErrorLayout = PageDecodeErrorLayout(view.context, page, readerActivity.readerTheme, - { readerActivity.presenter.retryPage(page) }) + val page = page ?: return + if (decodeErrorLayout != null || !webtoonReader.isAdded) return - (view as ViewGroup).addView(decodeErrorLayout) - } - } + decodeErrorLayout = PageDecodeErrorLayout(view.context, page, readerActivity.readerTheme, + { readerActivity.presenter.retryPage(page) }) - /** - * Sets the visibility of the progress bar. - * - * @param visible whether to show it or not. - */ - private fun setProgressVisible(visible: Boolean) { - view.progress.visibility = if (visible) View.VISIBLE else View.GONE - } - - /** - * Sets the visibility of the image view. - * - * @param visible whether to show it or not. - */ - private fun setImageVisible(visible: Boolean) { - view.image_view.visibility = if (visible) View.VISIBLE else View.GONE - } - - /** - * Sets the visibility of the retry button. - * - * @param visible whether to show it or not. - */ - private fun setRetryButtonVisible(visible: Boolean) { - view.retry_button.visibility = if (visible) View.VISIBLE else View.GONE + (view as ViewGroup).addView(decodeErrorLayout) } /** diff --git a/app/src/main/res/layout/item_pager_reader.xml b/app/src/main/res/layout/item_pager_reader.xml index 3b5f3d8c95..8da96ed630 100644 --- a/app/src/main/res/layout/item_pager_reader.xml +++ b/app/src/main/res/layout/item_pager_reader.xml @@ -1,5 +1,4 @@ - + android:layout_height="wrap_content" + xmlns:android="http://schemas.android.com/apk/res/android"> - + + android:layout_gravity="center_horizontal"/> -