Show dialog on manga loading error

This commit is contained in:
Koitharu
2022-08-18 11:07:20 +03:00
parent 68b68eb4c5
commit 072f6d8c69
13 changed files with 136 additions and 29 deletions

View File

@@ -21,14 +21,14 @@ abstract class AlertDialogFragment<B : ViewBinding> : DialogFragment() {
viewBinding = binding viewBinding = binding
return MaterialAlertDialogBuilder(requireContext(), theme) return MaterialAlertDialogBuilder(requireContext(), theme)
.setView(binding.root) .setView(binding.root)
.also(::onBuildDialog) .run(::onBuildDialog)
.create() .create()
} }
final override fun onCreateView( final override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
) = viewBinding?.root ) = viewBinding?.root
@CallSuper @CallSuper
@@ -37,9 +37,9 @@ abstract class AlertDialogFragment<B : ViewBinding> : DialogFragment() {
super.onDestroyView() super.onDestroyView()
} }
open fun onBuildDialog(builder: MaterialAlertDialogBuilder) = Unit open fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder = builder
protected fun bindingOrNull(): B? = viewBinding protected fun bindingOrNull(): B? = viewBinding
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
} }

View File

@@ -58,8 +58,8 @@ class CloudFlareDialog : AlertDialogFragment<FragmentCloudflareBinding>(), Cloud
super.onDestroyView() super.onDestroyView()
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setNegativeButton(android.R.string.cancel, null) return super.onBuildDialog(builder).setNegativeButton(android.R.string.cancel, null)
} }
override fun onResume() { override fun onResume() {

View File

@@ -0,0 +1,70 @@
package org.koitharu.kotatsu.core.ui
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.text.HtmlCompat
import androidx.core.text.htmlEncode
import androidx.core.text.parseAsHtml
import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.databinding.DialogMangaErrorBinding
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.utils.ext.report
import org.koitharu.kotatsu.utils.ext.withArgs
class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
private lateinit var error: Throwable
private lateinit var manga: Manga
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = requireArguments()
manga = requireNotNull(args.getParcelable<ParcelableManga>(ARG_MANGA)?.manga)
error = args.getSerializable(ARG_ERROR) as Throwable
}
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogMangaErrorBinding {
return DialogMangaErrorBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(binding.textViewMessage) {
movementMethod = LinkMovementMethod.getInstance()
text = context.getString(
R.string.manga_error_description_pattern,
this@MangaErrorDialog.error.message?.htmlEncode().orEmpty(),
manga.publicUrl,
).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY)
}
}
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
return super.onBuildDialog(builder)
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.report) { _, _ ->
dismiss()
error.report(TAG)
}.setTitle(R.string.error_occurred)
}
companion object {
private const val TAG = "MangaErrorDialog"
private const val ARG_ERROR = "error"
private const val ARG_MANGA = "manga"
fun show(fm: FragmentManager, manga: Manga, error: Throwable) = MangaErrorDialog().withArgs(2) {
putParcelable(ARG_MANGA, ParcelableManga(manga, false))
putSerializable(ARG_ERROR, error)
}.show(fm, TAG)
}
}

View File

@@ -18,7 +18,6 @@ import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.domain.MangaIntent
@@ -27,6 +26,7 @@ import org.koitharu.kotatsu.base.ui.widgets.BottomSheetHeaderBar
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.os.ShortcutsUpdater import org.koitharu.kotatsu.core.os.ShortcutsUpdater
import org.koitharu.kotatsu.core.ui.MangaErrorDialog
import org.koitharu.kotatsu.databinding.ActivityDetailsBinding import org.koitharu.kotatsu.databinding.ActivityDetailsBinding
import org.koitharu.kotatsu.details.ui.model.HistoryInfo import org.koitharu.kotatsu.details.ui.model.HistoryInfo
import org.koitharu.kotatsu.download.ui.service.DownloadService import org.koitharu.kotatsu.download.ui.service.DownloadService
@@ -36,6 +36,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderState import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.utils.ext.* import org.koitharu.kotatsu.utils.ext.*
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class DetailsActivity : class DetailsActivity :
@@ -172,11 +173,12 @@ class DetailsActivity :
} }
private fun onError(e: Throwable) { private fun onError(e: Throwable) {
val manga = viewModel.manga.value
when { when {
ExceptionResolver.canResolve(e) -> { ExceptionResolver.canResolve(e) -> {
resolveError(e) resolveError(e)
} }
viewModel.manga.value == null -> { manga == null -> {
Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show() Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show()
finishAfterTransition() finishAfterTransition()
} }
@@ -192,8 +194,8 @@ class DetailsActivity :
) )
snackbar.anchorView = binding.headerChapters snackbar.anchorView = binding.headerChapters
if (e.isReportable()) { if (e.isReportable()) {
snackbar.setAction(R.string.report) { snackbar.setAction(R.string.details) {
e.report("DetailsActivity::onError") MangaErrorDialog.show(supportFragmentManager, manga, e)
} }
} }
snackbar.show() snackbar.show()

View File

@@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup import org.koitharu.kotatsu.base.ui.widgets.CheckableButtonGroup
@@ -17,7 +18,6 @@ import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.databinding.DialogListModeBinding import org.koitharu.kotatsu.databinding.DialogListModeBinding
import org.koitharu.kotatsu.utils.ext.setValueRounded import org.koitharu.kotatsu.utils.ext.setValueRounded
import org.koitharu.kotatsu.utils.progress.IntPercentLabelFormatter import org.koitharu.kotatsu.utils.progress.IntPercentLabelFormatter
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class ListModeSelectDialog : class ListModeSelectDialog :
@@ -33,8 +33,9 @@ class ListModeSelectDialog :
container: ViewGroup?, container: ViewGroup?,
) = DialogListModeBinding.inflate(inflater, container, false) ) = DialogListModeBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setTitle(R.string.list_mode) return super.onBuildDialog(builder)
.setTitle(R.string.list_mode)
.setPositiveButton(R.string.done, null) .setPositiveButton(R.string.done, null)
.setCancelable(true) .setCancelable(true)
} }

