From 28e3f1c0631c258a360e13ea5111cb6b59825a3f Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Mar 2023 17:17:41 +0200 Subject: [PATCH] Update error details dialog --- app/src/main/AndroidManifest.xml | 15 ++++++- .../exceptions/resolve/DialogErrorObserver.kt | 4 +- .../exceptions/resolve/ExceptionResolver.kt | 26 +++-------- .../resolve/SnackbarErrorObserver.kt | 4 +- ...gaErrorDialog.kt => ErrorDetailsDialog.kt} | 44 ++++++++++++++----- .../reader/ui/pager/PageHolderDelegate.kt | 5 +-- .../reader/ui/pager/standard/PageHolder.kt | 2 +- .../reader/ui/pager/webtoon/WebtoonHolder.kt | 2 +- .../kotatsu/settings/SettingsActivity.kt | 13 +++++- .../org/koitharu/kotatsu/utils/ext/Bundle.kt | 4 ++ ...nga_error.xml => dialog_error_details.xml} | 0 app/src/main/res/values/strings.xml | 2 +- 12 files changed, 75 insertions(+), 46 deletions(-) rename app/src/main/java/org/koitharu/kotatsu/core/ui/{MangaErrorDialog.kt => ErrorDetailsDialog.kt} (56%) rename app/src/main/res/layout/{dialog_manga_error.xml => dialog_error_details.xml} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 58e59c8ff..b633c91f2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -86,7 +86,17 @@ + android:label="@string/settings"> + + + + + + + + + + + + + diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt index 0dc07c7f2..365151648 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/DialogErrorObserver.kt @@ -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) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt index 459986d17..60df80fbf 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/ExceptionResolver.kt @@ -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() - } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt index 1e1366d76..ff0df6ba6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/exceptions/resolve/SnackbarErrorObserver.kt @@ -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) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/ui/MangaErrorDialog.kt b/app/src/main/java/org/koitharu/kotatsu/core/ui/ErrorDetailsDialog.kt similarity index 56% rename from app/src/main/java/org/koitharu/kotatsu/core/ui/MangaErrorDialog.kt rename to app/src/main/java/org/koitharu/kotatsu/core/ui/ErrorDetailsDialog.kt index e9b2e1697..02102ae0d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/ui/MangaErrorDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/ui/ErrorDetailsDialog.kt @@ -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() { +class ErrorDetailsDialog : AlertDialogFragment() { - 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() { 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() { 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) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index 3c838884e..bb1e8c37b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -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() { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt index 073fa0185..d377e6833 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt @@ -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) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt index 4218e0533..7e629a704 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt @@ -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) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt index eadf48103..d2014a9ba 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -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) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/Bundle.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/Bundle.kt index 2203c4fb9..fb7a0bb02 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/Bundle.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/Bundle.kt @@ -21,6 +21,10 @@ inline fun Intent.getParcelableExtraCompat(key: String) return getParcelableExtra(key) as T? } +inline fun Intent.getSerializableExtraCompat(key: String): T? { + return getSerializableExtra(key) as T? +} + inline fun Bundle.getSerializableCompat(key: String): T? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getSerializable(key, T::class.java) diff --git a/app/src/main/res/layout/dialog_manga_error.xml b/app/src/main/res/layout/dialog_error_details.xml similarity index 100% rename from app/src/main/res/layout/dialog_manga_error.xml rename to app/src/main/res/layout/dialog_error_details.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7eb7b4ecb..c1670872c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -377,7 +377,7 @@ You can delete the original file from storage to save space Import will start soon Feed - Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. If it is available, send an error report to the developers. + Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. Make sure you are using the <a href="kotatsu://about">latest version of Kotatsu</a><br>3. If it is available, send an error report to the developers. Show recent manga shortcuts Make recent manga available by long pressing on application icon Tap on the right edge or pressing the right key always switches to the next page