Unify reader error layout (#6512)

So nobody will think that the error layout is broken when they see different
layout.
This commit is contained in:
Ivan Iskandar 2022-02-03 09:41:20 +07:00 committed by GitHub
parent b6553bdc34
commit 7108993936
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 214 deletions

View file

@ -0,0 +1,29 @@
package eu.kanade.tachiyomi.ui.reader.viewer
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import com.google.android.material.button.MaterialButton
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
/**
* A button class to be used by child views of the pager viewer. All tap gestures are handled by
* the pager, but this class disables that behavior to allow clickable buttons.
*/
class ReaderButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialButtonStyle
) : MaterialButton(context, attrs, defStyleAttr) {
var viewer: PagerViewer? = null
override fun onTouchEvent(event: MotionEvent): Boolean {
viewer?.pager?.setGestureDetectorEnabled(false)
if (event.actionMasked == MotionEvent.ACTION_UP) {
viewer?.pager?.setGestureDetectorEnabled(true)
}
return super.onTouchEvent(event)
}
}

View file

@ -1,24 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint
import android.content.Context
import android.view.MotionEvent
import com.google.android.material.button.MaterialButton
/**
* A button class to be used by child views of the pager viewer. All tap gestures are handled by
* the pager, but this class disables that behavior to allow clickable buttons.
*/
@SuppressLint("ViewConstructor")
class PagerButton(context: Context, viewer: PagerViewer) : MaterialButton(context) {
init {
setOnTouchListener { _, event ->
viewer.pager.setGestureDetectorEnabled(false)
if (event.actionMasked == MotionEvent.ACTION_UP) {
viewer.pager.setGestureDetectorEnabled(true)
}
false
}
}
}

View file

@ -3,14 +3,10 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint
import android.content.Context
import android.view.Gravity
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import android.view.LayoutInflater
import androidx.core.view.isVisible
import androidx.core.view.setMargins
import androidx.core.view.updateLayoutParams
import com.google.android.material.textview.MaterialTextView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
@ -18,7 +14,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import rx.Observable
import rx.Subscription
@ -54,14 +49,9 @@ class PagerPageHolder(
}
/**
* Retry button used to allow retrying.
* Error layout to show when the image fails to load.
*/
private var retryButton: PagerButton? = null
/**
* Error layout to show when the image fails to decode.
*/
private var decodeErrorLayout: ViewGroup? = null
private var errorLayout: ReaderErrorBinding? = null
/**
* Subscription for status changes of the page.
@ -176,8 +166,7 @@ class PagerPageHolder(
*/
private fun setQueued() {
progressIndicator.show()
retryButton?.isVisible = false
decodeErrorLayout?.isVisible = false
errorLayout?.root?.isVisible = false
}
/**
@ -185,8 +174,7 @@ class PagerPageHolder(
*/
private fun setLoading() {
progressIndicator.show()
retryButton?.isVisible = false
decodeErrorLayout?.isVisible = false
errorLayout?.root?.isVisible = false
}
/**
@ -194,8 +182,7 @@ class PagerPageHolder(
*/
private fun setDownloading() {
progressIndicator.show()
retryButton?.isVisible = false
decodeErrorLayout?.isVisible = false
errorLayout?.root?.isVisible = false
}
/**
@ -203,8 +190,7 @@ class PagerPageHolder(
*/
private fun setImage() {
progressIndicator.setProgress(0)
retryButton?.isVisible = false
decodeErrorLayout?.isVisible = false
errorLayout?.root?.isVisible = false
unsubscribeReadImageHeader()
val streamFn = page.stream ?: return
@ -299,7 +285,7 @@ class PagerPageHolder(
*/
private fun setError() {
progressIndicator.hide()
initRetryButton().isVisible = true
showErrorLayout(withOpenInWebView = false)
}
override fun onImageLoaded() {
@ -313,7 +299,7 @@ class PagerPageHolder(
override fun onImageLoadError() {
super.onImageLoadError()
progressIndicator.hide()
initDecodeErrorLayout().isVisible = true
showErrorLayout(withOpenInWebView = true)
}
/**
@ -324,78 +310,24 @@ class PagerPageHolder(
viewer.activity.hideMenu()
}
/**
* Initializes a button to retry pages.
*/
private fun initRetryButton(): PagerButton {
if (retryButton != null) return retryButton!!
retryButton = PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
gravity = Gravity.CENTER
}
setText(R.string.action_retry)
setOnClickListener {
private fun showErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), this, true)
errorLayout?.actionRetry?.viewer = viewer
errorLayout?.actionRetry?.setOnClickListener {
page.chapter.pageLoader?.retryPage(page)
}
}
addView(retryButton)
return retryButton!!
}
/**
* Initializes a decode error layout.
*/
private fun initDecodeErrorLayout(): ViewGroup {
if (decodeErrorLayout != null) return decodeErrorLayout!!
val margins = 8.dpToPx
val decodeLayout = LinearLayout(context).apply {
gravity = Gravity.CENTER
orientation = LinearLayout.VERTICAL
}
decodeErrorLayout = decodeLayout
MaterialTextView(context).apply {
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins)
}
gravity = Gravity.CENTER
setText(R.string.decode_image_error)
decodeLayout.addView(this)
}
PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins)
}
setText(R.string.action_retry)
setOnClickListener {
page.chapter.pageLoader?.retryPage(page)
}
decodeLayout.addView(this)
}
val imageUrl = page.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins)
}
setText(R.string.action_open_in_web_view)
setOnClickListener {
val imageUrl = page.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.viewer = viewer
errorLayout?.actionOpenInWebView?.setOnClickListener {
val intent = WebViewActivity.newIntent(context, imageUrl!!)
context.startActivity(intent)
}
decodeLayout.addView(this)
}
}
addView(decodeLayout)
return decodeLayout
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
errorLayout?.root?.isVisible = true
return errorLayout!!
}
}

