Merge branch 'devel' into feature/nextgen
This commit is contained in:
@@ -14,8 +14,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 32
|
targetSdkVersion 32
|
||||||
versionCode 411
|
versionCode 412
|
||||||
versionName '3.3.2'
|
versionName '3.4'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -77,19 +77,19 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||||
implementation('com.github.nv95:kotatsu-parsers:c92f89f307') {
|
implementation('com.github.nv95:kotatsu-parsers:da3b0ae0cf') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.8.0'
|
implementation 'androidx.core:core-ktx:1.8.0'
|
||||||
implementation 'androidx.activity:activity-ktx:1.5.0-rc01'
|
implementation 'androidx.activity:activity-ktx:1.5.0'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.0-rc01'
|
implementation 'androidx.fragment:fragment-ktx:1.5.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-rc02'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0-rc02'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-service:2.5.0-rc02'
|
implementation 'androidx.lifecycle:lifecycle-service:2.5.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-process:2.5.0-rc02'
|
implementation 'androidx.lifecycle:lifecycle-process:2.5.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
@@ -99,7 +99,7 @@ dependencies {
|
|||||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
||||||
implementation 'com.google.android.material:material:1.7.0-alpha02'
|
implementation 'com.google.android.material:material:1.7.0-alpha02'
|
||||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.0-rc02'
|
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.0'
|
||||||
|
|
||||||
implementation 'androidx.room:room-runtime:2.4.2'
|
implementation 'androidx.room:room-runtime:2.4.2'
|
||||||
implementation 'androidx.room:room-ktx:2.4.2'
|
implementation 'androidx.room:room-ktx:2.4.2'
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ package org.koitharu.kotatsu.core.model.parcelable
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
import org.koitharu.kotatsu.utils.ext.createList
|
|
||||||
|
|
||||||
class ParcelableMangaChapters(
|
class ParcelableMangaChapters(
|
||||||
val chapters: List<MangaChapter>,
|
val chapters: List<MangaChapter>,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
createList(parcel.readInt()) { parcel.readMangaChapter() }
|
List(parcel.readInt()) { parcel.readMangaChapter() }
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ package org.koitharu.kotatsu.core.model.parcelable
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||||
import org.koitharu.kotatsu.utils.ext.createList
|
|
||||||
|
|
||||||
class ParcelableMangaPages(
|
class ParcelableMangaPages(
|
||||||
val pages: List<MangaPage>,
|
val pages: List<MangaPage>,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
createList(parcel.readInt()) { parcel.readMangaPage() }
|
List(parcel.readInt()) { parcel.readMangaPage() }
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package org.koitharu.kotatsu.core.model.parcelable
|
|||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
import org.koitharu.kotatsu.utils.ext.createSet
|
import org.koitharu.kotatsu.utils.ext.Set
|
||||||
|
|
||||||
class ParcelableMangaTags(
|
class ParcelableMangaTags(
|
||||||
val tags: Set<MangaTag>,
|
val tags: Set<MangaTag>,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
createSet(parcel.readInt()) { parcel.readMangaTag() }
|
Set(parcel.readInt()) { parcel.readMangaTag() }
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.koitharu.kotatsu.core.ui
|
package org.koitharu.kotatsu.core.ui
|
||||||
|
|
||||||
|
import android.text.Html
|
||||||
import coil.ComponentRegistry
|
import coil.ComponentRegistry
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
@@ -10,6 +11,7 @@ import org.koin.dsl.module
|
|||||||
import org.koitharu.kotatsu.core.parser.FaviconMapper
|
import org.koitharu.kotatsu.core.parser.FaviconMapper
|
||||||
import org.koitharu.kotatsu.local.data.CacheDir
|
import org.koitharu.kotatsu.local.data.CacheDir
|
||||||
import org.koitharu.kotatsu.local.data.CbzFetcher
|
import org.koitharu.kotatsu.local.data.CbzFetcher
|
||||||
|
import org.koitharu.kotatsu.utils.image.CoilImageGetter
|
||||||
|
|
||||||
val uiModule
|
val uiModule
|
||||||
get() = module {
|
get() = module {
|
||||||
@@ -40,4 +42,5 @@ val uiModule
|
|||||||
.build()
|
.build()
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
factory<Html.ImageGetter> { CoilImageGetter(androidContext(), get()) }
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,6 @@ val detailsModule
|
|||||||
get() = module {
|
get() = module {
|
||||||
|
|
||||||
viewModel { intent ->
|
viewModel { intent ->
|
||||||
DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get(), get(), get())
|
DetailsViewModel(intent.get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,7 @@ import coil.size.Scale
|
|||||||
import coil.util.CoilUtils
|
import coil.util.CoilUtils
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
@@ -80,6 +81,7 @@ class DetailsFragment :
|
|||||||
viewModel.readingHistory.observe(viewLifecycleOwner, ::onHistoryChanged)
|
viewModel.readingHistory.observe(viewLifecycleOwner, ::onHistoryChanged)
|
||||||
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
||||||
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
||||||
|
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
|
||||||
addMenuProvider(DetailsMenuProvider())
|
addMenuProvider(DetailsMenuProvider())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +110,6 @@ class DetailsFragment :
|
|||||||
textViewTitle.text = manga.title
|
textViewTitle.text = manga.title
|
||||||
textViewSubtitle.textAndVisible = manga.altTitle
|
textViewSubtitle.textAndVisible = manga.altTitle
|
||||||
textViewAuthor.textAndVisible = manga.author
|
textViewAuthor.textAndVisible = manga.author
|
||||||
textViewDescription.text = manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank)
|
|
||||||
?: getString(R.string.no_description)
|
|
||||||
when (manga.state) {
|
when (manga.state) {
|
||||||
MangaState.FINISHED -> {
|
MangaState.FINISHED -> {
|
||||||
textViewState.apply {
|
textViewState.apply {
|
||||||
@@ -172,6 +172,14 @@ class DetailsFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onDescriptionChanged(description: CharSequence?) {
|
||||||
|
if (description.isNullOrBlank()) {
|
||||||
|
binding.textViewDescription.setText(R.string.no_description)
|
||||||
|
} else {
|
||||||
|
binding.textViewDescription.text = description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onHistoryChanged(history: MangaHistory?) {
|
private fun onHistoryChanged(history: MangaHistory?) {
|
||||||
with(binding.buttonRead) {
|
with(binding.buttonRead) {
|
||||||
if (history == null) {
|
if (history == null) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.koitharu.kotatsu.details.ui
|
package org.koitharu.kotatsu.details.ui
|
||||||
|
|
||||||
|
import android.text.Html
|
||||||
|
import androidx.core.text.parseAsHtml
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
@@ -43,6 +45,7 @@ class DetailsViewModel(
|
|||||||
private val bookmarksRepository: BookmarksRepository,
|
private val bookmarksRepository: BookmarksRepository,
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
private val scrobbler: Scrobbler,
|
private val scrobbler: Scrobbler,
|
||||||
|
private val imageGetter: Html.ImageGetter,
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
private val delegate = MangaDetailsDelegate(
|
private val delegate = MangaDetailsDelegate(
|
||||||
@@ -79,7 +82,19 @@ class DetailsViewModel(
|
|||||||
|
|
||||||
val bookmarks = delegate.manga.flatMapLatest {
|
val bookmarks = delegate.manga.flatMapLatest {
|
||||||
if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList())
|
if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList())
|
||||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
|
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||||
|
|
||||||
|
val description = delegate.manga
|
||||||
|
.distinctUntilChangedBy { it?.description.orEmpty() }
|
||||||
|
.transformLatest {
|
||||||
|
val description = it?.description
|
||||||
|
if (description.isNullOrEmpty()) {
|
||||||
|
emit(null)
|
||||||
|
} else {
|
||||||
|
emit(description.parseAsHtml())
|
||||||
|
emit(description.parseAsHtml(imageGetter = imageGetter))
|
||||||
|
}
|
||||||
|
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, null)
|
||||||
|
|
||||||
val onMangaRemoved = SingleLiveEvent<Manga>()
|
val onMangaRemoved = SingleLiveEvent<Manga>()
|
||||||
val isScrobblingAvailable: Boolean
|
val isScrobblingAvailable: Boolean
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import java.io.InputStream
|
|||||||
class PagesCache(context: Context) {
|
class PagesCache(context: Context) {
|
||||||
|
|
||||||
private val cacheDir = context.externalCacheDir ?: context.cacheDir
|
private val cacheDir = context.externalCacheDir ?: context.cacheDir
|
||||||
private val lruCache = DiskLruCache.create(
|
private val lruCache = createDiskLruCacheSafe(
|
||||||
cacheDir.subdir(CacheDir.PAGES.dir),
|
dir = cacheDir.subdir(CacheDir.PAGES.dir),
|
||||||
FileSize.MEGABYTES.convert(200, FileSize.BYTES),
|
size = FileSize.MEGABYTES.convert(200, FileSize.BYTES),
|
||||||
)
|
)
|
||||||
|
|
||||||
operator fun get(url: String): File? {
|
operator fun get(url: String): File? {
|
||||||
@@ -60,4 +60,14 @@ class PagesCache(context: Context) {
|
|||||||
progress.value = (bytesCopied.toDouble() / contentLength.toDouble()).toFloat()
|
progress.value = (bytesCopied.toDouble() / contentLength.toDouble()).toFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createDiskLruCacheSafe(dir: File, size: Long): DiskLruCache {
|
||||||
|
return try {
|
||||||
|
DiskLruCache.create(dir, size)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
dir.deleteRecursively()
|
||||||
|
dir.mkdir()
|
||||||
|
DiskLruCache.create(dir, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
|
|||||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||||
import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity
|
import org.koitharu.kotatsu.scrobbling.data.ScrobblingEntity
|
||||||
import org.koitharu.kotatsu.scrobbling.domain.model.*
|
import org.koitharu.kotatsu.scrobbling.domain.model.*
|
||||||
import org.koitharu.kotatsu.utils.ext.findKey
|
import org.koitharu.kotatsu.utils.ext.findKeyByValue
|
||||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||||
|
|
||||||
abstract class Scrobbler(
|
abstract class Scrobbler(
|
||||||
@@ -59,7 +59,7 @@ abstract class Scrobbler(
|
|||||||
scrobbler = scrobblerService,
|
scrobbler = scrobblerService,
|
||||||
mangaId = mangaId,
|
mangaId = mangaId,
|
||||||
targetId = targetId,
|
targetId = targetId,
|
||||||
status = statuses.findKey(status),
|
status = statuses.findKeyByValue(status),
|
||||||
chapter = chapter,
|
chapter = chapter,
|
||||||
comment = comment,
|
comment = comment,
|
||||||
rating = rating,
|
rating = rating,
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.annotation.MainThread
|
import androidx.annotation.MainThread
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -23,9 +23,6 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.NoSuchAlgorithmException
|
|
||||||
import java.security.cert.CertificateEncodingException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -74,7 +71,8 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
|
|||||||
.setTitle(R.string.app_update_available)
|
.setTitle(R.string.app_update_available)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(R.string.download) { _, _ ->
|
.setPositiveButton(R.string.download) { _, _ ->
|
||||||
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(version.apkUrl)))
|
val intent = Intent(Intent.ACTION_VIEW, version.apkUrl.toUri())
|
||||||
|
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.open_in_browser)))
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.close, null)
|
.setNegativeButton(R.string.close, null)
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
@@ -88,42 +86,23 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
|
|||||||
private val PERIOD = TimeUnit.HOURS.toMillis(6)
|
private val PERIOD = TimeUnit.HOURS.toMillis(6)
|
||||||
|
|
||||||
fun isUpdateSupported(context: Context): Boolean {
|
fun isUpdateSupported(context: Context): Boolean {
|
||||||
return getCertificateSHA1Fingerprint(context) == CERT_SHA1
|
return BuildConfig.DEBUG || getCertificateSHA1Fingerprint(context) == CERT_SHA1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@SuppressLint("PackageManagerGetSignatures")
|
@SuppressLint("PackageManagerGetSignatures")
|
||||||
private fun getCertificateSHA1Fingerprint(context: Context): String? {
|
private fun getCertificateSHA1Fingerprint(context: Context): String? = runCatching {
|
||||||
val packageInfo = try {
|
val packageInfo = context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)
|
||||||
context.packageManager.getPackageInfo(
|
val signatures = requireNotNull(packageInfo?.signatures)
|
||||||
context.packageName,
|
val cert: ByteArray = signatures.first().toByteArray()
|
||||||
PackageManager.GET_SIGNATURES
|
|
||||||
)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
e.printStackTraceDebug()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val signatures = packageInfo?.signatures
|
|
||||||
val cert: ByteArray = signatures?.firstOrNull()?.toByteArray() ?: return null
|
|
||||||
val input: InputStream = ByteArrayInputStream(cert)
|
val input: InputStream = ByteArrayInputStream(cert)
|
||||||
val c = try {
|
val cf = CertificateFactory.getInstance("X509")
|
||||||
val cf = CertificateFactory.getInstance("X509")
|
val c = cf.generateCertificate(input) as X509Certificate
|
||||||
cf.generateCertificate(input) as X509Certificate
|
val md: MessageDigest = MessageDigest.getInstance("SHA1")
|
||||||
} catch (e: CertificateException) {
|
val publicKey: ByteArray = md.digest(c.encoded)
|
||||||
e.printStackTraceDebug()
|
return publicKey.byte2HexFormatted()
|
||||||
return null
|
}.onFailure { error ->
|
||||||
}
|
error.printStackTraceDebug()
|
||||||
return try {
|
}.getOrNull()
|
||||||
val md: MessageDigest = MessageDigest.getInstance("SHA1")
|
|
||||||
val publicKey: ByteArray = md.digest(c.encoded)
|
|
||||||
publicKey.byte2HexFormatted()
|
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
|
||||||
e.printStackTraceDebug()
|
|
||||||
null
|
|
||||||
} catch (e: CertificateEncodingException) {
|
|
||||||
e.printStackTraceDebug()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,25 +3,25 @@ package org.koitharu.kotatsu.settings
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||||
|
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
|
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
|
||||||
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.ext.printStackTraceDebug
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
import org.koitharu.kotatsu.utils.ext.serializableArgument
|
|
||||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
|
||||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
|
||||||
|
|
||||||
class SourceSettingsFragment : BasePreferenceFragment(0) {
|
class SourceSettingsFragment : BasePreferenceFragment(0) {
|
||||||
|
|
||||||
private val source by serializableArgument<MangaSource>(EXTRA_SOURCE)
|
private val source by serializableArgument<MangaSource>(EXTRA_SOURCE)
|
||||||
private var repository: RemoteMangaRepository? = null
|
private var repository: RemoteMangaRepository? = null
|
||||||
|
private val exceptionResolver = ExceptionResolver(this)
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
@@ -63,6 +63,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0) {
|
|||||||
|
|
||||||
private fun loadUsername(preference: Preference) = viewLifecycleScope.launch {
|
private fun loadUsername(preference: Preference) = viewLifecycleScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
preference.summary = null
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
requireNotNull(repository?.getAuthProvider()?.getUsername())
|
requireNotNull(repository?.getAuthProvider()?.getUsername())
|
||||||
}
|
}
|
||||||
@@ -70,10 +71,28 @@ class SourceSettingsFragment : BasePreferenceFragment(0) {
|
|||||||
preference.title = getString(R.string.logged_in_as, username)
|
preference.title = getString(R.string.logged_in_as, username)
|
||||||
}.onFailure { error ->
|
}.onFailure { error ->
|
||||||
preference.isEnabled = error is AuthRequiredException
|
preference.isEnabled = error is AuthRequiredException
|
||||||
|
when {
|
||||||
|
error is AuthRequiredException -> Unit
|
||||||
|
ExceptionResolver.canResolve(error) -> {
|
||||||
|
Snackbar.make(listView, error.getDisplayMessage(resources), Snackbar.LENGTH_INDEFINITE)
|
||||||
|
.setAction(ExceptionResolver.getResolveStringId(error)) { resolveError(error) }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
else -> preference.summary = error.getDisplayMessage(resources)
|
||||||
|
}
|
||||||
error.printStackTraceDebug()
|
error.printStackTraceDebug()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveError(error: Throwable): Unit {
|
||||||
|
viewLifecycleScope.launch {
|
||||||
|
if (exceptionResolver.resolve(error)) {
|
||||||
|
val pref = findPreference<Preference>(KEY_AUTH) ?: return@launch
|
||||||
|
loadUsername(pref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val KEY_AUTH = "auth"
|
private const val KEY_AUTH = "auth"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package org.koitharu.kotatsu.settings.newsources
|
package org.koitharu.kotatsu.settings.newsources
|
||||||
|
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.model.getLocaleTitle
|
import org.koitharu.kotatsu.core.model.getLocaleTitle
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||||
|
import org.koitharu.kotatsu.utils.ext.mapToSet
|
||||||
|
|
||||||
class NewSourcesViewModel(
|
class NewSourcesViewModel(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
@@ -30,12 +32,14 @@ class NewSourcesViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun buildList() {
|
private fun buildList() {
|
||||||
|
val locales = LocaleListCompat.getDefault().mapToSet { it.language }
|
||||||
val hidden = settings.hiddenSources
|
val hidden = settings.hiddenSources
|
||||||
sources.value = initialList.map {
|
sources.value = initialList.map {
|
||||||
|
val locale = it.locale
|
||||||
SourceConfigItem.SourceItem(
|
SourceConfigItem.SourceItem(
|
||||||
source = it,
|
source = it,
|
||||||
summary = it.getLocaleTitle(),
|
summary = it.getLocaleTitle(),
|
||||||
isEnabled = it.name !in hidden,
|
isEnabled = it.name !in hidden && (locale == null || locale in locales),
|
||||||
isDraggable = false,
|
isDraggable = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,25 +18,20 @@ inline fun <T> MutableSet(size: Int, init: (index: Int) -> T): MutableSet<T> {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> createSet(size: Int, init: (index: Int) -> T): Set<T> = when (size) {
|
@Suppress("FunctionName")
|
||||||
|
inline fun <T> Set(size: Int, init: (index: Int) -> T): Set<T> = when (size) {
|
||||||
0 -> emptySet()
|
0 -> emptySet()
|
||||||
1 -> Collections.singleton(init(0))
|
1 -> Collections.singleton(init(0))
|
||||||
else -> MutableSet(size, init)
|
else -> MutableSet(size, init)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> createList(size: Int, init: (index: Int) -> T): List<T> = when (size) {
|
|
||||||
0 -> emptyList()
|
|
||||||
1 -> Collections.singletonList(init(0))
|
|
||||||
else -> MutableList(size, init)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> List<T>.asArrayList(): ArrayList<T> = if (this is ArrayList<*>) {
|
fun <T> List<T>.asArrayList(): ArrayList<T> = if (this is ArrayList<*>) {
|
||||||
this as ArrayList<T>
|
this as ArrayList<T>
|
||||||
} else {
|
} else {
|
||||||
ArrayList(this)
|
ArrayList(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <K, V> Map<K, V>.findKey(value: V): K? {
|
fun <K, V> Map<K, V>.findKeyByValue(value: V): K? {
|
||||||
for ((k, v) in entries) {
|
for ((k, v) in entries) {
|
||||||
if (v == value) {
|
if (v == value) {
|
||||||
return k
|
return k
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
package org.koitharu.kotatsu.utils.ext
|
|
||||||
|
|
||||||
import androidx.core.os.LocaleListCompat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw kotlin.NoSuchElementException()
|
|
||||||
|
|
||||||
fun LocaleListCompat.toList(): List<Locale> = createList(size()) { i -> getOrThrow(i) }
|
|
||||||
|
|
||||||
operator fun LocaleListCompat.iterator() = object : Iterator<Locale> {
|
|
||||||
private var index = 0
|
|
||||||
override fun hasNext(): Boolean = index < size()
|
|
||||||
override fun next(): Locale = getOrThrow(index++)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <R, C : MutableCollection<in R>> LocaleListCompat.mapTo(
|
|
||||||
destination: C,
|
|
||||||
block: (Locale) -> R,
|
|
||||||
): C {
|
|
||||||
val len = size()
|
|
||||||
for (i in 0 until len) {
|
|
||||||
val item = get(i) ?: continue
|
|
||||||
destination.add(block(item))
|
|
||||||
}
|
|
||||||
return destination
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T> LocaleListCompat.map(block: (Locale) -> T): List<T> {
|
|
||||||
return mapTo(ArrayList(size()), block)
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T> LocaleListCompat.mapToSet(block: (Locale) -> T): Set<T> {
|
|
||||||
return mapTo(LinkedHashSet(size()), block)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.ext
|
||||||
|
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
operator fun LocaleListCompat.iterator(): ListIterator<Locale> = LocaleListCompatIterator(this)
|
||||||
|
|
||||||
|
fun LocaleListCompat.toList(): List<Locale> = List(size()) { i -> getOrThrow(i) }
|
||||||
|
|
||||||
|
inline fun <T> LocaleListCompat.map(block: (Locale) -> T): List<T> {
|
||||||
|
return List(size()) { i -> block(getOrThrow(i)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> LocaleListCompat.mapToSet(block: (Locale) -> T): Set<T> {
|
||||||
|
return Set(size()) { i -> block(getOrThrow(i)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw NoSuchElementException()
|
||||||
|
|
||||||
|
private class LocaleListCompatIterator(private val list: LocaleListCompat) : ListIterator<Locale> {
|
||||||
|
|
||||||
|
private var index = 0
|
||||||
|
|
||||||
|
override fun hasNext() = index < list.size()
|
||||||
|
|
||||||
|
override fun hasPrevious() = index > 0
|
||||||
|
|
||||||
|
override fun next() = list.get(index++) ?: throw NoSuchElementException()
|
||||||
|
|
||||||
|
override fun nextIndex() = index
|
||||||
|
|
||||||
|
override fun previous() = list.get(--index) ?: throw NoSuchElementException()
|
||||||
|
|
||||||
|
override fun previousIndex() = index - 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.image
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.text.Html
|
||||||
|
import coil.ImageLoader
|
||||||
|
import coil.executeBlocking
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
|
||||||
|
class CoilImageGetter(
|
||||||
|
private val context: Context,
|
||||||
|
private val coil: ImageLoader,
|
||||||
|
) : Html.ImageGetter {
|
||||||
|
|
||||||
|
override fun getDrawable(source: String?): Drawable? {
|
||||||
|
return coil.executeBlocking(
|
||||||
|
ImageRequest.Builder(context)
|
||||||
|
.data(source)
|
||||||
|
.allowHardware(false)
|
||||||
|
.build()
|
||||||
|
).drawable?.apply {
|
||||||
|
setBounds(0, 0, intrinsicHeight, intrinsicHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -302,4 +302,16 @@
|
|||||||
<string name="use_fingerprint">Fingerabdruck verwenden, falls vorhanden</string>
|
<string name="use_fingerprint">Fingerabdruck verwenden, falls vorhanden</string>
|
||||||
<string name="appwidget_shelf_description">Manga aus Ihren Favoriten</string>
|
<string name="appwidget_shelf_description">Manga aus Ihren Favoriten</string>
|
||||||
<string name="appwidget_recent_description">Ihr kürzlich gelesener Manga</string>
|
<string name="appwidget_recent_description">Ihr kürzlich gelesener Manga</string>
|
||||||
|
<string name="report">Melden</string>
|
||||||
|
<string name="tracking">Nachverfolgung</string>
|
||||||
|
<string name="logout">Abmelden</string>
|
||||||
|
<string name="status_planned">Geplant</string>
|
||||||
|
<string name="status_on_hold">In der Warteschleife</string>
|
||||||
|
<string name="show_reading_indicators">Indikatoren für den Lesefortschritt anzeigen</string>
|
||||||
|
<string name="show_all">Alle anzeigen</string>
|
||||||
|
<string name="show_reading_indicators_summary">Gelesenen Prozentsatz in Verlauf und Favoriten anzeigen</string>
|
||||||
|
<string name="clear_cookies_summary">Kann im Falle einiger Probleme helfen. Alle Berechtigungen werden für ungültig erklärt</string>
|
||||||
|
<string name="status_completed">Abgeschlossen</string>
|
||||||
|
<string name="exclude_nsfw_from_history_summary">Manga, die als NSFW markiert sind, werden nicht in den Verlauf aufgenommen und Ihr Fortschritt wird nicht gespeichert.</string>
|
||||||
|
<string name="data_deletion">Datenlöschung</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -301,4 +301,5 @@
|
|||||||
<string name="use_fingerprint">Utilizar la huella dactilar si está disponible</string>
|
<string name="use_fingerprint">Utilizar la huella dactilar si está disponible</string>
|
||||||
<string name="appwidget_shelf_description">Mangas de tus favoritos</string>
|
<string name="appwidget_shelf_description">Mangas de tus favoritos</string>
|
||||||
<string name="appwidget_recent_description">Sus mangas recientemente leídos</string>
|
<string name="appwidget_recent_description">Sus mangas recientemente leídos</string>
|
||||||
|
<string name="logout">Cerrar sesión</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -302,4 +302,10 @@
|
|||||||
<string name="use_fingerprint">Käytä sormenjälkeä, jos käytettävissä</string>
|
<string name="use_fingerprint">Käytä sormenjälkeä, jos käytettävissä</string>
|
||||||
<string name="appwidget_shelf_description">Manga suosikeistasi</string>
|
<string name="appwidget_shelf_description">Manga suosikeistasi</string>
|
||||||
<string name="appwidget_recent_description">Äskettäin lukemasi manga</string>
|
<string name="appwidget_recent_description">Äskettäin lukemasi manga</string>
|
||||||
|
<string name="tracking">Seuranta</string>
|
||||||
|
<string name="logout">Kirjaudu ulos</string>
|
||||||
|
<string name="status_reading">Lukemassa</string>
|
||||||
|
<string name="status_re_reading">Lukemassa uudelleen</string>
|
||||||
|
<string name="data_deletion">Tietojen poistaminen</string>
|
||||||
|
<string name="show_all">Näytä kaikki</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -302,4 +302,19 @@
|
|||||||
<string name="use_fingerprint">Utiliser l\'empreinte digitale si elle est disponible</string>
|
<string name="use_fingerprint">Utiliser l\'empreinte digitale si elle est disponible</string>
|
||||||
<string name="appwidget_recent_description">Vos mangas récemment lus</string>
|
<string name="appwidget_recent_description">Vos mangas récemment lus</string>
|
||||||
<string name="appwidget_shelf_description">Les mangas de vos favoris</string>
|
<string name="appwidget_shelf_description">Les mangas de vos favoris</string>
|
||||||
|
<string name="report">Signaler</string>
|
||||||
|
<string name="tracking">Suivi</string>
|
||||||
|
<string name="status_planned">Planifié</string>
|
||||||
|
<string name="status_reading">Lecture</string>
|
||||||
|
<string name="show_reading_indicators">Afficher les indicateurs de progression de lecture</string>
|
||||||
|
<string name="show_reading_indicators_summary">Afficher le pourcentage de lecture dans l\'historique et les favoris</string>
|
||||||
|
<string name="exclude_nsfw_from_history_summary">Les mangas marqués comme étant pour adultes ne seront jamais ajoutés à l\'historique et votre progression ne sera pas sauvegardée</string>
|
||||||
|
<string name="clear_cookies_summary">Peut aider en cas de problème. Toutes les autorisations seront invalidées</string>
|
||||||
|
<string name="show_all">Tout afficher</string>
|
||||||
|
<string name="status_on_hold">En attente</string>
|
||||||
|
<string name="status_dropped">Abandonné</string>
|
||||||
|
<string name="data_deletion">Suppression des données</string>
|
||||||
|
<string name="logout">Se déconnecter</string>
|
||||||
|
<string name="status_completed">Terminé</string>
|
||||||
|
<string name="status_re_reading">Relecture</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -302,4 +302,19 @@
|
|||||||
<string name="use_fingerprint">Usa le impronte digitali se disponibili</string>
|
<string name="use_fingerprint">Usa le impronte digitali se disponibili</string>
|
||||||
<string name="appwidget_shelf_description">Manga dai preferiti</string>
|
<string name="appwidget_shelf_description">Manga dai preferiti</string>
|
||||||
<string name="appwidget_recent_description">I manga letti di recente</string>
|
<string name="appwidget_recent_description">I manga letti di recente</string>
|
||||||
|
<string name="report">Segnala</string>
|
||||||
|
<string name="tracking">Tracciamento</string>
|
||||||
|
<string name="status_reading">Lettura</string>
|
||||||
|
<string name="status_re_reading">Rilettura</string>
|
||||||
|
<string name="status_on_hold">In attesa</string>
|
||||||
|
<string name="show_reading_indicators">Mostrare gli indicatori di progresso della lettura</string>
|
||||||
|
<string name="data_deletion">Eliminazione dei dati</string>
|
||||||
|
<string name="show_reading_indicators_summary">Mostra la percentuale di lettura nella cronologia e nei preferiti</string>
|
||||||
|
<string name="exclude_nsfw_from_history_summary">I manga contrassegnati come per adulti non verranno mai aggiunti alla cronologia e i vostri progressi non verranno salvati</string>
|
||||||
|
<string name="clear_cookies_summary">Può aiutare in caso di problemi. Tutte le autorizzazioni saranno invalidate</string>
|
||||||
|
<string name="show_all">Mostra tutto</string>
|
||||||
|
<string name="logout">Esci</string>
|
||||||
|
<string name="status_planned">Pianificato</string>
|
||||||
|
<string name="status_completed">Finito</string>
|
||||||
|
<string name="status_dropped">Abbandonato</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -303,4 +303,18 @@
|
|||||||
<string name="use_fingerprint">指紋がある場合は、指紋を使用する</string>
|
<string name="use_fingerprint">指紋がある場合は、指紋を使用する</string>
|
||||||
<string name="appwidget_shelf_description">お気に入りの漫画</string>
|
<string name="appwidget_shelf_description">お気に入りの漫画</string>
|
||||||
<string name="report">報告</string>
|
<string name="report">報告</string>
|
||||||
|
<string name="status_reading">読書</string>
|
||||||
|
<string name="status_re_reading">再読込</string>
|
||||||
|
<string name="status_completed">完了</string>
|
||||||
|
<string name="status_on_hold">保留中</string>
|
||||||
|
<string name="tracking">追跡</string>
|
||||||
|
<string name="logout">ログアウト</string>
|
||||||
|
<string name="status_planned">予定</string>
|
||||||
|
<string name="status_dropped">ドロップ</string>
|
||||||
|
<string name="data_deletion">データの削除</string>
|
||||||
|
<string name="show_reading_indicators_summary">履歴とお気に入りに既読率を表示する</string>
|
||||||
|
<string name="clear_cookies_summary">いくつかの問題の場合に助けることができる。すべての認証が無効になります</string>
|
||||||
|
<string name="show_reading_indicators">読書の進行状況インジケーターを表示</string>
|
||||||
|
<string name="exclude_nsfw_from_history_summary">NSFWとマークされたマンガは履歴に追加されず、進行状況も保存されない</string>
|
||||||
|
<string name="show_all">すべて表示</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -303,4 +303,18 @@
|
|||||||
<string name="appwidget_shelf_description">Favorilerinizden mangalar</string>
|
<string name="appwidget_shelf_description">Favorilerinizden mangalar</string>
|
||||||
<string name="appwidget_recent_description">Son okuduğunuz mangalar</string>
|
<string name="appwidget_recent_description">Son okuduğunuz mangalar</string>
|
||||||
<string name="report">Bildir</string>
|
<string name="report">Bildir</string>
|
||||||
|
<string name="tracking">İzleme</string>
|
||||||
|
<string name="logout">Oturumu kapat</string>
|
||||||
|
<string name="status_reading">Okunuyor</string>
|
||||||
|
<string name="status_completed">Tamamlandı</string>
|
||||||
|
<string name="show_reading_indicators">Okuma ilerleme göstergelerini göster</string>
|
||||||
|
<string name="data_deletion">Verileri sil</string>
|
||||||
|
<string name="show_reading_indicators_summary">Geçmişte ve favorilerde okunma yüzdesini göster</string>
|
||||||
|
<string name="exclude_nsfw_from_history_summary">Uygunsuz olarak işaretlenen mangalar asla geçmişe eklenmeyecek ve ilerlemeniz kaydedilmeyecektir</string>
|
||||||
|
<string name="clear_cookies_summary">Bazı sorunlarda yardımcı olabilir. Tüm yetkilendirmeler geçersiz kılınacaktır</string>
|
||||||
|
<string name="status_on_hold">Beklemede</string>
|
||||||
|
<string name="status_dropped">Bırakıldı</string>
|
||||||
|
<string name="status_planned">Planlandı</string>
|
||||||
|
<string name="status_re_reading">Yeniden okunuyor</string>
|
||||||
|
<string name="show_all">Tümünü göster</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -56,9 +56,4 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="cookies_clear"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/clear_cookies" />
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user