Compare commits

...

39 Commits
v8.0 ... v8.1

Author SHA1 Message Date
Koitharu
e3e315e2a6 Skip local directories with .notamanga file 2025-03-30 10:43:05 +03:00
Koitharu
bfc733784f Option to disable dangerous genres highlighting #1348 2025-03-30 10:34:27 +03:00
Koitharu
3ff25de252 Fix crash 2025-03-30 10:21:42 +03:00
Koitharu
3c726c1c56 Update dependencies 2025-03-30 10:21:42 +03:00
Koitharu
9cb7ff691f Merge pull request #1349 from weblate/weblate-kotatsu-strings
Translations update from Hosted Weblate
2025-03-30 10:21:06 +03:00
lenn
645ae3124f Translated using Weblate (Polish)
Currently translated at 96.0% (780 of 812 strings)

Co-authored-by: lenn <l3ennec@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pl/
Translation: Kotatsu/Strings
2025-03-29 14:02:13 +01:00
Ore Ki
a3d1922913 Translated using Weblate (Indonesian)
Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Ore Ki <ramadrizkyyy@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/id/
Translation: Kotatsu/Strings
2025-03-29 14:02:11 +01:00
gekka
62d2ea8f15 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.6% (809 of 812 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.5% (808 of 812 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2025-03-29 14:02:09 +01:00
Draken
823752076b Translated using Weblate (Vietnamese)
Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Draken <premieregirl26@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/vi/
Translation: Kotatsu/Strings
2025-03-29 14:02:06 +01:00
Lorenzo Stella
3cbd392c72 Translated using Weblate (English)
Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (English (United Kingdom))

Currently translated at 2.4% (20 of 812 strings)

Translated using Weblate (English)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (English)

Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Lorenzo Stella <lorenzo.stella.1408@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/en/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/en_GB/
Translation: Kotatsu/Strings
2025-03-29 14:02:04 +01:00
Anonymous
57f62f5860 Translated using Weblate (Malayalam)
Currently translated at 2.8% (23 of 812 strings)

Translated using Weblate (Sinhala)

Currently translated at 2.7% (22 of 812 strings)

Translated using Weblate (Croatian)

Currently translated at 97.5% (792 of 812 strings)

Translated using Weblate (Hungarian)

Currently translated at 76.3% (620 of 812 strings)

Translated using Weblate (Malay)

Currently translated at 38.4% (312 of 812 strings)

Translated using Weblate (Estonian)

Currently translated at 67.6% (549 of 812 strings)

Translated using Weblate (Thai)

Currently translated at 55.7% (453 of 812 strings)

Translated using Weblate (Czech)

Currently translated at 98.0% (796 of 812 strings)

Translated using Weblate (Vietnamese)

Currently translated at 99.1% (805 of 812 strings)

Translated using Weblate (Kazakh)

Currently translated at 66.9% (544 of 812 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 46.9% (381 of 812 strings)

Translated using Weblate (Filipino)

Currently translated at 98.2% (798 of 812 strings)

Translated using Weblate (Hindi)

Currently translated at 82.2% (668 of 812 strings)

Translated using Weblate (Polish)

Currently translated at 93.1% (756 of 812 strings)

Translated using Weblate (Korean)

Currently translated at 41.9% (341 of 812 strings)

Translated using Weblate (Greek)

Currently translated at 66.2% (538 of 812 strings)

Translated using Weblate (Serbian)

Currently translated at 97.9% (795 of 812 strings)

Translated using Weblate (Arabic)

Currently translated at 98.7% (802 of 812 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 80.9% (657 of 812 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.0% (804 of 812 strings)

Translated using Weblate (Ukrainian)

Currently translated at 81.6% (663 of 812 strings)

Translated using Weblate (Russian)

Currently translated at 98.5% (800 of 812 strings)

Translated using Weblate (Japanese)

Currently translated at 57.1% (464 of 812 strings)

Translated using Weblate (Turkish)

Currently translated at 99.6% (809 of 812 strings)

Translated using Weblate (French)

Currently translated at 99.6% (809 of 812 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.6% (785 of 812 strings)

Translated using Weblate (Italian)

Currently translated at 99.5% (808 of 812 strings)

Translated using Weblate (German)

Currently translated at 78.3% (636 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 94.9% (771 of 812 strings)

Translated using Weblate (Belarusian)

Currently translated at 92.1% (748 of 812 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/de/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/el/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/et/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hu/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/kk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ml/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/nn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pl/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/si/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/th/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/vi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hant/
Translation: Kotatsu/Strings
2025-03-29 14:02:02 +01:00
Koitharu
648fab6be5 Fix Open online manga variant 2025-03-27 20:39:36 +02:00
Koitharu
817ae68e67 Option to toggle ssiv debug 2025-03-26 19:24:32 +02:00
Koitharu
7c4b91ddc4 UI Fixes 2025-03-26 19:23:06 +02:00
Koitharu
d54e015195 Fix Favorite/Favourite strings 2025-03-25 10:08:16 +02:00
Koitharu
e369d1ba9d Merge remote-tracking branch 'weblate/devel' into devel 2025-03-25 10:01:27 +02:00
Koitharu
1a4358998b Merge pull request #1334 from Arararararagi/patch-1
typos and coherence check in strings
2025-03-25 09:47:37 +02:00
MrCrystallized
c53a833d9d Translated using Weblate (Bengali)
Currently translated at 22.1% (180 of 812 strings)

Translated using Weblate (Bengali)

Currently translated at 88.8% (8 of 9 strings)

Co-authored-by: MrCrystallized <mrcrystallizedbruh@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/bn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/bn/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2025-03-24 21:54:54 +01:00
Lorenzo Stella
afff700ad3 Translated using Weblate (Romanian)
Currently translated at 11.0% (90 of 812 strings)

Translated using Weblate (Khmer (Central))

Currently translated at 17.7% (144 of 812 strings)

Translated using Weblate (Latvian)

Currently translated at 21.6% (176 of 812 strings)

Translated using Weblate (Lithuanian)

Currently translated at 5.5% (45 of 812 strings)

Translated using Weblate (Catalan)

Currently translated at 10.9% (89 of 812 strings)

Translated using Weblate (Hungarian)

Currently translated at 76.6% (622 of 812 strings)

Translated using Weblate (Punjabi)

Currently translated at 4.1% (34 of 812 strings)

Translated using Weblate (Malay)

Currently translated at 38.6% (314 of 812 strings)

Translated using Weblate (Thai)

Currently translated at 56.0% (455 of 812 strings)

Translated using Weblate (Kazakh)

Currently translated at 67.2% (546 of 812 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 47.1% (383 of 812 strings)

Translated using Weblate (Nepali)

Currently translated at 30.5% (248 of 812 strings)

Translated using Weblate (Hindi)

Currently translated at 82.6% (671 of 812 strings)

Translated using Weblate (Polish)

Currently translated at 93.5% (760 of 812 strings)

Translated using Weblate (Korean)

Currently translated at 42.2% (343 of 812 strings)

Translated using Weblate (Greek)

Currently translated at 66.5% (540 of 812 strings)

Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 81.2% (660 of 812 strings)

Translated using Weblate (Ukrainian)

Currently translated at 82.0% (666 of 812 strings)

Translated using Weblate (Japanese)

Currently translated at 57.3% (466 of 812 strings)

Translated using Weblate (Persian)

Currently translated at 35.5% (289 of 812 strings)

Translated using Weblate (Finnish)

Currently translated at 32.0% (260 of 812 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (812 of 812 strings)

Translated using Weblate (German)

Currently translated at 78.6% (639 of 812 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 35.4% (288 of 812 strings)

Translated using Weblate (Spanish)

Currently translated at 95.4% (775 of 812 strings)

Translated using Weblate (English)

Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Lorenzo Stella <lorenzo.stella.1408@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ca/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/de/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/el/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/en/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fa/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hu/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/kk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/km/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/lt/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/lv/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ne/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/nn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pa/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pl/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ro/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/th/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hant/
Translation: Kotatsu/Strings
2025-03-24 21:54:54 +01:00
Anon
5bc00bc7f5 Translated using Weblate (Serbian)
Currently translated at 98.2% (798 of 812 strings)

Co-authored-by: Anon <anonymousprivate76@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translation: Kotatsu/Strings
2025-03-24 21:54:54 +01:00
Dragibus Noir
e2ace90cdb Translated using Weblate (French)
Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Dragibus Noir <dragibusnoir@pm.me>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fr/
Translation: Kotatsu/Strings
2025-03-24 21:54:54 +01:00
Lumiini
1afbd2b6a8 Translated using Weblate (Finnish)
Currently translated at 66.6% (6 of 9 strings)

Co-authored-by: Lumiini <bennokaynak@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/fi/
Translation: Kotatsu/plurals
2025-03-24 21:54:53 +01:00
Frosted
d36c5af0c4 Translated using Weblate (Turkish)
Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translation: Kotatsu/Strings
2025-03-24 21:54:53 +01:00
Nicola Bortoletto
705bb2b084 Translated using Weblate (Italian)
Currently translated at 100.0% (812 of 812 strings)

Co-authored-by: Nicola Bortoletto <nicola.bortoletto@live.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translation: Kotatsu/Strings
2025-03-24 21:54:53 +01:00
gekka
a208d13930 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (808 of 812 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2025-03-24 21:54:53 +01:00
Kusou
44d8861b7f Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (808 of 808 strings)

Co-authored-by: Kusou <orion26br@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2025-03-24 21:54:53 +01:00
ruban s
9821f06ca1 Translated using Weblate (Tamil)
Currently translated at 100.0% (808 of 808 strings)

Co-authored-by: ruban s <rubans013@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ta/
Translation: Kotatsu/Strings
2025-03-24 21:54:52 +01:00
Amato Fugosi
92f9f56f59 Translated using Weblate (Bengali)
Currently translated at 19.9% (161 of 808 strings)

Translated using Weblate (Bengali)

Currently translated at 88.8% (8 of 9 strings)

Co-authored-by: Amato Fugosi <fugosiamato@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/bn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/bn/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2025-03-24 21:54:52 +01:00
Koitharu
424c4d8827 Multiple authors support 2025-03-24 14:18:04 +02:00
Koitharu
24cf2a2725 Use page preview in reader while loading 2025-03-22 16:18:35 +02:00
Koitharu
1a5c3c1f6f Update dependencies 2025-03-21 09:03:31 +02:00
Koitharu
0b8fbf892a Improve details activity 2025-03-20 18:00:58 +02:00
Koitharu
a2f9356b8a Fix crashes 2025-03-20 08:20:43 +02:00
Koitharu
7003463bac Option to use disabled sources for suggestions 2025-03-20 07:52:08 +02:00
Koitharu
7a663fa9c1 Merge branch 'devel' 2025-03-18 19:36:42 +02:00
I bit my tongue
9cc1cdac62 typos and coherence check 2025-03-16 21:13:54 +00:00
Koitharu
8d7f44d2da Fix DateTimeAgo formatting
(cherry picked from commit 5f0514638a)
2025-02-24 18:09:03 +02:00
Koitharu
930d4dfd83 Fix checking for removed manga updates (close #1064)
(cherry picked from commit 1b8d35d424)
2025-02-24 18:07:49 +02:00
Koitharu
290cb652ee Update parsers 2025-02-24 18:05:51 +02:00
124 changed files with 833 additions and 364 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 35
versionCode = 1004
versionName = '8.0'
versionCode = 1005
versionName = '8.1'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {

View File

@@ -12,7 +12,6 @@ import org.koitharu.kotatsu.local.data.LocalMangaRepository
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.reader.domain.PageLoader
import org.koitharu.kotatsu.reader.ui.ReaderViewModel
class KotatsuApp : BaseApp() {
@@ -67,7 +66,6 @@ class KotatsuApp : BaseApp() {
setClassInstanceLimit(PagesCache::class.java, 1)
setClassInstanceLimit(MangaLoaderContext::class.java, 1)
setClassInstanceLimit(PageLoader::class.java, 1)
setClassInstanceLimit(ReaderViewModel::class.java, 1)
penaltyLog()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {
penaltyListener(notifier.executor, notifier)

View File

@@ -5,6 +5,7 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import leakcanary.LeakCanary
import org.koitharu.kotatsu.KotatsuApp
import org.koitharu.kotatsu.R
@@ -24,6 +25,7 @@ class SettingsMenuProvider(
override fun onPrepareMenu(menu: Menu) {
super.onPrepareMenu(menu)
menu.findItem(R.id.action_leakcanary).isChecked = application.isLeakCanaryEnabled
menu.findItem(R.id.action_ssiv_debug).isChecked = SubsamplingScaleImageView.isDebug
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
@@ -44,6 +46,13 @@ class SettingsMenuProvider(
true
}
R.id.action_ssiv_debug -> {
val checked = !menuItem.isChecked
menuItem.isChecked = checked
SubsamplingScaleImageView.isDebug = checked
true
}
else -> false
}
}

View File

@@ -4,6 +4,12 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/action_ssiv_debug"
android:checkable="true"
android:title="SSIV debug"
app:showAsAction="never"
tools:ignore="HardcodedText" />
<item
android:id="@+id/action_leakcanary"
android:checkable="true"

View File

@@ -28,8 +28,11 @@ abstract class BaseBrowserActivity : BaseActivity<ActivityBrowserBinding>(), Bro
viewBinding.webView.webChromeClient = ProgressChromeClient(viewBinding.progressBar)
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView)
onBackPressedDispatcher.addCallback(onBackPressedCallback)
onCreate2(savedInstanceState)
}
protected abstract fun onCreate2(savedInstanceState: Bundle?)
override fun onApplyWindowInsets(
v: View,
insets: WindowInsetsCompat

View File

@@ -25,8 +25,7 @@ class BrowserActivity : BaseBrowserActivity() {
@Inject
lateinit var mangaRepositoryFactory: MangaRepository.Factory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
override fun onCreate2(savedInstanceState: Bundle?) {
setDisplayHomeAsUp(true, true)
val mangaSource = MangaSource(intent?.getStringExtra(AppRouter.KEY_SOURCE))
val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository

View File

@@ -37,8 +37,7 @@ class CloudFlareActivity : BaseBrowserActivity(), CloudFlareCallback {
private lateinit var cfClient: CloudFlareClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
override fun onCreate2(savedInstanceState: Bundle?) {
setDisplayHomeAsUp(true, true)
val url = intent?.dataString
if (url.isNullOrEmpty()) {

View File

@@ -102,6 +102,9 @@ open class BaseApp : Application(), Configuration.Provider {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
if (ACRA.isACRASenderServiceProcess()) {
return
}
initAcra {
buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.JSON

View File

@@ -0,0 +1,48 @@
package org.koitharu.kotatsu.core.image
import android.os.Parcel
import android.os.Parcelable
import android.view.View
import androidx.collection.ArrayMap
import coil3.memory.MemoryCache
import coil3.request.SuccessResult
import coil3.util.CoilUtils
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
@Parcelize
class CoilMemoryCacheKey(
val data: MemoryCache.Key
) : Parcelable {
companion object : Parceler<CoilMemoryCacheKey> {
override fun CoilMemoryCacheKey.write(parcel: Parcel, flags: Int) = with(data) {
parcel.writeString(key)
parcel.writeInt(extras.size)
for (entry in extras.entries) {
parcel.writeString(entry.key)
parcel.writeString(entry.value)
}
}
override fun create(parcel: Parcel): CoilMemoryCacheKey = CoilMemoryCacheKey(
MemoryCache.Key(
key = parcel.readString().orEmpty(),
extras = run {
val size = parcel.readInt()
val map = ArrayMap<String, String>(size)
repeat(size) {
map.put(parcel.readString(), parcel.readString())
}
map
},
),
)
fun from(view: View): CoilMemoryCacheKey? {
return (CoilUtils.result(view) as? SuccessResult)?.memoryCacheKey?.let {
CoilMemoryCacheKey(it)
}
}
}
}

View File

@@ -18,11 +18,13 @@ import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource
import org.koitharu.kotatsu.core.util.ext.getDisplayName
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.toLocale
import org.koitharu.kotatsu.core.util.ext.toLocaleOrNull
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.splitTwoParts
import com.google.android.material.R as materialR
import java.util.Locale
import androidx.appcompat.R as appcompatR
data object LocalMangaSource : MangaSource {
override val name = "LOCAL"
@@ -79,6 +81,8 @@ tailrec fun MangaSource.unwrap(): MangaSource = if (this is MangaSourceInfo) {
this
}
fun MangaSource.getLocale(): Locale? = (unwrap() as? MangaParserSource)?.locale?.toLocaleOrNull()
fun MangaSource.getSummary(context: Context): String? = when (val source = unwrap()) {
is MangaParserSource -> {
val type = context.getString(source.contentType.titleResId)
@@ -99,7 +103,7 @@ fun MangaSource.getTitle(context: Context): String = when (val source = unwrap()
}
fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans(
ForegroundColorSpan(context.getThemeColor(materialR.attr.colorError, Color.RED)),
ForegroundColorSpan(context.getThemeColor(appcompatR.attr.colorError, Color.RED)),
RelativeSizeSpan(0.74f),
SuperscriptSpan(),
) {

View File

@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksActivity
import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.image.CoilMemoryCacheKey
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.MangaSourceInfo
import org.koitharu.kotatsu.core.model.appUrl
@@ -105,7 +106,7 @@ import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet
import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity
import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity
import java.io.File
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
class AppRouter private constructor(
private val activity: FragmentActivity?,
@@ -180,11 +181,12 @@ class AppRouter private constructor(
)
}
fun openImage(url: String, source: MangaSource?, anchor: View? = null) {
fun openImage(url: String, source: MangaSource?, anchor: View? = null, preview: CoilMemoryCacheKey? = null) {
startActivity(
Intent(contextOrNull(), ImageActivity::class.java)
.setData(url.toUri())
.putExtra(KEY_SOURCE, source?.name),
.putExtra(KEY_SOURCE, source?.name)
.putExtra(KEY_PREVIEW, preview),
anchor?.let { scaleUpActivityOptionsOf(it) },
)
}
@@ -412,7 +414,7 @@ class AppRouter private constructor(
return
}
buildAlertDialog(contextOrNull() ?: return) {
setIcon(context.getThemeDrawable(materialR.attr.actionModeShareDrawable))
setIcon(context.getThemeDrawable(appcompatR.attr.actionModeShareDrawable))
setTitle(R.string.share)
setItems(
arrayOf(
@@ -587,8 +589,11 @@ class AppRouter private constructor(
/** Private utils **/
private fun startActivity(intent: Intent, options: Bundle? = null) {
fragment?.startActivity(intent, options)
?: activity?.startActivity(intent, options)
fragment?.also {
if (it.host != null) {
it.startActivity(intent, options)
}
} ?: activity?.startActivity(intent, options)
}
private fun startActivitySafe(intent: Intent): Boolean = try {
@@ -768,6 +773,7 @@ class AppRouter private constructor(
const val KEY_MANGA = "manga"
const val KEY_MANGA_LIST = "manga_list"
const val KEY_PAGES = "pages"
const val KEY_PREVIEW = "preview"
const val KEY_QUERY = "query"
const val KEY_READER_MODE = "reader_mode"
const val KEY_SORT_ORDER = "sort_order"

View File

@@ -105,6 +105,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getEnumValue(KEY_LIST_MODE_FAVORITES, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_FAVORITES, value) }
val isTagsWarningsEnabled: Boolean
get() = prefs.getBoolean(KEY_TAGS_WARNINGS, true)
var isNsfwContentDisabled: Boolean
get() = prefs.getBoolean(KEY_DISABLE_NSFW, false)
set(value) = prefs.edit { putBoolean(KEY_DISABLE_NSFW, value) }
@@ -359,6 +362,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
val isSuggestionsExcludeNsfw: Boolean
get() = prefs.getBoolean(KEY_SUGGESTIONS_EXCLUDE_NSFW, false)
val isSuggestionsIncludeDisabledSources: Boolean
get() = prefs.getBoolean(KEY_SUGGESTIONS_DISABLED_SOURCES, false)
val isSuggestionsNotificationAvailable: Boolean
get() = prefs.getBoolean(KEY_SUGGESTIONS_NOTIFICATIONS, false)
@@ -658,6 +664,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_SUGGESTIONS_WIFI_ONLY = "suggestions_wifi"
const val KEY_SUGGESTIONS_EXCLUDE_NSFW = "suggestions_exclude_nsfw"
const val KEY_SUGGESTIONS_EXCLUDE_TAGS = "suggestions_exclude_tags"
const val KEY_SUGGESTIONS_DISABLED_SOURCES = "suggestions_disabled_sources"
const val KEY_SUGGESTIONS_NOTIFICATIONS = "suggestions_notifications"
const val KEY_SHIKIMORI = "shikimori"
const val KEY_ANILIST = "anilist"
@@ -728,6 +735,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_BACKUP_TG_ENABLED = "backup_periodic_tg_enabled"
const val KEY_BACKUP_TG_CHAT = "backup_periodic_tg_chat_id"
const val KEY_MANGA_LIST_BADGES = "manga_list_badges"
const val KEY_TAGS_WARNINGS = "tags_warnings"
// keys for non-persistent preferences
const val KEY_APP_VERSION = "app_version"

View File

@@ -103,7 +103,7 @@ abstract class BaseActivity<B : ViewBinding> :
supportActionBar?.run {
setDisplayHomeAsUpEnabled(isEnabled)
if (showUpAsClose) {
setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material)
setHomeAsUpIndicator(materialR.drawable.ic_clear_black_24)
}
}
}

View File

@@ -36,6 +36,7 @@ import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.isLayoutReversed
import org.koitharu.kotatsu.databinding.FastScrollerBinding
import kotlin.math.roundToInt
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
private const val SCROLLBAR_HIDE_DELAY = 1000L
@@ -132,7 +133,7 @@ class FastScroller @JvmOverloads constructor(
clipChildren = false
orientation = HORIZONTAL
@ColorInt var bubbleColor = context.getThemeColor(materialR.attr.colorControlNormal, Color.DKGRAY)
@ColorInt var bubbleColor = context.getThemeColor(appcompatR.attr.colorControlNormal, Color.DKGRAY)
@ColorInt var handleColor = bubbleColor
@ColorInt var trackColor = context.getThemeColor(materialR.attr.colorOutline, Color.LTGRAY)
@ColorInt var textColor = context.getThemeColor(android.R.attr.textColorPrimaryInverse, Color.WHITE)

View File

@@ -5,6 +5,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
class RetainedLifecycleCoroutineScope(
@@ -14,7 +15,9 @@ class RetainedLifecycleCoroutineScope(
override val coroutineContext: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
init {
lifecycle.addOnClearedListener(this)
launch(Dispatchers.Main.immediate) {
lifecycle.addOnClearedListener(this@RetainedLifecycleCoroutineScope)
}
}
override fun onCleared() {

View File

@@ -34,6 +34,7 @@ import org.koitharu.kotatsu.core.ui.image.AnimatedPlaceholderDrawable
import org.koitharu.kotatsu.core.util.progress.ImageRequestIndicatorListener
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
fun ImageView.newImageRequest(lifecycleOwner: LifecycleOwner, data: Any?): ImageRequest.Builder? {
@@ -112,7 +113,7 @@ fun ImageRequest.Builder.bookmarkExtra(bookmark: Bookmark): ImageRequest.Builder
fun ImageRequest.Builder.defaultPlaceholders(context: Context): ImageRequest.Builder {
val errorColor = ColorUtils.blendARGB(
context.getThemeColor(materialR.attr.colorErrorContainer),
context.getThemeColor(materialR.attr.colorBackgroundFloating),
context.getThemeColor(appcompatR.attr.colorBackgroundFloating),
0.25f,
)
return placeholder(AnimatedPlaceholderDrawable(context))

View File

@@ -87,6 +87,10 @@ fun <T, R> Collection<T>.mapSortedByCount(isDescending: Boolean = true, mapper:
return sorted.map { it.first }
}
fun Collection<CharSequence?>.contains(element: CharSequence?, ignoreCase: Boolean): Boolean = any { x ->
(x == null && element == null) || (x != null && element != null && x.contains(element, ignoreCase))
}
fun Collection<CharSequence?>.indexOfContains(element: CharSequence?, ignoreCase: Boolean): Int = indexOfFirst { x ->
(x == null && element == null) || (x != null && element != null && x.contains(element, ignoreCase))
}

View File

@@ -21,7 +21,13 @@ inline fun <T> LocaleListCompat.mapToSet(block: (Locale) -> T): Set<T> {
fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw NoSuchElementException()
fun String.toLocale() = Locale(this)
fun String.toLocale(): Locale = Locale.forLanguageTag(this)
fun String.toLocaleOrNull() = if (isEmpty()) {
null
} else {
toLocale().takeUnless { it.displayName == this }
}
fun Locale?.getDisplayName(context: Context): String = when (this) {
null -> context.getString(R.string.all_languages)

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.util.ext
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.os.Build
import androidx.annotation.PluralsRes
import androidx.annotation.Px
import androidx.core.util.TypedValueCompat
@@ -30,7 +31,10 @@ fun Context.getSystemBoolean(resName: String, fallback: Boolean): Boolean {
fun Resources.getQuantityStringSafe(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any): String = try {
getQuantityString(resId, quantity, *formatArgs)
} catch (e: Resources.NotFoundException) {
e.report(silent = true)
e.printStackTraceDebug()
formatArgs.firstOrNull()?.toString() ?: quantity.toString()
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.VANILLA_ICE_CREAM) { // known issue
e.printStackTraceDebug()
formatArgs.firstOrNull()?.toString() ?: quantity.toString()
} else {
throw e
}
}

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.details.data
import org.koitharu.kotatsu.core.model.getLocale
import org.koitharu.kotatsu.core.model.isLocal
import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.model.Manga
@@ -7,6 +8,7 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import org.koitharu.kotatsu.reader.data.filterChapters
import java.util.Locale
data class MangaDetails(
private val manga: Manga,
@@ -39,6 +41,13 @@ data class MangaDetails(
fun toManga() = manga
fun getLocale(): Locale? {
findAppropriateLocale(chapters.keys.singleOrNull())?.let {
return it
}
return manga.source.getLocale()
}
fun filterChapters(branch: String?) = MangaDetails(
manga = manga.filterChapters(branch),
localManga = localManga?.run {
@@ -69,4 +78,16 @@ data class MangaDetails(
}
return result
}
private fun findAppropriateLocale(name: String?): Locale? {
if (name.isNullOrEmpty()) {
return null
}
return Locale.getAvailableLocales().find { lc ->
name.contains(lc.getDisplayName(lc), ignoreCase = true) ||
name.contains(lc.getDisplayName(Locale.ENGLISH), ignoreCase = true) ||
name.contains(lc.getDisplayLanguage(lc), ignoreCase = true) ||
name.contains(lc.getDisplayLanguage(Locale.ENGLISH), ignoreCase = true)
}
}
}

View File

@@ -0,0 +1,29 @@
package org.koitharu.kotatsu.details.ui
import android.text.Spannable
import android.text.TextPaint
import android.text.style.ClickableSpan
import android.view.View
import android.widget.TextView
class AuthorSpan(private val listener: OnAuthorClickListener) : ClickableSpan() {
override fun onClick(widget: View) {
val text = (widget as? TextView)?.text as? Spannable ?: return
val start = text.getSpanStart(this)
val end = text.getSpanEnd(this)
val selected = text.substring(start, end).trim()
if (selected.isNotEmpty()) {
listener.onAuthorClick(selected)
}
}
override fun updateDrawState(ds: TextPaint) {
ds.setColor(ds.linkColor)
}
fun interface OnAuthorClickListener {
fun onAuthorClick(author: String)
}
}

View File

@@ -2,12 +2,15 @@ package org.koitharu.kotatsu.details.ui
import android.content.Context
import android.os.Bundle
import android.text.SpannedString
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.activity.viewModels
import androidx.core.text.buildSpannedString
import androidx.core.text.inSpans
import androidx.core.text.method.LinkMovementMethodCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isGone
@@ -43,6 +46,7 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.core.image.CoilMemoryCacheKey
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.LocalMangaSource
import org.koitharu.kotatsu.core.model.UnknownMangaSource
@@ -100,10 +104,12 @@ import org.koitharu.kotatsu.list.ui.adapter.mangaGridItemAD
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaListModel
import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -115,7 +121,7 @@ class DetailsActivity :
View.OnClickListener,
View.OnLayoutChangeListener, ViewTreeObserver.OnDrawListener,
ChipsView.OnChipClickListener, OnListItemClickListener<Bookmark>,
SwipeRefreshLayout.OnRefreshListener {
SwipeRefreshLayout.OnRefreshListener, AuthorSpan.OnAuthorClickListener {
@Inject
lateinit var shortcutManager: AppShortcutManager
@@ -135,7 +141,6 @@ class DetailsActivity :
supportActionBar?.setDisplayShowTitleEnabled(false)
viewBinding.chipFavorite.setOnClickListener(this)
infoBinding.textViewLocal.setOnClickListener(this)
infoBinding.textViewAuthor.setOnClickListener(this)
infoBinding.textViewSource.setOnClickListener(this)
viewBinding.imageViewCover.setOnClickListener(this)
viewBinding.textViewTitle.setOnClickListener(this)
@@ -145,6 +150,7 @@ class DetailsActivity :
viewBinding.textViewDescription.addOnLayoutChangeListener(this)
viewBinding.swipeRefreshLayout.setOnRefreshListener(this)
viewBinding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
infoBinding.textViewAuthor.movementMethod = LinkMovementMethodCompat.getInstance()
viewBinding.textViewDescription.movementMethod = LinkMovementMethodCompat.getInstance()
viewBinding.chipsTags.onChipClickListener = this
TitleScrollCoordinator(viewBinding.textViewTitle).attach(viewBinding.scrollView)
@@ -179,16 +185,6 @@ class DetailsActivity :
viewModel.isStatsAvailable.observe(this, menuInvalidator)
viewModel.remoteManga.observe(this, menuInvalidator)
viewModel.tags.observe(this, ::onTagsChanged)
viewModel.branches.observe(this) {
val branch = it.singleOrNull()
infoBinding.textViewTranslation.textAndVisible = branch?.name
infoBinding.textViewTranslation.drawableStart = branch?.locale?.let {
LocaleUtils.getEmojiFlag(it)
}?.let {
TextDrawable.compound(infoBinding.textViewTranslation, it)
}
infoBinding.textViewTranslationLabel.isVisible = infoBinding.textViewTranslation.isVisible
}
viewModel.chapters.observe(this, PrefetchObserver(this))
viewModel.onDownloadStarted
.filterNot { appRouter.isChapterPagesSheetShown() }
@@ -202,36 +198,31 @@ class DetailsActivity :
addMenuProvider(menuProvider)
}
override fun isNsfwContent(): Flow<Boolean> = viewModel.manga.map { it?.isNsfw == true }
override fun isNsfwContent(): Flow<Boolean> = viewModel.manga.map { it?.contentRating == ContentRating.ADULT }
override fun onClick(v: View) {
when (v.id) {
R.id.textView_author -> {
val manga = viewModel.manga.value
val author = manga?.author ?: return
router.showAuthorDialog(author, manga.source)
}
R.id.textView_source -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.openList(manga.source, null, null)
}
R.id.textView_local -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.showLocalInfoDialog(manga)
}
R.id.chip_favorite -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.showFavoriteDialog(manga)
}
R.id.imageView_cover -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.openImage(
url = viewModel.coverUrl.value ?: return,
source = manga.source,
preview = CoilMemoryCacheKey.from(viewBinding.imageViewCover),
anchor = v,
)
}
@@ -251,17 +242,17 @@ class DetailsActivity :
}
R.id.button_scrobbling_more -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.showScrobblingSelectorSheet(manga, null)
}
R.id.button_related_more -> {
val manga = viewModel.manga.value ?: return
val manga = viewModel.getMangaOrNull() ?: return
router.openRelated(manga)
}
R.id.textView_title -> {
val title = viewModel.manga.value?.title?.nullIfEmpty() ?: return
val title = viewModel.getMangaOrNull()?.title?.nullIfEmpty() ?: return
buildAlertDialog(this) {
setMessage(title)
setNegativeButton(R.string.close, null)
@@ -273,6 +264,10 @@ class DetailsActivity :
}
}
override fun onAuthorClick(author: String) {
router.showAuthorDialog(author, viewModel.getMangaOrNull()?.source ?: return)
}
override fun onChipClick(chip: Chip, data: Any?) {
val tag = data as? MangaTag ?: return
router.showTagDialog(tag)
@@ -306,7 +301,6 @@ class DetailsActivity :
oldBottom: Int
) {
with(viewBinding) {
buttonDescriptionMore.isVisible = textViewDescription.isTextTruncated
containerBottomSheet?.let { sheet ->
val peekHeight = BottomSheetBehavior.from(sheet).peekHeight
if (scrollView.paddingBottom != peekHeight) {
@@ -407,11 +401,21 @@ class DetailsActivity :
with(viewBinding) {
textViewTitle.text = manga.title
textViewSubtitle.textAndVisible = manga.altTitles.joinToString("\n")
textViewNsfw.isVisible = manga.isNsfw
textViewNsfw16.isVisible = manga.contentRating == ContentRating.SUGGESTIVE
textViewNsfw18.isVisible = manga.contentRating == ContentRating.ADULT
textViewDescription.text = details.description.ifNullOrEmpty { getString(R.string.no_description) }
}
with(infoBinding) {
textViewAuthor.textAndVisible = manga.author
val translation = details.getLocale()
infoBinding.textViewTranslation.textAndVisible = translation?.getDisplayLanguage(translation)
?.toTitleCase(translation)
infoBinding.textViewTranslation.drawableStart = translation?.let {
LocaleUtils.getEmojiFlag(it)
}?.let {
TextDrawable.compound(infoBinding.textViewTranslation, it)
}
infoBinding.textViewTranslationLabel.isVisible = infoBinding.textViewTranslation.isVisible
textViewAuthor.textAndVisible = manga.getAuthorsString()
textViewAuthorLabel.isVisible = textViewAuthor.isVisible
if (manga.hasRating) {
ratingBarRating.rating = manga.rating * ratingBarRating.numStars
@@ -533,6 +537,24 @@ class DetailsActivity :
return getString(R.string.chapters_time_pattern, this, timeFormatted)
}
private fun Manga.getAuthorsString(): SpannedString? {
if (authors.isEmpty()) {
return null
}
return buildSpannedString {
authors.forEach { a ->
if (a.isNotEmpty()) {
if (isNotEmpty()) {
append(", ")
}
inSpans(AuthorSpan(this@DetailsActivity)) {
append(a)
}
}
}
}.nullIfEmpty()
}
private class PrefetchObserver(
private val context: Context,
) : FlowCollector<List<ChapterListItem>?> {

View File

@@ -69,7 +69,7 @@ class DetailsMenuProvider(
}
R.id.action_online -> {
router.openDetails(manga)
router.openDetails(viewModel.remoteManga.value ?: return false)
}
R.id.action_related -> {

View File

@@ -15,16 +15,17 @@ import org.koitharu.kotatsu.core.ui.list.decor.AbstractSelectionItemDecoration
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class ChaptersSelectionDecoration(context: Context) : AbstractSelectionItemDecoration() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val radius = context.resources.getDimension(materialR.dimen.abc_control_corner_material)
private val radius = context.resources.getDimension(appcompatR.dimen.abc_control_corner_material)
private val checkIcon = ContextCompat.getDrawable(context, materialR.drawable.ic_mtrl_checked_circle)
private val iconOffset = context.resources.getDimensionPixelOffset(R.dimen.chapter_check_offset)
private val iconSize = context.resources.getDimensionPixelOffset(R.dimen.chapter_check_size)
private val strokeColor = context.getThemeColor(materialR.attr.colorPrimary, Color.RED)
private val strokeColor = context.getThemeColor(appcompatR.attr.colorPrimary, Color.RED)
private val fillColor = ColorUtils.setAlphaComponent(
ColorUtils.blendARGB(strokeColor, context.getThemeColor(materialR.attr.colorSurface), 0.8f),
0x74,
@@ -32,7 +33,7 @@ class ChaptersSelectionDecoration(context: Context) : AbstractSelectionItemDecor
init {
paint.color = ColorUtils.setAlphaComponent(
context.getThemeColor(materialR.attr.colorPrimary, Color.DKGRAY),
context.getThemeColor(appcompatR.attr.colorPrimary, Color.DKGRAY),
98,
)
paint.style = Paint.Style.FILL

View File

@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.details.ui.model
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel
import java.util.Locale
data class MangaBranch(
val name: String?,
@@ -11,8 +10,6 @@ data class MangaBranch(
val isCurrent: Boolean,
) : ListModel {
val locale: Locale? by lazy(::findAppropriateLocale)
override fun areItemsTheSame(other: ListModel): Boolean {
return other is MangaBranch && other.name == name
}
@@ -28,16 +25,4 @@ data class MangaBranch(
override fun toString(): String {
return "$name: $count"
}
private fun findAppropriateLocale(): Locale? {
if (name.isNullOrEmpty()) {
return null
}
return Locale.getAvailableLocales().find { lc ->
name.contains(lc.getDisplayName(lc), ignoreCase = true) ||
name.contains(lc.getDisplayName(Locale.ENGLISH), ignoreCase = true) ||
name.contains(lc.getDisplayLanguage(lc), ignoreCase = true) ||
name.contains(lc.getDisplayLanguage(Locale.ENGLISH), ignoreCase = true)
}
}
}

View File

@@ -7,7 +7,6 @@ import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
@@ -27,7 +26,6 @@ import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
import org.koitharu.kotatsu.core.util.ext.findCurrentPagerFragment
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.menuView
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
@@ -106,9 +104,6 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(),
}
val binding = viewBinding ?: return
val isActionModeStarted = actionModeDelegate?.isActionModeStarted == true
if (sheet.context.isAnimationsEnabled) {
TransitionManager.beginDelayedTransition(binding.toolbar)
}
binding.toolbar.menuView?.isVisible = newState == STATE_EXPANDED && !isActionModeStarted
binding.splitButtonRead.isVisible = newState != STATE_EXPANDED && !isActionModeStarted
&& viewModel is DetailsViewModel

View File

@@ -71,7 +71,7 @@ class BookmarksViewModel @Inject constructor(
if (b.isNullOrEmpty()) {
continue
}
result += ListHeader(chapter.name)
result += ListHeader(chapter)
result.addAll(b)
}
if (result.isEmpty()) {

View File

@@ -3,10 +3,12 @@ package org.koitharu.kotatsu.details.ui.pager.pages
import androidx.lifecycle.LifecycleOwner
import coil3.ImageLoader
import coil3.request.allowRgb565
import coil3.request.transformations
import coil3.size.Scale
import coil3.size.Size
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.image.TrimTransformation
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.decodeRegion
@@ -43,6 +45,7 @@ fun pageThumbnailAD(
size(thumbSize)
scale(Scale.FILL)
allowRgb565(true)
transformations(TrimTransformation())
decodeRegion(0)
mangaSourceExtra(item.page.source)
enqueueWith(coil)

View File

@@ -130,7 +130,7 @@ class PagesViewModel @Inject constructor(
for (page in snapshot) {
if (page.chapterId != previousChapterId) {
chaptersLoader.peekChapter(page.chapterId)?.let {
add(ListHeader(it.name))
add(ListHeader(it))
}
previousChapterId = page.chapterId
}

View File

@@ -16,7 +16,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.Manga
import java.time.Instant
import java.util.UUID
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
data class DownloadItemModel(
val id: UUID,
@@ -62,7 +62,7 @@ data class DownloadItemModel(
fun getErrorMessage(context: Context): CharSequence? = if (error != null) {
buildSpannedString {
bold {
color(context.getThemeColor(materialR.attr.colorError, Color.RED)) {
color(context.getThemeColor(appcompatR.attr.colorError, Color.RED)) {
append(error)
}
}
@@ -72,7 +72,7 @@ data class DownloadItemModel(
}
override fun compareTo(other: DownloadItemModel): Int {
return timestamp.compareTo(other.timestamp)
return timestamp compareTo other.timestamp
}
override fun areItemsTheSame(other: ListModel): Boolean {

View File

@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.decor.AbstractSelectionItemDecoration
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class DownloadsSelectionDecoration(context: Context) : AbstractSelectionItemDecoration() {
@@ -23,7 +24,7 @@ class DownloadsSelectionDecoration(context: Context) : AbstractSelectionItemDeco
private val checkIcon = ContextCompat.getDrawable(context, materialR.drawable.ic_mtrl_checked_circle)
private val iconOffset = context.resources.getDimensionPixelOffset(R.dimen.card_indicator_offset)
private val iconSize = context.resources.getDimensionPixelOffset(R.dimen.card_indicator_size)
private val strokeColor = context.getThemeColor(materialR.attr.colorPrimary, Color.RED)
private val strokeColor = context.getThemeColor(appcompatR.attr.colorPrimary, Color.RED)
private val fillColor = ColorUtils.setAlphaComponent(
ColorUtils.blendARGB(strokeColor, context.getThemeColor(materialR.attr.colorSurface), 0.8f),
0x74,

View File

@@ -6,7 +6,6 @@ import android.content.Intent
import android.content.IntentFilter
import androidx.core.content.ContextCompat
import androidx.room.withTransaction
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
@@ -354,7 +353,7 @@ class MangaSourcesRepository @Inject constructor(
.conflate()
}
private fun getExternalSources() = context.packageManager.queryIntentContentProviders(
fun getExternalSources(): List<ExternalMangaSource> = context.packageManager.queryIntentContentProviders(
Intent("app.kotatsu.parser.PROVIDE_MANGA"), 0,
).map { resolveInfo ->
ExternalMangaSource(

View File

@@ -36,15 +36,15 @@ class RecoverMangaUseCase @Inject constructor(
) = Manga(
id = broken.id,
title = current.title,
altTitle = current.altTitle,
altTitles = current.altTitles,
url = current.url,
publicUrl = current.publicUrl,
rating = current.rating,
isNsfw = current.isNsfw,
contentRating = current.contentRating,
coverUrl = current.coverUrl,
tags = current.tags,
state = current.state,
author = current.author,
authors = current.authors,
largeCoverUrl = current.largeCoverUrl,
description = current.description,
chapters = current.chapters,

View File

@@ -14,12 +14,13 @@ import org.koitharu.kotatsu.core.ui.list.decor.AbstractSelectionItemDecoration
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class SourceSelectionDecoration(context: Context) : AbstractSelectionItemDecoration() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val strokeColor = context.getThemeColor(materialR.attr.colorPrimary, Color.RED)
private val strokeColor = context.getThemeColor(appcompatR.attr.colorPrimary, Color.RED)
private val fillColor = ColorUtils.setAlphaComponent(
ColorUtils.blendARGB(strokeColor, context.getThemeColor(materialR.attr.colorSurface), 0.8f),
0x74,

View File

@@ -13,13 +13,14 @@ import org.koitharu.kotatsu.core.ui.list.decor.AbstractSelectionItemDecoration
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class CategoriesSelectionDecoration(context: Context) : AbstractSelectionItemDecoration() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val radius = context.resources.getDimension(R.dimen.list_selector_corner)
private val strokeColor = context.getThemeColor(materialR.attr.colorPrimary, Color.RED)
private val strokeColor = context.getThemeColor(appcompatR.attr.colorPrimary, Color.RED)
private val fillColor = ColorUtils.setAlphaComponent(
ColorUtils.blendARGB(strokeColor, context.getThemeColor(materialR.attr.colorSurface), 0.8f),
0x74,

View File

@@ -22,6 +22,7 @@ import org.koitharu.kotatsu.core.util.ext.setThemeTextAppearance
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ViewFilterFieldBinding
import java.util.LinkedList
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class FilterFieldLayout @JvmOverloads constructor(
@@ -100,7 +101,7 @@ class FilterFieldLayout @JvmOverloads constructor(
label.drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_error_small)
TextViewCompat.setCompoundDrawableTintList(
label,
context.getThemeColorStateList(materialR.attr.colorControlNormal),
context.getThemeColorStateList(appcompatR.attr.colorControlNormal),
)
addView(label)
errorView = label

View File

@@ -14,7 +14,7 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import javax.inject.Inject
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
class FilterHeaderProducer @Inject constructor(
private val searchRepository: MangaSearchRepository,
@@ -129,7 +129,7 @@ class FilterHeaderProducer @Inject constructor(
result.addFirst(
ChipsView.ChipModel(
title = snapshot.query,
icon = materialR.drawable.abc_ic_search_api_material,
icon = appcompatR.drawable.abc_ic_search_api_material,
isCloseable = true,
data = snapshot.query,
),

View File

@@ -11,21 +11,20 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import coil3.Image
import coil3.ImageLoader
import coil3.asDrawable
import coil3.request.CachePolicy
import coil3.request.ErrorResult
import coil3.request.ImageRequest
import coil3.request.SuccessResult
import coil3.request.lifecycle
import coil3.target.ViewTarget
import coil3.target.GenericViewTarget
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
import org.koitharu.kotatsu.core.image.CoilMemoryCacheKey
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.ui.BaseActivity
@@ -36,6 +35,7 @@ import org.koitharu.kotatsu.core.util.ext.end
import org.koitharu.kotatsu.core.util.ext.enqueueWith
import org.koitharu.kotatsu.core.util.ext.getDisplayIcon
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra
import org.koitharu.kotatsu.core.util.ext.observe
@@ -44,7 +44,7 @@ import org.koitharu.kotatsu.core.util.ext.start
import org.koitharu.kotatsu.databinding.ActivityImageBinding
import org.koitharu.kotatsu.databinding.ItemErrorStateBinding
import javax.inject.Inject
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
@AndroidEntryPoint
class ImageActivity : BaseActivity<ActivityImageBinding>(),
@@ -63,7 +63,6 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(),
setContentView(ActivityImageBinding.inflate(layoutInflater))
viewBinding.buttonBack.setOnClickListener(this)
viewBinding.buttonMenu.setOnClickListener(this)
val imageUrl = requireNotNull(intent.data)
val menuProvider = ImageMenuProvider(
activity = this,
@@ -74,14 +73,14 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(),
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.root, null))
viewModel.onImageSaved.observeEvent(this, ::onImageSaved)
loadImage(imageUrl)
loadImage()
}
override fun onClick(v: View) {
when (v.id) {
R.id.button_back -> dispatchNavigateUp()
R.id.button_menu -> menuMediator.onLongClick(v)
else -> loadImage(intent.data)
else -> loadImage()
}
}
@@ -122,10 +121,11 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(),
return insets.consumeAll(typeMask)
}
private fun loadImage(url: Uri?) {
private fun loadImage() {
ImageRequest.Builder(this)
.data(url)
.memoryCachePolicy(CachePolicy.DISABLED)
.data(intent.data)
.memoryCacheKey(intent.getParcelableExtraCompat<CoilMemoryCacheKey>(AppRouter.KEY_PREVIEW)?.data)
.memoryCachePolicy(CachePolicy.READ_ONLY)
.lifecycle(this)
.listener(this)
.mangaSourceExtra(MangaSource(intent.getStringExtra(AppRouter.KEY_SOURCE)))
@@ -147,22 +147,24 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(),
button.setImageDrawable(
CircularProgressDrawable(this).also {
it.setStyle(CircularProgressDrawable.LARGE)
it.setColorSchemeColors(getThemeColor(materialR.attr.colorControlNormal))
it.setColorSchemeColors(getThemeColor(appcompatR.attr.colorControlNormal))
it.start()
},
)
} else {
button.setImageResource(materialR.drawable.abc_ic_menu_overflow_material)
button.setImageResource(appcompatR.drawable.abc_ic_menu_overflow_material)
}
}
private class SsivTarget(
override val view: SubsamplingScaleImageView,
) : ViewTarget<SubsamplingScaleImageView> {
) : GenericViewTarget<SubsamplingScaleImageView>() {
override fun onError(error: Image?) = setDrawable(error?.asDrawable(view.resources))
override fun onSuccess(result: Image) = setDrawable(result.asDrawable(view.resources))
override var drawable: Drawable? = null
set(value) {
field = value
setImageDrawable(value)
}
override fun equals(other: Any?): Boolean {
return (this === other) || (other is SsivTarget && view == other.view)
@@ -172,7 +174,7 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(),
override fun toString() = "SsivTarget(view=$view)"
private fun setDrawable(drawable: Drawable?) {
private fun setImageDrawable(drawable: Drawable?) {
if (drawable != null) {
view.setImage(ImageSource.bitmap(drawable.toBitmap()))
} else {

View File

@@ -140,7 +140,7 @@ class MangaListMapper @Inject constructor(
@ColorRes
private fun getTagTint(tag: MangaTag): Int {
return if (tag.title.lowercase() in dict) {
return if (settings.isTagsWarningsEnabled && tag.title.lowercase() in dict) {
R.color.warning
} else {
0
@@ -148,7 +148,7 @@ class MangaListMapper @Inject constructor(
}
private fun readTagsDict(context: Context): ScatterSet<String> =
context.resources.openRawResource(R.raw.tags_redlist).use {
context.resources.openRawResource(R.raw.tags_warnlist).use {
val set = MutableScatterSet<String>()
it.bufferedReader().forEachLine { x ->
val line = x.trim()

View File

@@ -15,12 +15,13 @@ import org.koitharu.kotatsu.core.ui.list.decor.AbstractSelectionItemDecoration
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.list.ui.model.MangaListModel
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
open class MangaSelectionDecoration(context: Context) : AbstractSelectionItemDecoration() {
protected val paint = Paint(Paint.ANTI_ALIAS_FLAG)
protected val strokeColor = context.getThemeColor(materialR.attr.colorPrimary, Color.RED)
protected val strokeColor = context.getThemeColor(appcompatR.attr.colorPrimary, Color.RED)
protected val fillColor = ColorUtils.setAlphaComponent(
ColorUtils.blendARGB(strokeColor, context.getThemeColor(materialR.attr.colorSurface), 0.8f),
0x74,

View File

@@ -32,7 +32,7 @@ fun mangaListDetailedItemAD(
bind { payloads ->
binding.textViewTitle.text = item.title
binding.textViewAuthor.textAndVisible = item.manga.author
binding.textViewAuthor.textAndVisible = item.manga.authors.joinToString(", ")
binding.progressView.setProgress(
value = item.progress,
animate = ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads,

View File

@@ -2,7 +2,9 @@ package org.koitharu.kotatsu.list.ui.model
import android.content.Context
import androidx.annotation.StringRes
import org.koitharu.kotatsu.core.model.getLocalizedTitle
import org.koitharu.kotatsu.core.ui.model.DateTimeAgo
import org.koitharu.kotatsu.parsers.model.MangaChapter
data class ListHeader private constructor(
private val textRaw: Any,
@@ -25,6 +27,13 @@ data class ListHeader private constructor(
badge: String? = null,
) : this(textRaw = textRes, buttonTextRes, payload, badge)
constructor(
chapter: MangaChapter,
@StringRes buttonTextRes: Int = 0,
payload: Any? = null,
badge: String? = null,
) : this(textRaw = chapter, buttonTextRes, payload, badge)
constructor(
dateTimeAgo: DateTimeAgo,
@StringRes buttonTextRes: Int = 0,
@@ -36,6 +45,7 @@ data class ListHeader private constructor(
is CharSequence -> textRaw
is Int -> if (textRaw != 0) context.getString(textRaw) else null
is DateTimeAgo -> textRaw.format(context)
is MangaChapter -> textRaw.getLocalizedTitle(context.resources)
else -> null
}

View File

@@ -1,6 +1,5 @@
package org.koitharu.kotatsu.local.data
import android.net.Uri
import androidx.core.net.toFile
import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
@@ -17,7 +16,6 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.AlphanumComparator
import org.koitharu.kotatsu.core.util.ext.deleteAwait
import org.koitharu.kotatsu.core.util.ext.isWriteable
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.takeIfWriteable
import org.koitharu.kotatsu.core.util.ext.withChildren
@@ -45,6 +43,7 @@ import javax.inject.Inject
import javax.inject.Singleton
private const val MAX_PARALLELISM = 4
private const val FILENAME_SKIP = ".notamanga"
@Singleton
class LocalMangaRepository @Inject constructor(
@@ -140,7 +139,7 @@ class LocalMangaRepository @Inject constructor(
}
suspend fun delete(manga: Manga): Boolean {
val file = Uri.parse(manga.url).toFile()
val file = manga.url.toUri().toFile()
val result = file.deleteAwait()
if (result) {
localMangaIndex.delete(manga.id)
@@ -256,8 +255,10 @@ class LocalMangaRepository @Inject constructor(
private suspend fun getAllFiles() = storageManager.getReadableDirs()
.asSequence()
.flatMap { dir ->
dir.withChildren { children -> children.filterNot { it.isHidden }.toList() }
dir.withChildren { children -> children.filterNot { it.isHidden || it.shouldSkip() }.toList() }
}
private fun Collection<LocalManga>.unwrap(): List<Manga> = map { it.manga }
private fun File.shouldSkip(): Boolean = isDirectory && File(this, FILENAME_SKIP).exists()
}

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.local.domain.model
import android.net.Uri
import androidx.core.net.toFile
import androidx.core.net.toUri
import org.koitharu.kotatsu.core.util.ext.contains
import org.koitharu.kotatsu.core.util.ext.creationTime
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
@@ -26,8 +27,8 @@ data class LocalManga(
fun isMatchesQuery(query: String): Boolean {
return manga.title.contains(query, ignoreCase = true) ||
manga.altTitle?.contains(query, ignoreCase = true) == true ||
manga.author?.contains(query, ignoreCase = true) == true
manga.altTitles.contains(query, ignoreCase = true) ||
manga.authors.contains(query, ignoreCase = true)
}
fun containsTags(tags: Collection<String>): Boolean {

View File

@@ -21,7 +21,7 @@ import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.setProgressIcon
import org.koitharu.kotatsu.databinding.DialogLocalInfoBinding
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
@AndroidEntryPoint
class LocalInfoDialog : AlertDialogFragment<DialogLocalInfoBinding>(), View.OnClickListener {
@@ -86,7 +86,7 @@ class LocalInfoDialog : AlertDialogFragment<DialogLocalInfoBinding>(), View.OnCl
val total = size + available
val segment = SegmentedBarView.Segment(
percent = (size.toDouble() / total.toDouble()).toFloat(),
color = KotatsuColors.segmentColor(view.context, materialR.attr.colorPrimary),
color = KotatsuColors.segmentColor(view.context, appcompatR.attr.colorPrimary),
)
requireViewBinding().labelUsed.text = view.context.getString(
R.string.memory_usage_pattern,

View File

@@ -429,9 +429,9 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
supportActionBar?.apply {
setHomeAsUpIndicator(
if (isOpened) {
materialR.drawable.abc_ic_ab_back_material
materialR.drawable.ic_arrow_back_black_24
} else {
materialR.drawable.abc_ic_search_api_material
materialR.drawable.ic_search_black_24
},
)
setHomeActionContentDescription(

View File

@@ -8,20 +8,6 @@ fun Manga.filterChapters(branch: String?): Manga {
return withChapters(chapters = chapters?.filter { it.branch == branch })
}
private fun Manga.withChapters(chapters: List<MangaChapter>?) = Manga(
id = id,
title = title,
altTitle = altTitle,
url = url,
publicUrl = publicUrl,
rating = rating,
isNsfw = isNsfw,
coverUrl = coverUrl,
tags = tags,
state = state,
author = author,
largeCoverUrl = largeCoverUrl,
description = description,
private fun Manga.withChapters(chapters: List<MangaChapter>?) = copy(
chapters = chapters,
source = source,
)
)

View File

@@ -8,9 +8,15 @@ import androidx.collection.LongSparseArray
import androidx.collection.set
import androidx.core.net.toFile
import androidx.core.net.toUri
import coil3.BitmapImage
import coil3.Image
import coil3.ImageLoader
import coil3.memory.MemoryCache
import coil3.request.ImageRequest
import coil3.request.transformations
import coil3.toBitmap
import com.davemorrissey.labs.subscaleview.ImageSource
import dagger.hilt.android.ActivityRetainedLifecycle
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Deferred
@@ -24,6 +30,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.use
@@ -36,9 +43,9 @@ import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.parser.CachingMangaRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.image.TrimTransformation
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.MimeTypes
import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope
import org.koitharu.kotatsu.core.util.ext.URI_SCHEME_ZIP
import org.koitharu.kotatsu.core.util.ext.cancelChildrenAndJoin
import org.koitharu.kotatsu.core.util.ext.compressToPNG
@@ -49,6 +56,8 @@ import org.koitharu.kotatsu.core.util.ext.isFileUri
import org.koitharu.kotatsu.core.util.ext.isNotEmpty
import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode
import org.koitharu.kotatsu.core.util.ext.isZipUri
import org.koitharu.kotatsu.core.util.ext.lifecycleScope
import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.ramAvailable
import org.koitharu.kotatsu.core.util.ext.toMimeType
@@ -76,13 +85,14 @@ class PageLoader @Inject constructor(
lifecycle: ActivityRetainedLifecycle,
@MangaHttpClient private val okHttp: OkHttpClient,
private val cache: PagesCache,
private val coil: ImageLoader,
private val settings: AppSettings,
private val mangaRepositoryFactory: MangaRepository.Factory,
private val imageProxyInterceptor: ImageProxyInterceptor,
private val downloadSlowdownDispatcher: DownloadSlowdownDispatcher,
) {
val loaderScope = RetainedLifecycleCoroutineScope(lifecycle) + InternalErrorHandler() + Dispatchers.Default
val loaderScope = lifecycle.lifecycleScope + InternalErrorHandler() + Dispatchers.Default
private val tasks = LongSparseArray<ProgressDeferred<Uri, Float>>()
private val semaphore = Semaphore(3)
@@ -121,6 +131,41 @@ class PageLoader @Inject constructor(
}
}
suspend fun loadPreview(page: MangaPage): ImageSource? {
val preview = page.preview
if (preview.isNullOrEmpty()) {
return null
}
val request = ImageRequest.Builder(context)
.data(preview)
.mangaSourceExtra(page.source)
.transformations(TrimTransformation())
.build()
return coil.execute(request).image?.toImageSource()
}
fun peekPreviewSource(preview: String?): ImageSource? {
if (preview.isNullOrEmpty()) {
return null
}
coil.memoryCache?.let { cache ->
val key = MemoryCache.Key(preview)
cache[key]?.image?.let {
return if (it is BitmapImage) {
ImageSource.cachedBitmap(it.toBitmap())
} else {
ImageSource.bitmap(it.toBitmap())
}
}
}
coil.diskCache?.let { cache ->
cache.openSnapshot(preview)?.use { snapshot ->
return ImageSource.file(snapshot.data.toFile())
}
}
return null
}
fun loadPageAsync(page: MangaPage, force: Boolean): ProgressDeferred<Uri, Float> {
var task = tasks[page.id]?.takeIf { it.isValid() }
if (force) {
@@ -237,7 +282,7 @@ class PageLoader @Inject constructor(
if (!skipCache) {
cache.get(pageUrl)?.let { return it.toUri() }
}
val uri = Uri.parse(pageUrl)
val uri = pageUrl.toUri()
return when {
uri.isZipUri() -> if (uri.scheme == URI_SCHEME_ZIP) {
uri
@@ -264,6 +309,12 @@ class PageLoader @Inject constructor(
return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES)
}
private fun Image.toImageSource(): ImageSource = if (this is BitmapImage) {
ImageSource.cachedBitmap(toBitmap())
} else {
ImageSource.bitmap(toBitmap())
}
private fun Deferred<Uri>.isValid(): Boolean {
return getCompletionResultOrNull()?.map { uri ->
uri.exists() && uri.isTargetNotEmpty()

View File

@@ -50,6 +50,7 @@ import org.koitharu.kotatsu.list.domain.ReadingProgress.Companion.PROGRESS_NONE
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.DeleteLocalMangaUseCase
import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
@@ -177,7 +178,7 @@ class ReaderViewModel @Inject constructor(
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null),
)
val isMangaNsfw = manga.map { it?.isNsfw == true }
val isMangaNsfw = manga.map { it?.contentRating == ContentRating.ADULT }
val isBookmarkAdded = readingState.flatMapLatest { state ->
val manga = mangaDetails.value?.toManga()

View File

@@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
@@ -108,19 +107,29 @@ class PageHolderDelegate(
}
override fun onReady() {
state = State.SHOWING
error = null
callback.onImageShowing(readerSettings)
if (state >= State.LOADED) {
state = State.SHOWING
error = null
callback.onImageShowing(readerSettings, isPreview = false)
} else if (state == State.LOADING_WITH_PREVIEW) {
callback.onImageShowing(readerSettings, isPreview = true)
}
}
override fun onImageLoaded() {
state = State.SHOWN
error = null
callback.onImageShown()
if (state >= State.LOADED) {
state = State.SHOWN
error = null
callback.onImageShown()
}
}
override fun onImageLoadError(e: Throwable) {
e.printStackTraceDebug()
if (state < State.LOADED) {
// ignore preview error
return
}
val uri = this.uri
error = e
if (state == State.LOADED && e is IOException && uri != null && uri.toFileOrNull()?.exists() != false) {
@@ -133,7 +142,7 @@ class PageHolderDelegate(
override fun onChanged(value: ReaderSettings) {
if (state == State.SHOWN) {
callback.onImageShowing(readerSettings)
callback.onImageShowing(readerSettings, isPreview = false)
}
callback.onConfigChanged()
}
@@ -172,21 +181,25 @@ class PageHolderDelegate(
}
}
private suspend fun doLoad(data: MangaPage, force: Boolean) {
private suspend fun doLoad(data: MangaPage, force: Boolean) = coroutineScope {
state = State.LOADING
error = null
callback.onLoadingStarted()
yield()
launch {
val preview = loader.loadPreview(data) ?: return@launch
if (state == State.LOADING) {
state = State.LOADING_WITH_PREVIEW
callback.onPreviewReady(preview)
}
}
try {
val task = withContext(Dispatchers.Default) {
loader.loadPageAsync(data, force)
}
uri = coroutineScope {
val progressObserver = observeProgress(this, task.progressAsFlow())
val file = task.await()
progressObserver.cancelAndJoin()
file
}
val progressObserver = observeProgress(this, task.progressAsFlow())
val file = task.await()
progressObserver.cancelAndJoin()
uri = file
state = State.LOADED
cachedBounds = if (readerSettings.isPagesCropEnabled(isWebtoon)) {
loader.getTrimmedBounds(checkNotNull(uri))
@@ -223,7 +236,7 @@ class PageHolderDelegate(
}
enum class State {
EMPTY, LOADING, LOADED, CONVERTING, CONVERTED, SHOWING, SHOWN, ERROR
EMPTY, LOADING, LOADING_WITH_PREVIEW, LOADED, CONVERTING, CONVERTED, SHOWING, SHOWN, ERROR
}
interface Callback {
@@ -232,9 +245,11 @@ class PageHolderDelegate(
fun onError(e: Throwable)
fun onPreviewReady(source: ImageSource)
fun onImageReady(source: ImageSource)
fun onImageShowing(settings: ReaderSettings)
fun onImageShowing(settings: ReaderSettings, isPreview: Boolean)
fun onImageShown()

View File

@@ -35,7 +35,7 @@ class DoublePageHolder(
.gravity = (if (isEven) Gravity.START else Gravity.END) or Gravity.BOTTOM
}
override fun onImageShowing(settings: ReaderSettings) {
override fun onImageShowing(settings: ReaderSettings, isPreview: Boolean) {
with(binding.ssiv) {
maxScale = 2f * maxOf(
width / sWidth.toFloat(),

View File

@@ -27,7 +27,7 @@ class ReversedPageHolder(
.gravity = Gravity.START or Gravity.BOTTOM
}
override fun onImageShowing(settings: ReaderSettings) {
override fun onImageShowing(settings: ReaderSettings, isPreview: Boolean) {
with(binding.ssiv) {
maxScale = 2f * maxOf(
width / sWidth.toFloat(),

View File

@@ -89,11 +89,15 @@ open class PageHolder(
}
}
override fun onPreviewReady(source: ImageSource) {
binding.ssiv.setImage(source)
}
override fun onImageReady(source: ImageSource) {
binding.ssiv.setImage(source)
}
override fun onImageShowing(settings: ReaderSettings) {
override fun onImageShowing(settings: ReaderSettings, isPreview: Boolean) {
binding.ssiv.maxScale = 2f * maxOf(
binding.ssiv.width / binding.ssiv.sWidth.toFloat(),
binding.ssiv.height / binding.ssiv.sHeight.toFloat(),

View File

@@ -89,11 +89,13 @@ class WebtoonHolder(
}
}
override fun onPreviewReady(source: ImageSource) = Unit
override fun onImageReady(source: ImageSource) {
binding.ssiv.setImage(source)
}
override fun onImageShowing(settings: ReaderSettings) {
override fun onImageShowing(settings: ReaderSettings, isPreview: Boolean) {
binding.ssiv.colorFilter = settings.colorFilter?.toColorFilter()
with(binding.ssiv) {
scrollTo(

View File

@@ -31,7 +31,7 @@ import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
import org.koitharu.kotatsu.scrobbling.common.ui.config.adapter.ScrobblingMangaAdapter
import javax.inject.Inject
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
@AndroidEntryPoint
class ScrobblerConfigActivity : BaseActivity<ActivityScrobblerConfigBinding>(),
@@ -114,7 +114,7 @@ class ScrobblerConfigActivity : BaseActivity<ActivityScrobblerConfigBinding>(),
private fun onUserChanged(user: ScrobblerUser?) {
if (user == null) {
viewBinding.imageViewAvatar.disposeImageRequest()
viewBinding.imageViewAvatar.setImageResource(materialR.drawable.abc_ic_menu_overflow_material)
viewBinding.imageViewAvatar.setImageResource(appcompatR.drawable.abc_ic_menu_overflow_material)
return
}
viewBinding.imageViewAvatar.newImageRequest(this, user.avatar)

View File

@@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.model.isNsfw
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.contains
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
@@ -83,7 +84,7 @@ class SearchV2Helper @AssistedInject constructor(
}
SearchKind.AUTHOR -> retainAll { m ->
m.author.isNullOrEmpty() || m.author.equals(query, ignoreCase = true)
m.authors.isEmpty() || m.authors.contains(query, ignoreCase = true)
}
SearchKind.SIMPLE, // no filtering expected
@@ -99,7 +100,7 @@ class SearchV2Helper @AssistedInject constructor(
}
SearchKind.AUTHOR -> sortByDescending { m ->
m.author?.equals(query, ignoreCase = true) == true
m.authors.contains(query, ignoreCase = true)
}
SearchKind.TAG -> sortByDescending { m ->

View File

@@ -23,19 +23,19 @@ import org.koitharu.kotatsu.core.util.ext.drawableEnd
import org.koitharu.kotatsu.core.util.ext.drawableStart
import org.koitharu.kotatsu.search.domain.SearchKind
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
private const val DRAWABLE_END = 2
class SearchEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = materialR.attr.editTextStyle,
@AttrRes defStyleAttr: Int = appcompatR.attr.editTextStyle,
) : AppCompatEditText(context, attrs, defStyleAttr) {
var searchSuggestionListener: SearchSuggestionListener? = null
private val clearIcon =
ContextCompat.getDrawable(context, materialR.drawable.abc_ic_clear_material)
ContextCompat.getDrawable(context, appcompatR.drawable.abc_ic_clear_material)
private var isEmpty = text.isNullOrEmpty()
init {

View File

@@ -4,14 +4,14 @@ import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import androidx.annotation.AttrRes
import com.google.android.material.R
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.shape.MaterialShapeDrawable
import androidx.appcompat.R as appcompatR
class SearchToolbar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = R.attr.toolbarStyle,
@AttrRes defStyleAttr: Int = appcompatR.attr.toolbarStyle,
) : MaterialToolbar(context, attrs, defStyleAttr) {
private val bgDrawable = MaterialShapeDrawable(context, attrs, defStyleAttr, 0)
@@ -21,4 +21,4 @@ class SearchToolbar @JvmOverloads constructor(
bgDrawable.setShadowColor(Color.DKGRAY)
background = bgDrawable
}
}
}

View File

@@ -27,7 +27,7 @@ import org.koitharu.kotatsu.databinding.ActivityReaderTapActionsBinding
import org.koitharu.kotatsu.reader.domain.TapGridArea
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
import java.util.EnumMap
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
@AndroidEntryPoint
class ReaderTapGridConfigActivity : BaseActivity<ActivityReaderTapActionsBinding>(), View.OnClickListener,
@@ -157,7 +157,7 @@ class ReaderTapGridConfigActivity : BaseActivity<ActivityReaderTapActionsBinding
}
private fun createBackground(action: TapAction?): Drawable? {
val ripple = getThemeDrawable(materialR.attr.selectableItemBackground)
val ripple = getThemeDrawable(appcompatR.attr.selectableItemBackground)
return if (action == null) {
ripple
} else {

View File

@@ -21,7 +21,6 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -36,11 +35,7 @@ class SourceAuthActivity : BaseBrowserActivity(), BrowserCallback {
private lateinit var authProvider: MangaParserAuthProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) {
return
}
override fun onCreate2(savedInstanceState: Bundle?) {
val source = MangaSource(intent?.getStringExtra(EXTRA_SOURCE))
if (source !is MangaParserSource) {
finishAfterTransition()

View File

@@ -27,7 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
import org.koitharu.kotatsu.databinding.ItemEmptyHintBinding
import org.koitharu.kotatsu.databinding.ItemSourceCatalogBinding
import org.koitharu.kotatsu.list.ui.model.ListModel
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
fun sourceCatalogItemSourceAD(
coil: ImageLoader,
@@ -46,7 +46,7 @@ fun sourceCatalogItemSourceAD(
listener.onItemClick(item, v)
}
val basePadding = context.getThemeDimensionPixelOffset(
materialR.attr.listPreferredItemPaddingEnd,
appcompatR.attr.listPreferredItemPaddingEnd,
binding.root.paddingStart,
)
binding.root.updatePaddingRelative(

View File

@@ -14,6 +14,7 @@ import org.koitharu.kotatsu.parsers.util.replaceWith
import org.koitharu.kotatsu.parsers.util.toIntUp
import kotlin.math.roundToInt
import kotlin.random.Random
import androidx.appcompat.R as appcompatR
import com.google.android.material.R as materialR
class BarChartView @JvmOverloads constructor(
@@ -38,7 +39,7 @@ class BarChartView @JvmOverloads constructor(
private val chartBounds = RectF()
@ColorInt
var barColor: Int = context.getThemeColor(materialR.attr.colorAccent)
var barColor: Int = context.getThemeColor(appcompatR.attr.colorAccent)
set(value) {
field = value
invalidate()

View File

@@ -48,11 +48,13 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.model.distinctById
import org.koitharu.kotatsu.core.model.getLocale
import org.koitharu.kotatsu.core.model.isNsfw
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.nav.ReaderIntent
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.LocaleComparator
import org.koitharu.kotatsu.core.util.ext.asArrayList
import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName
import org.koitharu.kotatsu.core.util.ext.awaitWorkInfosByTag
@@ -179,7 +181,7 @@ class SuggestionsWorker @AssistedInject constructor(
historyRepository.getList(0, 20) +
favouritesRepository.getLastManga(20)
).distinctById()
val sources = sourcesRepository.getEnabledSources()
val sources = getSources()
if (seed.isEmpty() || sources.isEmpty()) {
return 0
}
@@ -188,7 +190,7 @@ class SuggestionsWorker @AssistedInject constructor(
val semaphore = Semaphore(MAX_PARALLELISM)
val producer = channelFlow {
for (it in sources.shuffled()) {
for (it in sources) {
if (it.isNsfw() && (appSettings.isSuggestionsExcludeNsfw || appSettings.isNsfwContentDisabled)) {
continue
}
@@ -243,6 +245,18 @@ class SuggestionsWorker @AssistedInject constructor(
return suggestions.size
}
private suspend fun getSources(): List<MangaSource> {
if (appSettings.isSuggestionsIncludeDisabledSources) {
val result = sourcesRepository.allMangaSources.toMutableList<MangaSource>()
result.addAll(sourcesRepository.getExternalSources())
result.shuffle()
result.sortWith(compareBy(nullsLast(LocaleComparator())) { it.getLocale() })
return result
} else {
return sourcesRepository.getEnabledSources().shuffled()
}
}
private suspend fun getList(
source: MangaSource,
tags: List<String>,

View File

@@ -20,7 +20,7 @@ import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra
import org.koitharu.kotatsu.core.util.ext.newImageRequest
import org.koitharu.kotatsu.databinding.ItemTrackDebugBinding
import org.koitharu.kotatsu.tracker.data.TrackEntity
import com.google.android.material.R as materialR
import androidx.appcompat.R as appcompatR
fun trackDebugAD(
lifecycleOwner: LifecycleOwner,
@@ -58,7 +58,7 @@ fun trackDebugAD(
if (item.lastResult == TrackEntity.RESULT_FAILED) {
append(" - ")
bold {
color(context.getThemeColor(materialR.attr.colorError, Color.RED)) {
color(context.getThemeColor(appcompatR.attr.colorError, Color.RED)) {
append(item.lastError ?: getString(R.string.error))
}
}

View File

@@ -74,19 +74,27 @@
tools:ignore="ContentDescription,UnusedAttribute" />
<TextView
android:id="@+id/textView_nsfw"
android:id="@+id/textView_nsfw_16"
style="@style/Widget.Kotatsu.TextView.Badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_indicator_offset"
android:background="@drawable/bg_chip"
android:backgroundTint="@color/warning"
android:gravity="center"
android:paddingHorizontal="4dp"
android:paddingVertical="2dp"
android:backgroundTint="@color/nsfw_16"
android:text="@string/nsfw_16"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="@id/imageView_cover"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_nsfw_18"
style="@style/Widget.Kotatsu.TextView.Badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_indicator_offset"
android:backgroundTint="@color/nsfw_18"
android:text="@string/nsfw"
android:textAlignment="center"
android:textAppearance="?textAppearanceLabelMedium"
android:textColor="?colorOnError"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />

View File

@@ -66,19 +66,27 @@
tools:ignore="ContentDescription,UnusedAttribute" />
<TextView
android:id="@+id/textView_nsfw"
android:id="@+id/textView_nsfw_16"
style="@style/Widget.Kotatsu.TextView.Badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_indicator_offset"
android:background="@drawable/bg_chip"
android:backgroundTint="@color/warning"
android:gravity="center"
android:paddingHorizontal="4dp"
android:paddingVertical="2dp"
android:backgroundTint="@color/nsfw_16"
android:text="@string/nsfw_16"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="@id/imageView_cover"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_nsfw_18"
style="@style/Widget.Kotatsu.TextView.Badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_indicator_offset"
android:backgroundTint="@color/nsfw_18"
android:text="@string/nsfw"
android:textAlignment="center"
android:textAppearance="?textAppearanceLabelMedium"
android:textColor="?colorOnError"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/imageView_cover"
app:layout_constraintEnd_toEndOf="@id/imageView_cover" />

View File

@@ -64,11 +64,8 @@
android:id="@+id/textView_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/custom_selectable_item_background"
android:padding="4dp"
android:singleLine="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAppearance="?textAppearanceBodyMedium"
app:layout_constrainedWidth="true"
app:layout_constraintBaseline_toBaselineOf="@id/textView_author_label"
@@ -87,7 +84,7 @@
android:text="@string/translation"
android:textAppearance="?textAppearanceTitleSmall"
app:layout_constraintStart_toStartOf="@id/card_details"
app:layout_constraintTop_toBottomOf="@id/textView_author_label" />
app:layout_constraintTop_toBottomOf="@id/textView_author" />
<TextView
android:id="@+id/textView_translation"

View File

@@ -11,7 +11,9 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:max="100" />
android:max="100"
app:hideAnimationBehavior="escape"
app:showAnimationBehavior="none" />
<LinearLayout
android:id="@+id/layout_error"
@@ -38,7 +40,7 @@
<Button
android:id="@+id/button_retry"
style="@style/Widget.Material3.Button.TonalButton"
style="?materialButtonTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
@@ -46,7 +48,7 @@
<Button
android:id="@+id/button_error_details"
style="@style/Widget.Material3.Button.TextButton"
style="?borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/details" />

View File

@@ -47,7 +47,9 @@
android:id="@+id/split_button_read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical">
android:layout_gravity="end|center_vertical"
android:paddingTop="0dp"
android:paddingBottom="0dp">
<Button
android:id="@+id/button_read"

View File

@@ -1,5 +1,4 @@
yaoi
yaoi(bl)
yuri
trap
traps

View File

@@ -320,8 +320,7 @@
<string name="no_chapters">لا توجد فصول</string>
<string name="allow_unstable_updates">السماح بالتحديثات غير مستقرة</string>
<string name="allow_unstable_updates_summary">تلقي إشعارات حول الإصدارات الغير مستقرة</string>
<string name="categories_delete_confirm">هل أنت متأكد أنك تريد حذف قوائم المُفضلة المحددة
\n؟ سيتم فقدان جميع المانجا فيها ولا يمكن التراجع عن هذا.</string>
<string name="categories_delete_confirm">هل أنت متأكد أنك تريد حذف قوائم المُفضلة المحددة \n؟ سيتم فقدان جميع المانجا فيها ولا يمكن التراجع عن هذا.</string>
<string name="pages_cache">بيانات التخزين المؤقت للصفحات</string>
<string name="not_found_404">المحتوى غير موجود أو تمت إزالته</string>
<string name="comics_archive_import_description">يمكنك اختيار ملف أو أكثر بتنسيق cbz أو zip ، سيتم التعرف على كل ملف على أنه مانغا منفصلة.</string>

View File

@@ -275,8 +275,7 @@
<string name="clear_all_history">Ачысціць усю гісторыю</string>
<string name="history_cleared">Гісторыя ачышчана</string>
<string name="incognito_mode">Рэжым інкогніта</string>
<string name="categories_delete_confirm">Вы ўпэўнены, што хочаце выдаліць выбраныя абраныя катэгорыі?
\nУся манга ў ім будзе страчана, і гэта нельга будзе адрабіць.</string>
<string name="categories_delete_confirm">Вы ўпэўнены, што хочаце выдаліць выбраныя абраныя катэгорыі? \nУся манга ў ім будзе страчана, і гэта нельга будзе адрабіць.</string>
<string name="no_bookmarks_summary">Вы можаце стварыць закладку падчас чытання мангі</string>
<string name="saved_manga">Захаваная манга</string>
<string name="theme_name_mamimi">Мамімі</string>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="items">
<item quantity="one">%1$d টি আইটেম</item>
<item quantity="one">%1$d আইটেম</item>
<item quantity="other">%1$d টি আইটেম</item>
</plurals>
<plurals name="new_chapters">
@@ -28,4 +28,12 @@
<item quantity="one">মাত্র %1$d মিনিট আগে</item>
<item quantity="other">%1$d মিনিট আগে</item>
</plurals>
</resources>
<plurals name="minutes">
<item quantity="one">%1$d মিনিট</item>
<item quantity="other">%1$d টি মিনিট</item>
</plurals>
<plurals name="hours">
<item quantity="one">%1$d ঘন্টা</item>
<item quantity="other">%1$d টি ঘন্টা</item>
</plurals>
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="favourites">পছন্দের গুলো</string>
<string name="history">ইতিহাস</string>
<string name="local_storage">লোকাল স্টোরেজ</string>
@@ -13,7 +13,7 @@
<string name="open_in_browser">ব্রাউজারে খুলুন</string>
<string name="error_occurred">কিছু একটা সমস্যা হয়েছে</string>
<string name="details">খুঁটিনাটি</string>
<string name="chapters">পর্ব সমূহ</string>
<string name="chapters">অধ্যায়</string>
<string name="list">তালিকা</string>
<string name="detailed_list">পুঙ্খানুপুঙ্খ তালিকা</string>
<string name="grid">গ্রিড</string>
@@ -71,7 +71,7 @@
<string name="remove">সরিয়ে ফেলুন</string>
<string name="_s_deleted_from_local_storage">\"%s\" লোকাল স্টোরেজ থেকে সরানো হয়েছে</string>
<string name="save_page">পেজ সেভ করুন</string>
<string name="page_saved">সেভ হয়েছে</string>
<string name="page_saved">Page saved</string>
<string name="standard">স্ট্যান্ডার্ড</string>
<string name="search_on_s">%s এ খুঁজুন</string>
<string name="delete_manga">মানগা ডিলিট করুন</string>
@@ -95,11 +95,11 @@
<string name="cannot_find_available_storage">সঞ্চয়স্থান উপলব্ধ নয়</string>
<string name="ignore_ssl_errors">SSL ত্রুটি উপেক্ষা করুন</string>
<string name="server_address">সার্ভার ঠিকানা</string>
<string name="text_feed_holder">আপনি যা পড়ছেন তার নতুন অধ্যায় এখানে দেখানো হয়েছে</string>
<string name="text_feed_holder">New chapters of what you are reading are shown here</string>
<string name="favourites_category_empty">খালি বিভাগ</string>
<string name="pause">বিরতি</string>
<string name="remove_completed">সম্পূর্ণভাবে সরান</string>
<string name="manga_save_location">ডাউনলোডের জন্য ফোল্ডার</string>
<string name="manga_save_location">Downloads folder</string>
<string name="suggestions_notifications_summary">কখনও কখনও প্রস্তাবিত মাঙ্গা সহ বিজ্ঞপ্তিগুলি দেখান৷</string>
<string name="updates_feed_cleared">সাফ করা হয়েছে</string>
<string name="list_mode">তালিকার ধরন</string>
@@ -146,9 +146,34 @@
<string name="repeat_password">পাসওয়ার্ড পুনরাবৃত্তি করুন</string>
<string name="mirror_switching_summary">মিরর উপলব্ধ থাকলে ত্রুটির মাঙ্গা উত্সগুলির জন্য স্বয়ংক্রিয়ভাবে ডোমেনগুলি পরিবর্তন করুন৷</string>
<string name="no_thanks">না ধন্যবাদ</string>
<string name="text_local_holder_secondary">এটি অনলাইন উত্স থেকে সংরক্ষণ করুন বা ফাইল আমদানি করুন</string>
<string name="text_local_holder_secondary">Save something from an online catalog or import it from a file</string>
<string name="text_local_holder_primary">আগে কিছু সংরক্ষণ করুন</string>
<string name="suggestion_manga">পরামর্শ: %s</string>
<string name="text_empty_holder_primary">এখানে খালি…</string>
<string name="done">সম্পন্ন</string>
</resources>
<string name="text_empty_holder_secondary_filtered">There are no manga matching the filters you selected</string>
<string name="zoom_mode_fit_height">Fit to height</string>
<string name="black_dark_theme">Black</string>
<string name="pages_saved">Pages saved</string>
<string name="zoom_mode_fit_width">Fit to width</string>
<string name="zoom_mode_keep_start">Keep at start</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d of %2$d on</string>
<string name="retry">পুনরায় চেষ্টা করুন</string>
<string name="color_theme">কালার স্কিম</string>
<string name="content_type_novel">উপন্যাস</string>
<string name="content_type_manhua">মানহুয়া</string>
<string name="prefetch_content">কন্টেন্ট প্রিলোড হচ্ছে</string>
<string name="mark_as_current">বর্তমান হিসেবে চিহ্নিত করুন</string>
<string name="compact">কম্প্যাক্ট</string>
<string name="show_in_grid_view">গ্রিড ভিউতে দেখান</string>
<string name="language">ভাষা</string>
<string name="share_logs">লগ শেয়ার করুন</string>
<string name="enable_logging">লগিং সক্ষম করুন</string>
<string name="show_suspicious_content">সন্দেহজনক কন্টেন্ট দেখান</string>
<string name="source_disabled">উৎস অক্ষম করা হয়েছে</string>
<string name="delete_old_backups">পুরনো ব্যাকআপ মুছে ফেলুন</string>
<string name="enable_logging_summary">ডিবাগের উদ্দেশ্যে কিছু অ্যাকশন রেকর্ড করুন। আপনি কী করছেন তা নিশ্চিত না হলে এটি চালু করবেন না</string>
<string name="theme_name_dynamic">ডাইনামিক</string>
<string name="theme_name_miku">মিকু</string>
<string name="data_not_restored">ডেটা পুনরুদ্ধার করা হয়নি</string>
</resources>

View File

@@ -74,4 +74,4 @@
<string name="delete_manga">Elimina manga</string>
<string name="reader_settings">Configuració del lector</string>
<string name="clear_search_history">Esborrar l\'historial de cerca</string>
</resources>
</resources>

View File

@@ -221,8 +221,7 @@
<string name="bookmarks_removed">Záložky odstraněny</string>
<string name="no_manga_sources">Žádné zdroje mang</string>
<string name="no_manga_sources_text">Zapnout zdroje mang pro čtení online</string>
<string name="categories_delete_confirm">Jste si jisti že chcete smazat zvolené oblíbené kategorie\?
\nVšechny mangy v ní budou ztraceny a nelze jej vrátit zpět.</string>
<string name="categories_delete_confirm">Jste si jisti že chcete smazat zvolené oblíbené kategorie? \nVšechny mangy v ní budou ztraceny a nelze jej vrátit zpět.</string>
<string name="reorder">Přeskupit</string>
<string name="empty">Prázdné</string>
<string name="explore">Prozkoumat</string>

View File

@@ -296,8 +296,7 @@
<string name="no_bookmarks_summary">Du kannst beim Lesen von Mangas Lesezeichen erstellen</string>
<string name="bookmarks_removed">Lesezeichen entfernt</string>
<string name="random">Zufällig</string>
<string name="categories_delete_confirm">Bist du sicher, dass du die ausgewählten Lieblingskategorien löschen möchtest\?
\nAlle darin enthaltenen Manga gehen verloren und das kann nicht rückgängig gemacht werden.</string>
<string name="categories_delete_confirm">Bist du sicher, dass du die ausgewählten Lieblingskategorien löschen möchtest? \nAlle darin enthaltenen Manga gehen verloren und das kann nicht rückgängig gemacht werden.</string>
<string name="reorder">Neu anordnen</string>
<string name="empty">Leer</string>
<string name="explore">Erkunden</string>
@@ -634,4 +633,4 @@
<string name="recent_queries">Kürzliche Suchen</string>
<string name="suggested_queries">Vorgeschlagene Suchen</string>
<string name="authors">Autoren</string>
</resources>
</resources>

View File

@@ -443,8 +443,7 @@
\n
\nΠροσοχή: η πρόοδος σου θα χαθεί.</string>
<string name="disable_battery_optimization_summary_downloads">Ενδέχεται να βοηθήσει με την εκκίνηση της λήψης αν αντιμετωπίζεις προβλήματα σχετικά με αυτήν</string>
<string name="categories_delete_confirm">Είσαι σίγουρος πως θες να διαγράψεις τις επιλεγμένες αγαπημένες κατηγορίες;
\nΌλα τα manga που ανήκουν σε αυτές θα χαθούν, και αυτό είναι μη αναστρέψιμο.</string>
<string name="categories_delete_confirm">Είσαι σίγουρος πως θες να διαγράψεις τις επιλεγμένες αγαπημένες κατηγορίες; \nΌλα τα manga που ανήκουν σε αυτές θα χαθούν, και αυτό είναι μη αναστρέψιμο.</string>
<string name="downloads_resumed">Οι λήψεις έχουν ξαναξεκινήσει</string>
<string name="downloads_paused">Οι λήψεις παύθηκαν</string>
<string name="remove_completed_downloads_confirm">Το ιστορικό λήψεων σου θα διαγραφεί</string>
@@ -535,4 +534,4 @@
<string name="appwidget_recent_description">Τα πρόσφατα διαβασμένα manga σου</string>
<string name="clear_cookies_summary">Μπορεί να βοηθήσει σε περίπτωση κάποιων προβλημάτων. Όλες οι εξουσιοδοτήσεις θα ανακληθούν</string>
<string name="category_hidden_done">Αυτή η κατηγορία αποκρύφτηκε από την αρχική οθόνη και είναι προσβάσιμη μέσω του Μενού → Διαχείριση κατηγοριών</string>
</resources>
</resources>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="favourites">Favourites</string>
<string name="you_have_not_favourites_yet">No favourites yet</string>
<string name="add_to_favourites">Favourite this</string>
<string name="favourites_categories">Favourite categories</string>
<string name="all_favourites">All favourites</string>
<string name="backup_information">You can create backup of your history and favourites and restore it</string>
<string name="empty_favourite_categories">No favourite categories</string>
<string name="appwidget_shelf_description">Manga from your favourites</string>
<string name="show_reading_indicators_summary">Show percentage read in history and favourites</string>
<string name="categories_delete_confirm">Are you sure you want to delete the selected favourite categories?\nAll manga in it will be lost and this cannot be undone.</string>
<string name="removed_from_favourites">Removed from favourites</string>
<string name="migrate_confirmation">Manga \"%1$s\" from \"%2$s\" will be replaced with \"%3$s\" from \"%4$s\" in your history and favourites (if present)</string>
<string name="not_in_favorites">Not in favourites</string>
</resources>

View File

@@ -298,8 +298,7 @@
<string name="manage">Gestionar</string>
<string name="confirm_exit">Pulsa de nuevo «Atrás» para salir</string>
<string name="folder_with_images">Carpeta con imágenes</string>
<string name="categories_delete_confirm">¿Estás seguro de que quieres eliminar las categorías favoritas seleccionadas\?
\nTodos los mangas en ella se perderán y esto no se puede deshacer.</string>
<string name="categories_delete_confirm">¿Estás seguro de que quieres eliminar las categorías favoritas seleccionadas? \nTodos los mangas en ella se perderán y esto no se puede deshacer.</string>
<string name="explore">Explorar</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="exit_confirmation_summary">Pulse dos veces «Atrás» para salir de la aplicación</string>
@@ -769,4 +768,4 @@
<string name="open_telegram_bot_summary">Presione para abrir el chat con Kotatsu Backup Bot</string>
<string name="send_backups_telegram">Enviar copias de seguridad por Telegram</string>
<string name="telegram_chat_id_summary">Ingrese el ID del chat donde se deben enviar las copias de seguridad</string>
</resources>
</resources>

View File

@@ -210,8 +210,7 @@
<string name="text_file_not_supported">Vali kas ZIP või CBZ fail.</string>
<string name="download">Lae alla</string>
<string name="chapter_is_missing">See peatükk puudub</string>
<string name="categories_delete_confirm">Kas sa oled kindel, et tahad kustutada valitud lemmikud kategooriad?
\nKõik manga nende sees kaob ja seda tagasi ei saa.</string>
<string name="categories_delete_confirm">Kas sa oled kindel, et tahad kustutada valitud lemmikud kategooriad? \nKõik manga nende sees kaob ja seda tagasi ei saa.</string>
<string name="logged_in_as">Logitud sisse kui %s</string>
<string name="suggestions_info">Kõik andmed analüüsitakse ainult lokaalselt selles seadmes ja neid ei saadeta kunagi kuhugi.</string>
<string name="reader_info_pattern">Pt. %1$d/%2$d Lk. %3$d/%4$d</string>

View File

@@ -280,4 +280,4 @@
<string name="report">گزارش</string>
<string name="status_planned">برنامه‌ریزی شده</string>
<string name="show_reading_indicators_summary">نشان دادن درصد خوانده شده در تاریخچه و پسندیده‌ها</string>
</resources>
</resources>

View File

@@ -24,4 +24,8 @@
<item quantity="one">%1$d päivä sitten</item>
<item quantity="other">%1$d päivää sitten</item>
</plurals>
</resources>
<plurals name="months_ago">
<item quantity="one">%1$d kuukausi sitten</item>
<item quantity="other">%1$d kuukausia sitten</item>
</plurals>
</resources>

View File

@@ -259,4 +259,4 @@
<string name="show_all">Näytä kaikki</string>
<string name="select_range">Valitse alue</string>
<string name="not_found_404">Sisältöä ei löydy tai se on poistettu</string>
</resources>
</resources>

View File

@@ -318,8 +318,7 @@
<string name="status_reading">Nagbabasa</string>
<string name="pages_cache">Cache ng mga pahina</string>
<string name="bookmarks">Mga bookmark</string>
<string name="categories_delete_confirm">Sigurado ka bang gusto mong tanggalin ang mga napiling paboritong kategorya\?
\nAng lahat ng manga sa loob nito ay mawawala at hindi na ito mababawi.</string>
<string name="categories_delete_confirm">Sigurado ka bang gusto mong tanggalin ang mga napiling paboritong kategorya? \nAng lahat ng manga sa loob nito ay mawawala at hindi na ito mababawi.</string>
<string name="bookmark_added">Idinagdag ang bookmark</string>
<string name="detect_reader_mode_summary">Awtomatikong matukoy kung ang manga ay webtoon</string>
<string name="disable_battery_optimization">Di paganahin ang pag-optimize ng baterya</string>

View File

@@ -271,7 +271,7 @@
<string name="not_found_404">Contenu non trouvé ou supprimé</string>
<string name="manga_error_description_pattern">Détails de l\'erreur:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Essayez d\'&lt;a href=%2$s&gt;ouvrir le manga dans un navigateur web&lt;/a&gt; pour vous assurer qu\'il est disponible sur sa source&lt;br&gt;2. Assurez-vous que vous utilisez la &lt;a href=kotatsu://about&gt;dernière version de Kotatsu&lt;/a&gt;&lt;br&gt;3. Si elle est disponible, envoyez un rapport d\'erreur aux développeurs.</string>
<string name="confirm_exit">Appuyez à nouveau sur Retour pour quitter</string>
<string name="categories_delete_confirm">Êtes-vous sûr(e) de vouloir supprimer les catégories de favoris sélectionnées?\nTous les mangas qui s\'y trouvent seront perdus et ceci ne peut pas être annulé.</string>
<string name="categories_delete_confirm">Êtes-vous sûr(e) de vouloir supprimer les catégories de favoris sélectionnées ?\nTous les mangas qui s\'y trouvent seront perdus et ceci ne peut pas être annulé.</string>
<string name="exit_confirmation_summary">Appuyez deux fois sur la touche Retour pour quitter l\'appli</string>
<string name="available">Disponible</string>
<string name="exit_confirmation">Confirmation de sortie</string>
@@ -797,4 +797,9 @@
<string name="badges_in_lists">Badges dans les listes</string>
<string name="no_write_permission_to_file">N\'a pas la permission d\'écrire un fichier</string>
<string name="clear_browser_data">Effacer les données du navigateur</string>
<string name="clear_browser_data_summary">Efface les données du navigateur telles que le cache et les cookies. Avertissement : l\'autorisation sur les sources de mangas peut devenir invalide</string>
<string name="include_disabled_sources">Inclure les sources désactivées</string>
<string name="nsfw_16">16+</string>
<string name="exclude_nsfw_from_suggestions_summary">Les mangas pour adultes ne seront pas affichés dans les suggestions. Cette option peut fonctionner de manière inexacte avec certaines sources</string>
<string name="suggestions_disabled_sources_summary">Afficher les suggestions de toutes les sources de mangas, y compris celles désactivées</string>
</resources>

View File

@@ -542,8 +542,7 @@
<string name="allow_unstable_updates">अस्थिर अद्यतन की अनुमति दें</string>
<string name="nothing_here">यहां कुछ नहीं है</string>
<string name="allow_unstable_updates_summary">अस्थिर बिल्ड के बारे में सूचनाएं प्राप्त करें</string>
<string name="categories_delete_confirm">क्या आप वाकई चयनित पसंदीदा श्रेणियां मिटाना चाहते हैं?
\nइसमें मौजूद सारा मंगा नष्ट हो जाएगा और इसे पूर्ववत नहीं किया जा सकता।</string>
<string name="categories_delete_confirm">क्या आप वाकई चयनित पसंदीदा श्रेणियां मिटाना चाहते हैं? \nइसमें मौजूद सारा मंगा नष्ट हो जाएगा और इसे पूर्ववत नहीं किया जा सकता।</string>
<string name="manga_error_description_pattern">त्रुटि विवरण:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1। यह सुनिश्चित करने के लिए कि यह अपने स्रोत पर उपलब्ध है &lt;a href=%2$s&gt;मंगा को वेब ब्राउज़र में खोलने का प्रयास करें&lt;/a&gt;&lt;br&gt;2। सुनिश्चित करें कि आप &lt;a href=kotatsu://about&gt;Kotatsu के नवीनतम संस्करण&lt;/a&gt;&lt;br&gt;3 का उपयोग कर रहे हैं। यदि यह उपलब्ध है, तो डेवलपर्स को एक त्रुटि रिपोर्ट भेजें।</string>
<string name="folder_with_images_import_description">आप अभिलेखों या छवियों वाली एक निर्देशिका का चयन कर सकते हैं। प्रत्येक संग्रह (या उपनिर्देशिका) को एक अध्याय के रूप में पहचाना जाएगा।</string>
<string name="images_procy_description">यदि संभव हो तो ट्रैफिक उपयोग को कम करने और छवि लोडिंग को तेज़ करने के लिए wsrv.nl सेवा का उपयोग करें</string>
@@ -667,4 +666,4 @@
<string name="pages_saved">सहेजे गए पृष्ठ</string>
<string name="invalid_server_address_message">अमान्य सर्वर पता</string>
<string name="retry">पुनः प्रयत्न करें</string>
</resources>
</resources>

View File

@@ -229,8 +229,7 @@
<string name="no_manga_sources">Nema izvora mange</string>
<string name="no_manga_sources_text">Omogućite izvore mange za čitanje mange na mreži</string>
<string name="random">Nasumično</string>
<string name="categories_delete_confirm">Jeste li sigurni da želite izbrisati odabrane omiljene kategorije?
\nSve mange u njoj bit će izgubljene i to se ne može poništiti.</string>
<string name="categories_delete_confirm">Jeste li sigurni da želite izbrisati odabrane omiljene kategorije? \nSve mange u njoj bit će izgubljene i to se ne može poništiti.</string>
<string name="reader_info_pattern">Poglavlje %1$d/%2$d Stranica %3$d/%4$d</string>
<string name="reader_info_bar">Prikaži informacijsku traku u čitaču</string>
<string name="comics_archive">Arhiva stripova</string>

View File

@@ -368,8 +368,7 @@
<string name="show_reading_indicators_summary">A haladás (százalékosan) megjelenítése az előzményekben és kedvencekben</string>
<string name="exclude_nsfw_from_history_summary">A NSFW jelzésű mangák soha nem kerülnek hozzáadásra az előzményekhez, és a haladásod nem lesz mentve</string>
<string name="clear_cookies_summary">Segíthet néhány probléma esetén. Minden hitelesítés érvényét veszti</string>
<string name="categories_delete_confirm">Biztosan törölni szeretnéd a kiválasztott kedvenc kategóriákat?
\nMinden mangája elveszik, és ez nem visszavonható.</string>
<string name="categories_delete_confirm">Biztosan törölni szeretnéd a kiválasztott kedvenc kategóriákat? \nMinden mangája elveszik, és ez nem visszavonható.</string>
<string name="history_shortcuts">Aktuális manga hivatkozásai megjelenítése</string>
<string name="history_shortcuts_summary">Tedd elérhetővé a legújabb mangákat az alkalmazás ikonjának hosszú megnyomásával</string>
<string name="network_unavailable_hint">Kapcsold be a Wi-Fi-t vagy a mobilhálózatot, hogy online olvashass mangát</string>
@@ -618,4 +617,4 @@
<string name="screenshots_block_incognito">Blokkold inkognitó módban</string>
<string name="crop_pages">Lap kivágása</string>
<string name="image_server">Előnyben részesített kiszolgáló</string>
</resources>
</resources>

View File

@@ -38,7 +38,7 @@
<string name="newest">Terbaru</string>
<string name="by_rating">Nilai</string>
<string name="sort_order">Urutkan</string>
<string name="filter">Filter</string>
<string name="filter">Saring</string>
<string name="theme">Tema</string>
<string name="light">Terang</string>
<string name="dark">Gelap</string>
@@ -644,7 +644,7 @@
<string name="suggested_queries">Pertanyaan yang disarankan</string>
<string name="source_pinned">Sumber disematkan</string>
<string name="recent_sources">Sumber sumbver terbaru</string>
<string name="text_empty_holder_secondary_filtered">Tidak ada manga yang cocok dengan filter yang Anda pilih</string>
<string name="text_empty_holder_secondary_filtered">Tidak ada manga sesuai filter yang ditemukan</string>
<string name="image_server">Server gambar yang dipilih</string>
<string name="crop_pages">Potong Halaman</string>
<string name="pin">Sematkan</string>
@@ -758,20 +758,49 @@
<string name="rating">Nilai</string>
<string name="source">Sumber</string>
<string name="added_long_ago">Bahasa</string>
<string name="popular_in_hour">Daftar</string>
<string name="popular_in_hour">Populer Dalam Satu Jam Terakhir</string>
<string name="stuck">Bahasa</string>
<string name="error_connection_reset">Koneksi diatur ulang oleh host jarak jauh</string>
<string name="incognito">Penyamaran</string>
<string name="demographic_kodomo">Kodomo</string>
<string name="incognito">Mode Penyamaran</string>
<string name="demographic_kodomo">Anak</string>
<string name="content_type_one_shot">Satu tembakan</string>
<string name="content_type_doujinshi">Film Doujinshi</string>
<string name="content_type_doujinshi">Doujinshi</string>
<string name="content_type_artist_cg">Artis CG</string>
<string name="reader_info_bar_transparent">Bilah informasi pembaca transparan</string>
<string name="handle_links">Menangani tautan</string>
<string name="handle_links">Tangani Tautan</string>
<string name="handle_links_summary">Menangani tautan manga dari aplikasi eksternal (misalnya browser web). Anda mungkin juga perlu mengaktifkannya secara manual di pengaturan sistem aplikasi.</string>
<string name="any">Setiap</string>
<string name="author">Pengarang</string>
<string name="plugin_incompatible_with_cause">Pastikan Anda menggunakan plugin dan Kotatsu versi terbaru</string>
<string name="restoring_backup">Memulihkan Cadangan</string>
<string name="backup_restored_background">Pencadangan akan dipulihkan di latar belakang</string>
<string name="clear_browser_data">Hapus data peramban</string>
<string name="error_details">Detail kesalahan</string>
<string name="no_write_permission_to_file">Tidak memiliki izin untuk menulis file</string>
<string name="error_disclaimer_manga">Cobalah membuka manga di peramban web untuk memastikan manga tersedia di sumbernya.</string>
<string name="error_disclaimer_report">Anda dapat mengirimkan laporan bug kepada pengembang. Ini akan membantu kami menyelidiki dan memperbaiki masalah tersebut.</string>
<string name="clear_browser_data_summary">Hapus data browser seperti cache dan cookie. Peringatan: Otorisasi dalam sumber manga mungkin menjadi tidak valid</string>
<string name="include_disabled_sources">Sertakan sumber yang dinonaktifkan</string>
<string name="exclude_nsfw_from_suggestions_summary">Manga dewasa tidak akan ditampilkan dalam saran. Pilihan ini mungkin tidak akurat pada beberapa</string>
<string name="search_disabled_sources">Cari melalui sumber yang dinonaktifkan</string>
<string name="content_type_game_cg">Permainan CG</string>
<string name="unnamed_chapter">Bab tanpa nama</string>
<string name="chapter_volume_number">Vol %1$sBab%2$s</string>
<string name="chapter_number">Bab%s</string>
<string name="simple">Simpel</string>
<string name="reader_controls_in_bottom_bar">Kontrol pembaca di bilah bawah</string>
<string name="chapters_and_pages">Bab dan halaman</string>
<string name="pages_slider">Penggeser pengalih halaman</string>
<string name="screen_rotation_locked">Rotasi layar dikunci</string>
<string name="screen_rotation_unlocked">Rotasi layar telah dibuka kuncinya</string>
<string name="error_disclaimer_app_outdated">Sepertinya versi Kotatsu Anda sudah kedaluwarsa. Harap instal versi terbaru untuk mendapatkan semua perbaikan yang tersedia.</string>
<string name="nsfw_16">16+</string>
<string name="link_to_manga_on_s">Tautan ke manga di%s</string>
<string name="link_to_manga_in_app">Tautan ke manga di kotatsu</string>
<string name="suggestions_disabled_sources_summary">Tampilkan saran dari semua sumber manga, termasuk yang dinonaktifkan</string>
<string name="disable_captcha_notifications">Nonaktifkan notifikasi captcha</string>
<string name="disable_captcha_notifications_summary">Anda tidak akan menerima pemberitahuan tentang penyelesaian CAPTCHA untuk sumber ini, tetapi hal ini dapat menyebabkan penghentian operasi latar belakang (memeriksa bab baru, memperoleh rekomendasi, dll.)</string>
<string name="global_search">"Pencarian global"</string>
<string name="search_everywhere">Cari dimana saja</string>
<string name="badges_in_lists">Lencana dalam daftar</string>
</resources>

View File

@@ -328,8 +328,7 @@
<string name="history_shortcuts">Mostra i collegamenti ai manga recenti</string>
<string name="reader_control_ltr">Controllo ergonomico del lettore</string>
<string name="brightness">Luminosità</string>
<string name="categories_delete_confirm">Sei sicuro/a di voler eliminare le categorie preferite selezionate\?
\n Tutti i manga in esso contenuti andranno persi e questo non può essere annullato.</string>
<string name="categories_delete_confirm">Sei sicuro/a di voler eliminare le categorie preferite selezionate? \n Tutti i manga in esso contenuti andranno persi e questo non può essere annullato.</string>
<string name="history_shortcuts_summary">Rendere disponibili i manga recenti premendo a lungo sull\'icona dell\'applicazione</string>
<string name="reader_info_bar">Mostra la barra delle informazioni nel lettore</string>
<string name="manga_error_description_pattern">Dettagli dell\'errore:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Prova ad &lt;a href=%2$s&gt;aprire il manga in un browser web&lt;/a&gt; per assicurarsi che sia disponibile sulla sua fonte&lt;br&gt;2. Controllare di stare usando la &lt;a href=kotatsu://about&gt;versione più recente di Kotatsu&lt;/a&gt;&lt;br&gt;3. Se è disponibile, inviare una segnalazione di errore agli sviluppatori.</string>
@@ -714,7 +713,7 @@
<string name="telegram_group">Gruppo Telegram</string>
<string name="screen_orientation">Orientamento schermo</string>
<string name="chapters_all">Tutti</string>
<string name="save_manga_confirm">Salvare il manga selezionato? Potrebbe utilizzare traffico dati e spazio nella memoria interna.</string>
<string name="save_manga_confirm">Salvare il manga selezionato? Potrebbe utilizzare traffico dati e spazio nella memoria interna</string>
<string name="error_not_image">Formato non valido: prevista un\'immagine ma ricevuto %s</string>
<string name="download_added">Download aggiunto</string>
<string name="more_options">Più opzioni</string>
@@ -801,4 +800,8 @@
<string name="clear_browser_data_summary">Pulisci i dati del browser, come la cache e i cookie. Attenzione: l\'autorizzazione nelle fonti manga potrebbe diventare non valida</string>
<string name="clear_browser_data">Pulisci dati del browser</string>
<string name="no_write_permission_to_file">Non ha l\'autorizzazione a scrivere un file</string>
<string name="nsfw_16">16+</string>
<string name="include_disabled_sources">Includi fonti disabilitate</string>
<string name="suggestions_disabled_sources_summary">Mostra suggerimenti da tutte le fonti manga, incluse quelle disabilite</string>
<string name="exclude_nsfw_from_suggestions_summary">I manga per adulti non verranno mostrati nei suggerimenti. Questa opzione potrebbe non funzionare accuratamente con alcune fonti</string>
</resources>

View File

@@ -271,8 +271,7 @@
<string name="not_found_404">コンテンツが見つからない、または削除された</string>
<string name="manage">管理</string>
<string name="random">ランダム</string>
<string name="categories_delete_confirm">選択したお気に入りカテゴリを本当に削除してもよいですか?
\nその中にあるマンガはすべて失われ、元に戻すことはできません。</string>
<string name="categories_delete_confirm">選択したお気に入りカテゴリを本当に削除してもよいですか? \nその中にあるマンガはすべて失われ、元に戻すことはできません。</string>
<string name="empty"></string>
<string name="explore">探索</string>
<string name="exit_confirmation_summary">アプリを終了するには、戻るを2回押してください</string>
@@ -463,4 +462,4 @@
<string name="reader_zoom_buttons">ズームボタンを表示</string>
<string name="zoom_out">ズームアウト</string>
<string name="retry">リトライ</string>
</resources>
</resources>

View File

@@ -398,8 +398,7 @@
<string name="invalid_port_number">Порттың нөмірі қате</string>
<string name="suggestions_wifi_only_summary">Шектеулі желі қосылымы болса ұсынысты жаңартпау</string>
<string name="webtoon_zoom_summary">Уебтүн режімінде ұлғайту ымына рұқсат ету</string>
<string name="categories_delete_confirm">Таңдаулы санатты шынымен жойғыңыз келе ме\?
\nІшіндегі бар маңга жойылады, сосын оны қайтарып ала алмайсыз.</string>
<string name="categories_delete_confirm">Таңдаулы санатты шынымен жойғыңыз келе ме? \nІшіндегі бар маңга жойылады, сосын оны қайтарып ала алмайсыз.</string>
<string name="frequency_once_per_month">Ай сайын</string>
<string name="reader_info_pattern">%1$d/%2$d-тарау %3$d/%4$d-бет</string>
<string name="contrast">Көреғарлық</string>
@@ -538,4 +537,4 @@
<string name="reader_actions">Оқымадағы әрекет</string>
<string name="sync_auth">Синхрондау тіркелгісіне кіру</string>
<string name="config_reset_confirm">Әдепкі баптауға қайтайық па? Әрекетті қайтаруға болмайды.</string>
</resources>
</resources>

View File

@@ -129,4 +129,4 @@
<string name="sources_pinned">ខ្ទាស់​ប្រភពទាំងអស់</string>
<string name="unpin">ដកខ្ទាស់</string>
<string name="pin">ដាក់ខ្ទស់</string>
</resources>
</resources>

View File

@@ -269,8 +269,7 @@
<string name="no_bookmarks_summary">만화를 읽는 도중 북마크를 추가할 수 있습니다</string>
<string name="bookmarks_removed">북마크 제거됨</string>
<string name="no_manga_sources_text">만화를 읽기 위해 만화 소스 사이트를 활성화 하세요</string>
<string name="categories_delete_confirm">정말 선택된 즐겨 찾는 카테고리를 삭제하시겠습니까\?
\n해당 카테고리의 모든 만화가 손실되며 취소할 수 없습니다.</string>
<string name="categories_delete_confirm">정말 선택된 즐겨 찾는 카테고리를 삭제하시겠습니까? \n해당 카테고리의 모든 만화가 손실되며 취소할 수 없습니다.</string>
<string name="empty">비어있음</string>
<string name="confirm_exit">뒤로가기 버튼을 다시 눌러 나가기</string>
<string name="exit_confirmation_summary">뒤로가기 버튼을 두 번 눌러 앱을 종료할 수 있습니다</string>
@@ -337,4 +336,4 @@
<string name="reset">초기화</string>
<string name="text_unsaved_changes_prompt">저장되지 않은 변경 사항을 저장하거나 삭제하시겠습니까\?</string>
<string name="manga_error_description_pattern">오류 세부정보:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. &lt;a href=%2$s&gt;웹 브라우저에서 만화를 열어&lt;/a&gt; 소스에서 사용할 수 있는지 확인하세요&lt;br&gt;2. &lt;a href=kotatsu://about&gt;최신 버전의 Kotatsu&lt;/a&gt;&lt;br&gt;를 사용하고 있는지 확인하세요.3. 사용 가능한 경우 개발자에게 오류 보고서를 보냅니다.</string>
</resources>
</resources>

View File

@@ -29,4 +29,4 @@
<string name="search_manga">Ieškoti manga</string>
<string name="remote_sources">Manga šaltiniai</string>
<string name="manga_downloading_">Atsisiunčiama…</string>
</resources>
</resources>

View File

@@ -161,4 +161,4 @@
<string name="captcha_solve">Risināt</string>
<string name="clear_cookies">Attīrīt sīkdatnes</string>
<string name="cookies_cleared">Visas sīkdatnes noņemtas</string>
</resources>
</resources>

View File

@@ -8,4 +8,4 @@
<string name="detailed_list">വിശദമായ ലിസ്റ്റ്</string>
<string name="chapter_d_of_d">%2$d-ൻ്റെ %1$d അധ്യായം</string>
<string name="try_again">വീണ്ടും ശ്രമിക്കുക</string>
</resources>
</resources>

View File

@@ -215,8 +215,7 @@
<string name="no_manga_sources">Tiada sumber manga</string>
<string name="no_manga_sources_text">Bolehkan sumber manga untuk membaca manga di dalam talian</string>
<string name="random">Rawak</string>
<string name="categories_delete_confirm">Adakah anda pasti anda mahu membuang kategori kegemaran yang dipilih?
\nSemua manga di dalamnya akan hilang dan ini tidak boleh diundur semula.</string>
<string name="categories_delete_confirm">Adakah anda pasti anda mahu membuang kategori kegemaran yang dipilih? \nSemua manga di dalamnya akan hilang dan ini tidak boleh diundur semula.</string>
<string name="reorder">Susun semula</string>
<string name="empty">Kosong</string>
<string name="explore">Jelajah</string>
@@ -303,4 +302,4 @@
<string name="error_corrupted_file">Data tidak sah dikembalikan atau fail rosak</string>
<string name="on_device">Pada peranti</string>
<string name="directories">Panduan</string>
</resources>
</resources>

View File

@@ -289,4 +289,4 @@
<string name="enable_logging">Skru på loggføring</string>
<string name="storage_usage">Lagringsbruk</string>
<string name="sync_auth_hint">Du kan logge med en eksisterende konto, eller opprette en ny</string>
</resources>
</resources>

Some files were not shown because too many files have changed in this diff Show More