View file

@ -13,6 +13,7 @@ import com.google.android.material.progressindicator.CircularProgressIndicator
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderButton
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderTransitionView
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
@ -126,13 +127,14 @@ class PagerTransitionHolder(
text = context.getString(R.string.transition_pages_error, error.message)
}
val retryBtn = PagerButton(context, viewer).apply {
val retryBtn = ReaderButton(context).apply {
viewer = this@PagerTransitionHolder.viewer
wrapContent()
setText(R.string.action_retry)
setOnClickListener {
val toChapter = transition.to
if (toChapter != null) {
viewer.activity.requestPreloadChapter(toChapter)
this@PagerTransitionHolder.viewer.activity.requestPreloadChapter(toChapter)
}
}
}

View file

@ -2,18 +2,16 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.content.res.Resources
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.button.MaterialButton
import com.google.android.material.textview.MaterialTextView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderErrorBinding
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
@ -52,14 +50,9 @@ class WebtoonPageHolder(
private lateinit var progressContainer: ViewGroup
/**
* Retry button container used to allow retrying.
* Error layout to show when the image fails to load.
*/
private var retryContainer: ViewGroup? = null
/**
* Error layout to show when the image fails to decode.
*/
private var decodeErrorLayout: ViewGroup? = null
private var errorLayout: ReaderErrorBinding? = null
/**
* Getter to retrieve the height of the recycler view.
@ -125,7 +118,7 @@ class WebtoonPageHolder(
unsubscribeProgress()
unsubscribeReadImageHeader()
removeDecodeErrorLayout()
removeErrorLayout()
frame.recycle()
progressIndicator.setProgress(0, animated = false)
}
@ -219,8 +212,7 @@ class WebtoonPageHolder(
private fun setQueued() {
progressContainer.isVisible = true
progressIndicator.show()
retryContainer?.isVisible = false
removeDecodeErrorLayout()
removeErrorLayout()
}
/**
@ -229,8 +221,7 @@ class WebtoonPageHolder(
private fun setLoading() {
progressContainer.isVisible = true
progressIndicator.show()
retryContainer?.isVisible = false
removeDecodeErrorLayout()
removeErrorLayout()
}
/**
@ -239,8 +230,7 @@ class WebtoonPageHolder(
private fun setDownloading() {
progressContainer.isVisible = true
progressIndicator.show()
retryContainer?.isVisible = false
removeDecodeErrorLayout()
removeErrorLayout()
}
/**
@ -248,8 +238,7 @@ class WebtoonPageHolder(
*/
private fun setImage() {
progressIndicator.setProgress(0)
retryContainer?.isVisible = false
removeDecodeErrorLayout()
removeErrorLayout()
unsubscribeReadImageHeader()
val streamFn = page?.stream ?: return
@ -302,7 +291,7 @@ class WebtoonPageHolder(
*/
private fun setError() {
progressContainer.isVisible = false
initRetryLayout().isVisible = true
initErrorLayout(withOpenInWebView = false)
}
/**
@ -317,7 +306,7 @@ class WebtoonPageHolder(
*/
private fun onImageDecodeError() {
progressContainer.isVisible = false
initDecodeErrorLayout().isVisible = true
initErrorLayout(withOpenInWebView = true)
}
/**
@ -340,94 +329,32 @@ class WebtoonPageHolder(
/**
* Initializes a button to retry pages.
*/
private fun initRetryLayout(): ViewGroup {
if (retryContainer != null) return retryContainer!!
retryContainer = FrameLayout(context)
frame.addView(retryContainer, MATCH_PARENT, parentHeight)
MaterialButton(context).apply {
layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
gravity = Gravity.CENTER_HORIZONTAL
setMargins(0, parentHeight / 4, 0, 0)
}
setText(R.string.action_retry)
setOnClickListener {
private fun initErrorLayout(withOpenInWebView: Boolean): ReaderErrorBinding {
if (errorLayout == null) {
errorLayout = ReaderErrorBinding.inflate(LayoutInflater.from(context), frame, true)
errorLayout?.root?.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, (parentHeight * 0.8).toInt())
errorLayout?.actionRetry?.setOnClickListener {
page?.let { it.chapter.pageLoader?.retryPage(it) }
}
retryContainer!!.addView(this)
}
return retryContainer!!
}
/**
* Initializes a decode error layout.
*/
private fun initDecodeErrorLayout(): ViewGroup {
if (decodeErrorLayout != null) return decodeErrorLayout!!
val margins = 8.dpToPx
val decodeLayout = LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, parentHeight).apply {
setMargins(0, parentHeight / 6, 0, 0)
}
gravity = Gravity.CENTER_HORIZONTAL
orientation = LinearLayout.VERTICAL
}
decodeErrorLayout = decodeLayout
MaterialTextView(context).apply {
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, margins, 0, margins)
}
gravity = Gravity.CENTER
setText(R.string.decode_image_error)
decodeLayout.addView(this)
}
MaterialButton(context).apply {
layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, margins, 0, margins)
}
setText(R.string.action_retry)
setOnClickListener {
page?.let { it.chapter.pageLoader?.retryPage(it) }
}
decodeLayout.addView(this)
}
val imageUrl = page?.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
MaterialButton(context).apply {
layoutParams = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(0, margins, 0, margins)
}
setText(R.string.action_open_in_web_view)
setOnClickListener {
val imageUrl = page?.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) {
errorLayout?.actionOpenInWebView?.setOnClickListener {
val intent = WebViewActivity.newIntent(context, imageUrl!!)
context.startActivity(intent)
}
decodeLayout.addView(this)
}
}
frame.addView(decodeLayout)
return decodeLayout
errorLayout?.actionOpenInWebView?.isVisible = withOpenInWebView
return errorLayout!!
}
/**
* Removes the decode error layout from the holder, if found.
*/
private fun removeDecodeErrorLayout() {
val layout = decodeErrorLayout
if (layout != null) {
frame.removeView(layout)
decodeErrorLayout = null
private fun removeErrorLayout() {
errorLayout?.let {
frame.removeView(it.root)
errorLayout = null
}
}
}

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/decode_image_error"
android:textAppearance="?attr/textAppearanceBodyMedium" />
<eu.kanade.tachiyomi.ui.reader.viewer.ReaderButton
android:id="@+id/action_retry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/action_retry" />
<eu.kanade.tachiyomi.ui.reader.viewer.ReaderButton
android:id="@+id/action_open_in_web_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/action_open_in_web_view"
android:visibility="gone" />
</LinearLayout>