Update error details dialog
This commit is contained in:
@@ -86,7 +86,17 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.koitharu.kotatsu.settings.SettingsActivity"
|
android:name="org.koitharu.kotatsu.settings.SettingsActivity"
|
||||||
android:exported="true"
|
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
|
<activity
|
||||||
android:name="org.koitharu.kotatsu.browser.BrowserActivity"
|
android:name="org.koitharu.kotatsu.browser.BrowserActivity"
|
||||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||||
@@ -145,6 +155,9 @@
|
|||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="kotatsu" />
|
<data android:scheme="kotatsu" />
|
||||||
|
<data android:host="shikimori-auth" />
|
||||||
|
<data android:host="anilist-auth" />
|
||||||
|
<data android:host="mal-auth" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import androidx.core.util.Consumer
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.koitharu.kotatsu.R
|
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.parsers.exception.ParseException
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class DialogErrorObserver(
|
|||||||
val fm = fragmentManager
|
val fm = fragmentManager
|
||||||
if (fm != null) {
|
if (fm != null) {
|
||||||
dialogBuilder.setPositiveButton(R.string.details) { _, _ ->
|
dialogBuilder.setPositiveButton(R.string.details) { _, _ ->
|
||||||
MangaErrorDialog.show(fm, error)
|
ErrorDetailsDialog.show(fm, error, error.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
package org.koitharu.kotatsu.core.exceptions.resolve
|
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.ActivityResultCallback
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.collection.ArrayMap
|
import androidx.collection.ArrayMap
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.browser.BrowserActivity
|
import org.koitharu.kotatsu.browser.BrowserActivity
|
||||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
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.AuthRequiredException
|
||||||
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
import org.koitharu.kotatsu.parsers.exception.NotFoundException
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
|
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
|
||||||
import org.koitharu.kotatsu.utils.TaggedActivityResult
|
import org.koitharu.kotatsu.utils.TaggedActivityResult
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
|
||||||
import org.koitharu.kotatsu.utils.isSuccess
|
import org.koitharu.kotatsu.utils.isSuccess
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
@@ -48,6 +44,10 @@ class ExceptionResolver private constructor(
|
|||||||
continuations.remove(result.tag)?.resume(result.isSuccess)
|
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) {
|
suspend fun resolve(e: Throwable): Boolean = when (e) {
|
||||||
is CloudFlareProtectedException -> resolveCF(e.url, e.headers)
|
is CloudFlareProtectedException -> resolveCF(e.url, e.headers)
|
||||||
is AuthRequiredException -> resolveAuthException(e.source)
|
is AuthRequiredException -> resolveAuthException(e.source)
|
||||||
@@ -100,21 +100,5 @@ class ExceptionResolver private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun canResolve(e: Throwable) = getResolveStringId(e) != 0
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import androidx.core.util.Consumer
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import org.koitharu.kotatsu.R
|
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.main.ui.owners.BottomNavOwner
|
||||||
import org.koitharu.kotatsu.parsers.exception.ParseException
|
import org.koitharu.kotatsu.parsers.exception.ParseException
|
||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
@@ -38,7 +38,7 @@ class SnackbarErrorObserver(
|
|||||||
val fm = fragmentManager
|
val fm = fragmentManager
|
||||||
if (fm != null) {
|
if (fm != null) {
|
||||||
snackbar.setAction(R.string.details) {
|
snackbar.setAction(R.string.details) {
|
||||||
MangaErrorDialog.show(fm, error)
|
ErrorDetailsDialog.show(fm, error, error.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package org.koitharu.kotatsu.core.ui
|
package org.koitharu.kotatsu.core.ui
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@@ -12,15 +15,15 @@ import androidx.fragment.app.FragmentManager
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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.databinding.DialogMangaErrorBinding
|
import org.koitharu.kotatsu.databinding.DialogErrorDetailsBinding
|
||||||
import org.koitharu.kotatsu.parsers.exception.ParseException
|
import org.koitharu.kotatsu.utils.ext.isReportable
|
||||||
import org.koitharu.kotatsu.utils.ext.report
|
import org.koitharu.kotatsu.utils.ext.report
|
||||||
import org.koitharu.kotatsu.utils.ext.requireSerializable
|
import org.koitharu.kotatsu.utils.ext.requireSerializable
|
||||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -28,8 +31,8 @@ class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
|
|||||||
exception = args.requireSerializable(ARG_ERROR)
|
exception = args.requireSerializable(ARG_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogMangaErrorBinding {
|
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): DialogErrorDetailsBinding {
|
||||||
return DialogMangaErrorBinding.inflate(inflater, container, false)
|
return DialogErrorDetailsBinding.inflate(inflater, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -39,28 +42,45 @@ class MangaErrorDialog : AlertDialogFragment<DialogMangaErrorBinding>() {
|
|||||||
text = context.getString(
|
text = context.getString(
|
||||||
R.string.manga_error_description_pattern,
|
R.string.manga_error_description_pattern,
|
||||||
exception.message?.htmlEncode().orEmpty(),
|
exception.message?.htmlEncode().orEmpty(),
|
||||||
exception.url,
|
arguments?.getString(ARG_URL),
|
||||||
).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY)
|
).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
|
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
|
||||||
return super.onBuildDialog(builder)
|
val builder = super.onBuildDialog(builder)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.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()
|
dismiss()
|
||||||
exception.report()
|
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 {
|
companion object {
|
||||||
|
|
||||||
private const val TAG = "MangaErrorDialog"
|
private const val TAG = "ErrorDetailsDialog"
|
||||||
private const val ARG_ERROR = "error"
|
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)
|
putSerializable(ARG_ERROR, error)
|
||||||
|
putString(ARG_URL, url)
|
||||||
}.show(fm, TAG)
|
}.show(fm, TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.koitharu.kotatsu.reader.ui.pager
|
package org.koitharu.kotatsu.reader.ui.pager
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
@@ -60,9 +59,9 @@ class PageHolderDelegate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showErrorDetails(context: Context) {
|
fun showErrorDetails(url: String?) {
|
||||||
val e = error ?: return
|
val e = error ?: return
|
||||||
ExceptionResolver.showDetails(context, e)
|
exceptionResolver.showDetails(e, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAttachedToWindow() {
|
fun onAttachedToWindow() {
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ open class PageHolder(
|
|||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
when (v.id) {
|
when (v.id) {
|
||||||
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class WebtoonHolder(
|
|||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
when (v.id) {
|
when (v.id) {
|
||||||
R.id.button_retry -> delegate.retry(boundData?.toMangaPage() ?: return)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
|||||||
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
|
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
|
||||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
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.sources.SourcesSettingsFragment
|
||||||
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
|
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
|
||||||
|
import org.koitharu.kotatsu.utils.ext.getSerializableExtraCompat
|
||||||
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
|
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -127,10 +129,17 @@ class SettingsActivity :
|
|||||||
ACTION_HISTORY -> HistorySettingsFragment()
|
ACTION_HISTORY -> HistorySettingsFragment()
|
||||||
ACTION_TRACKER -> TrackerSettingsFragment()
|
ACTION_TRACKER -> TrackerSettingsFragment()
|
||||||
ACTION_SOURCE -> SourceSettingsFragment.newInstance(
|
ACTION_SOURCE -> SourceSettingsFragment.newInstance(
|
||||||
intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL,
|
intent.getSerializableExtraCompat(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
ACTION_MANAGE_SOURCES -> SourcesSettingsFragment()
|
ACTION_MANAGE_SOURCES -> SourcesSettingsFragment()
|
||||||
|
Intent.ACTION_VIEW -> {
|
||||||
|
when (intent.data?.host) {
|
||||||
|
HOST_ABOUT -> AboutSettingsFragment()
|
||||||
|
else -> SettingsHeadersFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> SettingsHeadersFragment()
|
else -> SettingsHeadersFragment()
|
||||||
}
|
}
|
||||||
supportFragmentManager.commit {
|
supportFragmentManager.commit {
|
||||||
@@ -146,9 +155,9 @@ class SettingsActivity :
|
|||||||
private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER"
|
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_HISTORY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_HISTORY"
|
||||||
private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS"
|
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 ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST"
|
||||||
private const val EXTRA_SOURCE = "source"
|
private const val EXTRA_SOURCE = "source"
|
||||||
|
private const val HOST_ABOUT = "about"
|
||||||
|
|
||||||
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)
|
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String)
|
|||||||
return getParcelableExtra(key) as T?
|
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? {
|
inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String): T? {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
getSerializable(key, T::class.java)
|
getSerializable(key, T::class.java)
|
||||||
|
|||||||
@@ -377,7 +377,7 @@
|
|||||||
<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:<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.</string>
|
<string name="manga_error_description_pattern">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.</string>
|
||||||
<string name="history_shortcuts">Show recent manga shortcuts</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="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>
|
<string name="reader_control_ltr_summary">Tap on the right edge or pressing the right key always switches to the next page</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user