Update error details dialog

This commit is contained in:
Koitharu
2023-03-11 17:17:41 +02:00
parent 072cdc35e8
commit 28e3f1c063
12 changed files with 75 additions and 46 deletions

View File

@@ -86,7 +86,17 @@
<activity
android:name="org.koitharu.kotatsu.settings.SettingsActivity"
android:exported="true"
android:label="@string/settings" />
android:label="@string/settings">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="kotatsu" />
<data android:host="about" />
</intent-filter>
</activity>
<activity
android:name="org.koitharu.kotatsu.browser.BrowserActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
@@ -145,6 +155,9 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="kotatsu" />
<data android:host="shikimori-auth" />
<data android:host="anilist-auth" />
<data android:host="mal-auth" />
</intent-filter>
</activity>

View File

@@ -6,7 +6,7 @@ import androidx.core.util.Consumer
import androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.MangaErrorDialog
import org.koitharu.kotatsu.core.ui.ErrorDetailsDialog
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
@@ -37,7 +37,7 @@ class DialogErrorObserver(
val fm = fragmentManager
if (fm != null) {
dialogBuilder.setPositiveButton(R.string.details) { _, _ ->
MangaErrorDialog.show(fm, error)
ErrorDetailsDialog.show(fm, error, error.url)
}
}
}

View File

@@ -1,27 +1,23 @@
package org.koitharu.kotatsu.core.exceptions.resolve
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.collection.ArrayMap
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Headers
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.ui.ErrorDetailsDialog
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
import org.koitharu.kotatsu.utils.TaggedActivityResult
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.isSuccess
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
@@ -48,6 +44,10 @@ class ExceptionResolver private constructor(
continuations.remove(result.tag)?.resume(result.isSuccess)
}
fun showDetails(e: Throwable, url: String?) {
ErrorDetailsDialog.show(getFragmentManager(), e, url)
}
suspend fun resolve(e: Throwable): Boolean = when (e) {
is CloudFlareProtectedException -> resolveCF(e.url, e.headers)
is AuthRequiredException -> resolveAuthException(e.source)
@@ -100,21 +100,5 @@ class ExceptionResolver private constructor(
}
fun canResolve(e: Throwable) = getResolveStringId(e) != 0
fun showDetails(context: Context, e: Throwable) {
val stackTrace = e.stackTraceToString()
val dialog = MaterialAlertDialogBuilder(context)
.setTitle(e.getDisplayMessage(context.resources))
.setMessage(stackTrace)
.setPositiveButton(androidx.preference.R.string.copy) { _, _ ->
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboardManager.setPrimaryClip(
ClipData.newPlainText(context.getString(R.string.error), stackTrace),
)
}
.setNegativeButton(R.string.close, null)
.create()
dialog.show()
}
}
}

View File

@@ -5,7 +5,7 @@ import androidx.core.util.Consumer
import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.Snackbar
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.MangaErrorDialog
import org.koitharu.kotatsu.core.ui.ErrorDetailsDialog
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
@@ -38,7 +38,7 @@ class SnackbarErrorObserver(
val fm = fragmentManager
if (fm != null) {
snackbar.setAction(R.string.details) {
MangaErrorDialog.show(fm, error)
ErrorDetailsDialog.show(fm, error, error.url)
}
}
}

View File

@@ -1,5 +1,8 @@
package org.koitharu.kotatsu.core.ui
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
@@ -12,15 +15,15 @@ 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.databinding.DialogMangaErrorBinding
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.databinding.DialogErrorDetailsBinding
import org.koitharu.kotatsu.utils.ext.isReportable
import org.koitharu.kotatsu.utils.ext.report
import org.koitharu.kotatsu.utils.ext.requireSerializable
import org.koitharu.kotatsu.utils.ext.withArgs
class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
class ErrorDetailsDialog : AlertDialogFragment<DialogErrorDetailsBinding>() {
private lateinit var exception: ParseException
private lateinit var exception: Throwable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -28,8 +31,8 @@ class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
exception = args.requireSerializable(ARG_ERROR)
}
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogMangaErrorBinding {
return DialogMangaErrorBinding.inflate(inflater, container, false)
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogErrorDetailsBinding {
return DialogErrorDetailsBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -39,28 +42,45 @@ class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
text = context.getString(
R.string.manga_error_description_pattern,
exception.message?.htmlEncode().orEmpty(),
exception.url,
arguments?.getString(ARG_URL),
).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY)
}
}
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
return super.onBuildDialog(builder)
val builder = super.onBuildDialog(builder)
.setCancelable(true)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.report) { _, _ ->
.setTitle(R.string.error_occurred)
.setNeutralButton(androidx.preference.R.string.copy) { _, _ ->
copyToClipboard()
}
if (exception.isReportable()) {
builder.setPositiveButton(R.string.report) { _, _ ->
dismiss()
exception.report()
}.setTitle(R.string.error_occurred)
}
}
return builder
}
private fun copyToClipboard() {
val clipboardManager = context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
?: return
clipboardManager.setPrimaryClip(
ClipData.newPlainText(getString(R.string.error), exception.stackTraceToString()),
)
}
companion object {
private const val TAG = "MangaErrorDialog"
private const val TAG = "ErrorDetailsDialog"
private const val ARG_ERROR = "error"
private const val ARG_URL = "url"
fun show(fm: FragmentManager, error: ParseException) = MangaErrorDialog().withArgs(1) {
fun show(fm: FragmentManager, error: Throwable, url: String?) = ErrorDetailsDialog().withArgs(2) {
putSerializable(ARG_ERROR, error)
putString(ARG_URL, url)
}.show(fm, TAG)
}
}