View File

@@ -27,8 +27,9 @@ class ImportDialogFragment : AlertDialogFragment<DialogImportBinding>(), View.On
return DialogImportBinding.inflate(inflater, container, false) return DialogImportBinding.inflate(inflater, container, false)
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setTitle(R.string._import) return super.onBuildDialog(builder)
.setTitle(R.string._import)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setCancelable(true) .setCancelable(true)
} }

View File

@@ -15,14 +15,15 @@ import org.koitharu.kotatsu.databinding.DialogReaderConfigBinding
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
@Deprecated("Not in use") @Deprecated("Not in use")
class ReaderConfigDialog : AlertDialogFragment<DialogReaderConfigBinding>(), class ReaderConfigDialog :
AlertDialogFragment<DialogReaderConfigBinding>(),
CheckableButtonGroup.OnCheckedChangeListener { CheckableButtonGroup.OnCheckedChangeListener {
private lateinit var mode: ReaderMode private lateinit var mode: ReaderMode
override fun onInflateView( override fun onInflateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup? container: ViewGroup?,
) = DialogReaderConfigBinding.inflate(inflater, container, false) ) = DialogReaderConfigBinding.inflate(inflater, container, false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -32,8 +33,9 @@ class ReaderConfigDialog : AlertDialogFragment<DialogReaderConfigBinding>(),
?: ReaderMode.STANDARD ?: ReaderMode.STANDARD
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setTitle(R.string.read_mode) return super.onBuildDialog(builder)
.setTitle(R.string.read_mode)
.setPositiveButton(R.string.done, null) .setPositiveButton(R.string.done, null)
.setCancelable(true) .setCancelable(true)
} }
@@ -48,8 +50,10 @@ class ReaderConfigDialog : AlertDialogFragment<DialogReaderConfigBinding>(),
} }
override fun onDismiss(dialog: DialogInterface) { override fun onDismiss(dialog: DialogInterface) {
((parentFragment as? Callback) (
?: (activity as? Callback))?.onReaderModeChanged(mode) (parentFragment as? Callback)
?: (activity as? Callback)
)?.onReaderModeChanged(mode)
super.onDismiss(dialog) super.onDismiss(dialog)
} }

View File

@@ -51,8 +51,9 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
viewModel.onError.observe(viewLifecycleOwner, this::onError) viewModel.onError.observe(viewLifecycleOwner, this::onError)
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setCancelable(false) return super.onBuildDialog(builder)
.setCancelable(false)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
} }

View File

@@ -44,8 +44,9 @@ class RestoreDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
viewModel.onError.observe(viewLifecycleOwner, this::onError) viewModel.onError.observe(viewLifecycleOwner, this::onError)
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder.setCancelable(false) return super.onBuildDialog(builder)
.setCancelable(false)
} }
private fun onError(e: Throwable) { private fun onError(e: Throwable) {

View File

@@ -43,8 +43,8 @@ class NewSourcesDialogFragment :
viewModel.sources.observe(viewLifecycleOwner) { adapter.items = it } viewModel.sources.observe(viewLifecycleOwner) { adapter.items = it }
} }
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder return super.onBuildDialog(builder)
.setPositiveButton(R.string.done, this) .setPositiveButton(R.string.done, this)
.setCancelable(true) .setCancelable(true)
.setTitle(R.string.remote_sources) .setTitle(R.string.remote_sources)

View File

@@ -39,8 +39,8 @@ class OnboardDialogFragment :
container: ViewGroup?, container: ViewGroup?,
) = DialogOnboardBinding.inflate(inflater, container, false) ) = DialogOnboardBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) { override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
builder super.onBuildDialog(builder)
.setPositiveButton(R.string.done, this) .setPositiveButton(R.string.done, this)
.setCancelable(true) .setCancelable(true)
if (isWelcome) { if (isWelcome) {
@@ -50,6 +50,7 @@ class OnboardDialogFragment :
.setTitle(R.string.remote_sources) .setTitle(R.string.remote_sources)
.setNegativeButton(android.R.string.cancel, this) .setNegativeButton(android.R.string.cancel, this)
} }
return builder
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="@dimen/margin_normal"
android:paddingTop="@dimen/margin_normal">
<TextView
android:id="@+id/textView_message"
style="@style/MaterialAlertDialog.Material3.Body.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:linksClickable="true"
tools:text="@tools:sample/lorem[20]" />
</LinearLayout>
</ScrollView>

View File

@@ -376,4 +376,5 @@
<string name="import_completed_hint">You can delete the original file from storage to save space</string> <string name="import_completed_hint">You can delete the original file from storage to save space</string>
<string name="import_will_start_soon">Import will start soon</string> <string name="import_will_start_soon">Import will start soon</string>
<string name="feed">Feed</string> <string name="feed">Feed</string>
<string name="manga_error_description_pattern">Error details:&lt;br>&lt;tt>%1$s&lt;/tt>&lt;br>&lt;br>1. Try to &lt;a href="%2$s">open manga in a web browser&lt;/a> to ensure it is available on its source&lt;br>2. If it is available, send an error report to the developers.</string>
</resources> </resources>