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