View File

@@ -1,6 +1,5 @@
package org.koitharu.kotatsu.reader.ui.pager
import android.content.Context
import android.net.Uri
import androidx.core.net.toUri
import androidx.lifecycle.Observer
@@ -60,9 +59,9 @@ class PageHolderDelegate(
}
}
fun showErrorDetails(context: Context) {
fun showErrorDetails(url: String?) {
val e = error ?: return
ExceptionResolver.showDetails(context, e)
exceptionResolver.showDetails(e, url)
}
fun onAttachedToWindow() {

View File

@@ -115,7 +115,7 @@ open class PageHolder(
override fun onClick(v: View) {
when (v.id) {
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return)
R.id.button_error_details -> delegate.showErrorDetails(v.context)
R.id.button_error_details -> delegate.showErrorDetails(boundData?.url)
}
}

View File

@@ -104,7 +104,7 @@ class WebtoonHolder(
override fun onClick(v: View) {
when (v.id) {
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return)
R.id.button_error_details -> delegate.showErrorDetails(v.context)
R.id.button_error_details -> delegate.showErrorDetails(boundData?.url)
}
}

View File

@@ -23,8 +23,10 @@ import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
import org.koitharu.kotatsu.utils.ext.getSerializableExtraCompat
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
@AndroidEntryPoint
@@ -127,10 +129,17 @@ class SettingsActivity :
ACTION_HISTORY -> HistorySettingsFragment()
ACTION_TRACKER -> TrackerSettingsFragment()
ACTION_SOURCE -> SourceSettingsFragment.newInstance(
intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL,
intent.getSerializableExtraCompat(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL,
)
ACTION_MANAGE_SOURCES -> SourcesSettingsFragment()
Intent.ACTION_VIEW -> {
when (intent.data?.host) {
HOST_ABOUT -> AboutSettingsFragment()
else -> SettingsHeadersFragment()
}
}
else -> SettingsHeadersFragment()
}
supportFragmentManager.commit {
@@ -146,9 +155,9 @@ class SettingsActivity :
private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER"
private const val ACTION_HISTORY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_HISTORY"
private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS"
private const val ACTION_SHIKIMORI = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SHIKIMORI_SETTINGS"
private const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST"
private const val EXTRA_SOURCE = "source"
private const val HOST_ABOUT = "about"
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)

View File

@@ -21,6 +21,10 @@ inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String)
return getParcelableExtra(key) as T?
}
inline fun <reified T : Serializable> Intent.getSerializableExtraCompat(key: String): T? {
return getSerializableExtra(key) as T?
}
inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializable(key, T::class.java)

View File

@@ -377,7 +377,7 @@
<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="feed">Feed</string>
<string name="manga_error_description_pattern">Error details:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Try to &lt;a href="%2$s"&gt;open manga in a web browser&lt;/a&gt; to ensure it is available on its source&lt;br&gt;2. If it is available, send an error report to the developers.</string>
<string name="manga_error_description_pattern">Error details:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Try to &lt;a href="%2$s"&gt;open manga in a web browser&lt;/a&gt; to ensure it is available on its source&lt;br&gt;2. Make sure you are using the &lt;a href="kotatsu://about"&gt;latest version of Kotatsu&lt;/a&gt;&lt;br&gt;3. If it is available, send an error report to the developers.</string>
<string name="history_shortcuts">Show recent manga shortcuts</string>
<string name="history_shortcuts_summary">Make recent manga available by long pressing on application icon</string>
<string name="reader_control_ltr_summary">Tap on the right edge or pressing the right key always switches to the next page</string>