Compare commits

...

55 Commits
v9.1.2 ... v9.2

Author SHA1 Message Date
Koitharu
be012f631a Update parsers 2025-09-23 18:50:44 +03:00
Koitharu
0165f43603 Fix crash 2025-09-23 18:43:20 +03:00
Koitharu
55801a1488 Update parsers 2025-09-21 13:28:38 +03:00
Koitharu
77103f016f Update parsers 2025-09-21 13:27:22 +03:00
DashZero
6b6719a259 Translated using Weblate (Thai)
Currently translated at 50.9% (446 of 875 strings)

Co-authored-by: DashZero <mee_original@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/th/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Joe
822642abb0 Translated using Weblate (Belarusian)
Currently translated at 99.6% (872 of 875 strings)

Co-authored-by: Joe <happenstance@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
தமிழ்நேரம்
260745fb95 Translated using Weblate (Tamil)
Currently translated at 100.0% (875 of 875 strings)

Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ta/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
MuhamadSyabitHidayattulloh
024ec0388f Translated using Weblate (Indonesian)
Currently translated at 100.0% (875 of 875 strings)

Co-authored-by: MuhamadSyabitHidayattulloh <tebepc@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/id/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Draken
5345998eec Translated using Weblate (Vietnamese)
Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (869 of 869 strings)

Co-authored-by: Draken <premieregirl26@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/vi/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Nicola Bortoletto
3d56190e71 Translated using Weblate (Italian)
Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (869 of 869 strings)

Co-authored-by: Nicola Bortoletto <nicola.bortoletto@live.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Shams deen
954431d0a5 Added translation using Weblate (Arabic (Egyptian))
Added translation using Weblate (Arabic (Egyptian))

Co-authored-by: Shams deen <shamsdeen84@gmail.com>
2025-09-21 13:26:53 +03:00
return_null
afec63b443 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (869 of 869 strings)

Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 99.5% (865 of 869 strings)

Co-authored-by: return_null <demolang@dismail.de>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Nataniel Dika Kurniawan
ac5b29c35a Translated using Weblate (Malay)
Currently translated at 51.7% (453 of 875 strings)

Translated using Weblate (Malay)

Currently translated at 51.6% (452 of 875 strings)

Translated using Weblate (Malay)

Currently translated at 49.5% (431 of 869 strings)

Translated using Weblate (Javanese)

Currently translated at 9.4% (82 of 869 strings)

Translated using Weblate (Javanese)

Currently translated at 100.0% (9 of 9 strings)

Translated using Weblate (Malay)

Currently translated at 40.5% (352 of 869 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (869 of 869 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (9 of 9 strings)

Added translation using Weblate (Javanese)

Added translation using Weblate (Javanese)

Translated using Weblate (Malay)

Currently translated at 38.4% (334 of 869 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (869 of 869 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (9 of 9 strings)

Co-authored-by: Nataniel Dika Kurniawan <hikawaart2@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/id/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/jv/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/id/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/jv/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ms/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2025-09-21 13:26:53 +03:00
Frosted
59f5578b66 Translated using Weblate (Turkish)
Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (869 of 869 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-09-21 13:26:53 +03:00
Roel v
391dbb4237 Translated using Weblate (Gothic)
Currently translated at 2.8% (25 of 869 strings)

Translated using Weblate (Gothic)

Currently translated at 44.4% (4 of 9 strings)

Added translation using Weblate (Gothic)

Added translation using Weblate (Gothic)

Co-authored-by: Roel v <roel11112@live.nl>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/got/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/got/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2025-09-21 13:26:53 +03:00
gekka
7d4505eb78 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.5% (865 of 869 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
Максим Горпиніч
e6ceb20cf7 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (875 of 875 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (869 of 869 strings)

Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translation: Kotatsu/Strings
2025-09-21 13:26:53 +03:00
lenn
8004f8c093 Translated using Weblate (Polish)
Currently translated at 97.8% (849 of 868 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-09-21 13:26:53 +03:00
Koitharu
61bf2abb6c Change inset handling in reader 2025-09-21 12:28:32 +03:00
Koitharu
d9612f3427 Webtoon pull gesture refactoring 2025-09-21 11:57:27 +03:00
Koitharu
435c3824f7 Remove A5 compatibility code 2025-09-20 09:56:03 +03:00
Koitharu
c846693570 Cleanup R8 rules 2025-09-20 09:37:23 +03:00
Koitharu
123937cd01 Fix SearchView closing on back pressed (close #1532, close #1487) 2025-09-20 09:31:10 +03:00
Koitharu
9f56554313 Reduce apk size 2025-09-20 07:55:27 +03:00
Koitharu
f8687bb697 Improve background WebView usage 2025-09-17 09:27:10 +03:00
Koitharu
43d3a2cc6a Fix crash on WebView.stopLoading() 2025-09-17 09:14:28 +03:00
MuhamadSyabitHidayattulloh
a95db6ed21 chore: Show description in offline mode (#1597) 2025-09-15 11:02:52 +07:00
Koitharu
fd0bb57338 Background captcha resolving 2025-09-14 20:11:55 +03:00
Koitharu
6b94bc2632 Update dependencies 2025-09-14 20:11:55 +03:00
Koitharu
c8b91599c6 Fix OkHttp initialization 2025-09-14 20:11:55 +03:00
Draken
3a8b0f9e93 Merge pull request #1586 from MuhamadSyabitHidayattulloh/feat/pull-gesture-navigate-chapter
feat: Add Pull Gesture Navigate Chapter
2025-09-13 23:23:12 +07:00
MuhamadSyabitHidayattulloh
17a0725666 feat: Realtime Favorite and Storage Badges 2025-09-13 09:27:46 +03:00
Draken
3be7848ad9 Revert distributionSha256Sum 2025-09-13 09:25:58 +03:00
dragonx943
08202c11a3 build: migrate to gradle 9 2025-09-13 09:25:58 +03:00
MuhamadSyabitHidayattulloh
5ef907d046 fix: Ui not visible if Control Panel show 2025-09-11 09:36:24 +07:00
MuhamadSyabitHidayattulloh
c3776ea3c6 feat: Add Pull Gesture Navigate Chapter 2025-09-10 14:41:28 +07:00
Koitharu
a624bffea3 Upgrade minSdk to 23 2025-09-07 10:56:40 +03:00
Koitharu
8f38b4fe30 Replace DummyParser with TestMangaRepository 2025-09-07 10:31:13 +03:00
Koitharu
71a2de5358 Update dependencies 2025-09-06 10:41:53 +03:00
Koitharu
5478f8fb59 Fix crash in ListSelectionController 2025-09-06 10:41:49 +03:00
ViAnh
5155c9a33d Reduce gaps between webtoon pages 2025-09-06 10:12:37 +03:00
Koitharu
f7a461a9d8 Fix sync auth buttons (close #1556) 2025-08-30 09:25:55 +03:00
Aray LXa
3a02d22e02 Translated using Weblate (Persian)
Currently translated at 70.2% (610 of 868 strings)

Translated using Weblate (Persian)

Currently translated at 66.9% (581 of 868 strings)

Co-authored-by: Aray LXa <araylxa@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fa/
Translation: Kotatsu/Strings
2025-08-30 09:24:58 +03:00
Koitharu
2b8a29e2a6 Update parsers 2025-08-30 09:24:05 +03:00
Koitharu
bc68441585 Update parsers 2025-08-24 11:06:16 +03:00
Koitharu
1cc51b6a88 Refactor usage WebView for parsers 2025-08-24 10:39:23 +03:00
Kusou
fd5aca7252 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (868 of 868 strings)

Co-authored-by: Kusou <orion26br@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Abay Emes
e447245fac Translated using Weblate (Kazakh)
Currently translated at 100.0% (9 of 9 strings)

Translated using Weblate (Kazakh)

Currently translated at 62.5% (543 of 868 strings)

Co-authored-by: Abay Emes <abayemes@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/kk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/kk/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2025-08-24 10:14:59 +03:00
Draken
5af0ee1c69 Translated using Weblate (Vietnamese)
Currently translated at 100.0% (868 of 868 strings)

Co-authored-by: Draken <premieregirl26@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/vi/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Shayan
c02d1641ab Translated using Weblate (Persian)
Currently translated at 59.2% (514 of 868 strings)

Co-authored-by: Shayan <shayans31516@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fa/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Aray LXa
f55c525c8a Translated using Weblate (Persian)
Currently translated at 59.2% (514 of 868 strings)

Co-authored-by: Aray LXa <araylxa@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fa/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Максим Горпиніч
a42fc87a9a Translated using Weblate (Ukrainian)
Currently translated at 100.0% (868 of 868 strings)

Co-authored-by: Максим Горпиніч <gorpinicmaksim0@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Juan Rubin
6b6905fd71 Translated using Weblate (Portuguese)
Currently translated at 100.0% (868 of 868 strings)

Co-authored-by: Juan Rubin <juancrubin08@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translation: Kotatsu/Strings
2025-08-24 10:14:59 +03:00
Koitharu
b7f57856db Author search support for external manga sources 2025-08-24 10:13:12 +03:00
Koitharu
1d6d626b62 Update parsers 2025-08-24 09:47:29 +03:00
117 changed files with 2097 additions and 934 deletions

2
.idea/.gitignore generated vendored
View File

@@ -3,3 +3,5 @@
/workspace.xml
/migrations.xml
/runConfigurations.xml
/appInsightsSettings.xml
/kotlinCodeInsightSettings.xml

26
.idea/appInsightsSettings.xml generated Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AppInsightsSettings">
<option name="tabSettings">
<map>
<entry key="Firebase Crashlytics">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="PLACEHOLDER" />
<option name="mobileSdkAppId" value="" />
<option name="projectId" value="" />
<option name="projectNumber" value="" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="THIRTY_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -8,7 +8,7 @@
**[Kotatsu](https://github.com/KotatsuApp/Kotatsu) is a free and open-source manga reader for Android with built-in online content sources.**
![Downloads count](https://img.shields.io/github/downloads/KotatsuApp/Kotatsu/total?color=1976d2) ![Latest Stable version](https://img.shields.io/github/v/release/KotatsuApp/Kotatsu?color=2596be&label=latest) ![Android 5.0](https://img.shields.io/badge/android-5.0+-brightgreen) [![Sources count](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2FKotatsuApp%2Fkotatsu-parsers%2Frefs%2Fheads%2Fmaster%2F.github%2Fsummary.yaml&query=total&label=manga%20sources&color=%23E9321C)](https://github.com/KotatsuApp/kotatsu-parsers) [![weblate](https://hosted.weblate.org/widgets/kotatsu/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/kotatsu/) [![Discord](https://img.shields.io/discord/898363402467045416?color=5865f2&label=discord)](https://discord.gg/NNJ5RgVBC5) [![Telegram](https://img.shields.io/badge/chat-telegram-60ACFF?)](https://t.me/kotatsuapp) [![License](https://img.shields.io/github/license/KotatsuApp/Kotatsu)](https://github.com/KotatsuApp/Kotatsu/blob/devel/LICENSE)
![Downloads count](https://img.shields.io/github/downloads/KotatsuApp/Kotatsu/total?color=1976d2) ![Latest Stable version](https://img.shields.io/github/v/release/KotatsuApp/Kotatsu?color=2596be&label=latest) ![Android 6.0](https://img.shields.io/badge/android-6.0+-brightgreen) [![Sources count](https://img.shields.io/badge/dynamic/yaml?url=https%3A%2F%2Fraw.githubusercontent.com%2FKotatsuApp%2Fkotatsu-parsers%2Frefs%2Fheads%2Fmaster%2F.github%2Fsummary.yaml&query=total&label=manga%20sources&color=%23E9321C)](https://github.com/KotatsuApp/kotatsu-parsers) [![weblate](https://hosted.weblate.org/widgets/kotatsu/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/kotatsu/) [![Discord](https://img.shields.io/discord/898363402467045416?color=5865f2&label=discord)](https://discord.gg/NNJ5RgVBC5) [![Telegram](https://img.shields.io/badge/chat-telegram-60ACFF?)](https://t.me/kotatsuapp) [![License](https://img.shields.io/github/license/KotatsuApp/Kotatsu)](https://github.com/KotatsuApp/Kotatsu/blob/devel/LICENSE)
### Download

View File

@@ -19,10 +19,10 @@ android {
defaultConfig {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
minSdk = 23
targetSdk = 36
versionCode = 1026
versionName = '9.1.2'
versionCode = 1030
versionName = '9.2'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {
@@ -87,6 +87,7 @@ android {
'-opt-in=coil3.annotation.InternalCoilApi',
'-opt-in=kotlinx.serialization.ExperimentalSerializationApi',
'-Xjspecify-annotations=strict',
'-Xannotation-default-target=first-only',
'-Xtype-enhancement-improvements-strict-mode'
]
}

View File

@@ -8,8 +8,7 @@
public static void checkParameterIsNotNull(...);
public static void checkNotNullParameter(...);
}
-keep public class ** extends org.koitharu.kotatsu.core.ui.BaseFragment
-keep class org.koitharu.kotatsu.core.db.entity.* { *; }
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
@@ -17,8 +16,10 @@
-dontwarn com.google.j2objc.annotations.**
-dontwarn coil3.PlatformContext
-keep class org.koitharu.kotatsu.core.exceptions.* { *; }
-keep class org.koitharu.kotatsu.settings.NotificationSettingsLegacyFragment
-keep class org.koitharu.kotatsu.settings.about.changelog.ChangelogFragment
-keep class org.koitharu.kotatsu.core.exceptions.* { *; }
-keep class org.koitharu.kotatsu.core.prefs.ScreenshotsPolicy { *; }
-keep class org.koitharu.kotatsu.backups.ui.periodical.PeriodicalBackupSettingsFragment { *; }
-keep class org.jsoup.parser.Tag

View File

@@ -41,8 +41,8 @@ class KotatsuApp : BaseApp() {
detectNetwork()
detectDiskWrites()
detectCustomSlowCalls()
detectResourceMismatches()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) detectUnbufferedIo()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) detectResourceMismatches()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) detectExplicitGc()
penaltyLog()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) {

View File

@@ -0,0 +1,57 @@
package org.koitharu.kotatsu.core.parser
import org.koitharu.kotatsu.core.cache.MemoryContentCache
import org.koitharu.kotatsu.core.model.TestMangaSource
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.SortOrder
import java.util.EnumSet
/*
This class is for parser development and testing purposes
You can open it in the app via Settings -> Debug
*/
class TestMangaRepository(
@Suppress("unused") private val loaderContext: MangaLoaderContext,
cache: MemoryContentCache
) : CachingMangaRepository(cache) {
override val source = TestMangaSource
override val sortOrders: Set<SortOrder> = EnumSet.allOf(SortOrder::class.java)
override var defaultSortOrder: SortOrder
get() = sortOrders.first()
set(value) = Unit
override val filterCapabilities = MangaListFilterCapabilities()
override suspend fun getFilterOptions() = MangaListFilterOptions()
override suspend fun getList(
offset: Int,
order: SortOrder?,
filter: MangaListFilter?
): List<Manga> = TODO("Get manga list by filter")
override suspend fun getDetailsImpl(
manga: Manga
): Manga = TODO("Fetch manga details")
override suspend fun getPagesImpl(
chapter: MangaChapter
): List<MangaPage> = TODO("Get pages for specific chapter")
override suspend fun getPageUrl(
page: MangaPage
): String = TODO("Return direct url of page image or page.url if it is already a direct url")
override suspend fun getRelatedMangaImpl(
seed: Manga
): List<Manga> = TODO("Get list of related manga. This method is optional and parser library has a default implementation")
}

View File

@@ -5,6 +5,8 @@ import androidx.preference.Preference
import leakcanary.LeakCanary
import org.koitharu.kotatsu.KotatsuApp
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.TestMangaSource
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
import org.koitharu.kotatsu.settings.utils.SplitSwitchPreference
import org.koitharu.workinspector.WorkInspector
@@ -35,6 +37,11 @@ class DebugSettingsFragment : BasePreferenceFragment(R.string.debug), Preference
true
}
KEY_TEST_PARSER -> {
router.openList(TestMangaSource, null, null)
true
}
else -> super.onPreferenceTreeClick(preference)
}
@@ -60,5 +67,6 @@ class DebugSettingsFragment : BasePreferenceFragment(R.string.debug), Preference
const val KEY_LEAK_CANARY = "leak_canary"
const val KEY_WORK_INSPECTOR = "work_inspector"
const val KEY_TEST_PARSER = "test_parser"
}
}

View File

@@ -1,17 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<org.koitharu.kotatsu.settings.utils.SplitSwitchPreference
android:id="@+id/action_leakcanary"
android:key="leak_canary"
android:persistent="false"
android:title="LeakCanary" />
<Preference
android:id="@+id/action_works"
android:key="work_inspector"
android:persistent="false"
android:title="@string/wi_lib_name" />
<Preference
android:key="test_parser"
android:persistent="false"
android:title="@string/test_parser"
app:allowDividerAbove="true" />
</androidx.preference.PreferenceScreen>

View File

@@ -51,6 +51,7 @@
android:backupAgent="org.koitharu.kotatsu.backups.domain.AppBackupAgent"
android:dataExtractionRules="@xml/backup_rules"
android:enableOnBackInvokedCallback="@bool/is_predictive_back_enabled"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_content"
android:fullBackupOnly="true"
android:hasFragileUserData="true"

View File

@@ -13,6 +13,7 @@ import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import okhttp3.internal.platform.PlatformRegistry
import org.acra.ACRA
import org.acra.ReportField
import org.acra.config.dialog
@@ -79,6 +80,7 @@ open class BaseApp : Application(), Configuration.Provider {
override fun onCreate() {
super.onCreate()
PlatformRegistry.applicationContext = this // TODO replace with OkHttp.initialize
if (ACRA.isACRASenderServiceProcess()) {
return
}

View File

@@ -7,6 +7,7 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.Settings
import androidx.annotation.CheckResult
import androidx.annotation.RequiresPermission
import androidx.collection.MutableScatterMap
import androidx.core.app.NotificationChannelCompat
@@ -43,6 +44,7 @@ import org.koitharu.kotatsu.core.model.UnknownMangaSource
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.model.isNsfw
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.network.webview.WebViewExecutor
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.prefs.SourceSettings
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
@@ -65,11 +67,13 @@ class CaptchaHandler @Inject constructor(
@LocalizedAppContext private val context: Context,
private val databaseProvider: Provider<MangaDatabase>,
private val coilProvider: Provider<ImageLoader>,
private val webViewExecutor: WebViewExecutor,
) : EventListener() {
private val exceptionMap = MutableScatterMap<MangaSource, CloudFlareProtectedException>()
private val mutex = Mutex()
@CheckResult
suspend fun handle(exception: CloudFlareException): Boolean = handleException(exception.source, exception, true)
suspend fun discard(source: MangaSource) {
@@ -79,10 +83,18 @@ class CaptchaHandler @Inject constructor(
override fun onError(request: ImageRequest, result: ErrorResult) {
super.onError(request, result)
val e = result.throwable
if (e is CloudFlareException && request.extras[ignoreCaptchaKey] != true) {
if (e is CloudFlareException) {
val scope = request.lifecycle?.coroutineScope ?: processLifecycleScope
scope.launch {
handleException(e.source, e, true)
if (
handleException(
source = e.source,
exception = e,
notify = request.extras[suppressCaptchaKey] != true,
)
) {
coilProvider.get().enqueue(request) // TODO check if ok
}
}
}
}
@@ -90,11 +102,14 @@ class CaptchaHandler @Inject constructor(
private suspend fun handleException(
source: MangaSource,
exception: CloudFlareException?,
notify: Boolean
notify: Boolean,
): Boolean = withContext(Dispatchers.Default) {
if (source == UnknownMangaSource) {
return@withContext false
}
if (exception != null && webViewExecutor.tryResolveCaptcha(exception, RESOLVE_TIMEOUT)) {
return@withContext true
}
mutex.withLock {
var removedException: CloudFlareProtectedException? = null
if (exception is CloudFlareProtectedException) {
@@ -119,7 +134,7 @@ class CaptchaHandler @Inject constructor(
notify(exceptions)
}
}
true
false
}
@RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
@@ -234,7 +249,7 @@ class CaptchaHandler @Inject constructor(
.data(source.faviconUri())
.allowHardware(false)
.allowConversionToBitmap(true)
.ignoreCaptchaErrors()
.suppressCaptchaErrors()
.mangaSourceExtra(source)
.size(context.resources.getNotificationIconSize())
.scale(Scale.FILL)
@@ -260,11 +275,11 @@ class CaptchaHandler @Inject constructor(
companion object {
fun ImageRequest.Builder.ignoreCaptchaErrors() = apply {
extras[ignoreCaptchaKey] = true
fun ImageRequest.Builder.suppressCaptchaErrors() = apply {
extras[suppressCaptchaKey] = true
}
val ignoreCaptchaKey = Extras.Key(false)
private val suppressCaptchaKey = Extras.Key(false)
private const val CHANNEL_ID = "captcha"
private const val TAG = CHANNEL_ID
@@ -272,5 +287,6 @@ class CaptchaHandler @Inject constructor(
private const val GROUP_NOTIFICATION_ID = 34
private const val SETTINGS_ACTION_CODE = 3
private const val ACTION_DISCARD = "org.koitharu.kotatsu.CAPTCHA_DISCARD"
private const val RESOLVE_TIMEOUT = 20_000L
}
}

View File

@@ -28,11 +28,15 @@ data object UnknownMangaSource : MangaSource {
override val name = "UNKNOWN"
}
data object TestMangaSource : MangaSource {
override val name = "TEST"
}
fun MangaSource(name: String?): MangaSource {
when (name ?: return UnknownMangaSource) {
UnknownMangaSource.name -> return UnknownMangaSource
LocalMangaSource.name -> return LocalMangaSource
TestMangaSource.name -> return TestMangaSource
}
if (name.startsWith("content:")) {
val parts = name.substringAfter(':').splitTwoParts('/') ?: return UnknownMangaSource
@@ -92,6 +96,7 @@ fun MangaSource.getSummary(context: Context): String? = when (val source = unwra
fun MangaSource.getTitle(context: Context): String = when (val source = unwrap()) {
is MangaParserSource -> source.title
LocalMangaSource -> context.getString(R.string.local_storage)
TestMangaSource -> context.getString(R.string.test_parser)
is ExternalMangaSource -> source.resolveName(context)
else -> context.getString(R.string.unknown)
}

View File

@@ -5,7 +5,6 @@ import androidx.annotation.WorkerThread
import androidx.core.util.Predicate
import okhttp3.Cookie
import okhttp3.HttpUrl
import org.koitharu.kotatsu.parsers.util.newBuilder
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

View File

@@ -0,0 +1,30 @@
package org.koitharu.kotatsu.core.network.webview
import android.graphics.Bitmap
import android.webkit.WebView
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
import kotlin.coroutines.Continuation
class CaptchaContinuationClient(
private val cookieJar: MutableCookieJar,
private val targetUrl: String,
continuation: Continuation<Unit>,
) : ContinuationResumeWebViewClient(continuation) {
private val oldClearance = CloudFlareHelper.getClearanceCookie(cookieJar, targetUrl)
override fun onPageFinished(view: WebView?, url: String?) = Unit
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
checkClearance(view)
}
private fun checkClearance(view: WebView?) {
val clearance = CloudFlareHelper.getClearanceCookie(cookieJar, targetUrl)
if (clearance != null && clearance != oldClearance) {
resumeContinuation(view)
}
}
}

View File

@@ -2,15 +2,22 @@ package org.koitharu.kotatsu.core.network.webview
import android.webkit.WebView
import android.webkit.WebViewClient
import kotlinx.coroutines.CancellableContinuation
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
class ContinuationResumeWebViewClient(
open class ContinuationResumeWebViewClient(
private val continuation: Continuation<Unit>,
) : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
view?.webViewClient = WebViewClient() // reset to default
continuation.resume(Unit)
resumeContinuation(view)
}
protected fun resumeContinuation(view: WebView?) {
if (continuation !is CancellableContinuation || continuation.isActive) {
view?.webViewClient = WebViewClient() // reset to default
continuation.resume(Unit)
}
}
}

View File

@@ -0,0 +1,127 @@
package org.koitharu.kotatsu.core.network.webview
import android.content.Context
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.MainThread
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import org.koitharu.kotatsu.core.exceptions.CloudFlareException
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
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.printStackTraceDebug
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@Singleton
class WebViewExecutor @Inject constructor(
@ApplicationContext private val context: Context,
private val proxyProvider: ProxyProvider,
private val cookieJar: MutableCookieJar,
private val mangaRepositoryFactoryProvider: Provider<MangaRepository.Factory>,
) {
private var webViewCached: WeakReference<WebView>? = null
private val mutex = Mutex()
val defaultUserAgent: String? by lazy {
WebSettings.getDefaultUserAgent(context)
}
suspend fun evaluateJs(baseUrl: String?, script: String): String? = mutex.withLock {
withContext(Dispatchers.Main.immediate) {
val webView = obtainWebView()
try {
if (!baseUrl.isNullOrEmpty()) {
suspendCoroutine { cont ->
webView.webViewClient = ContinuationResumeWebViewClient(cont)
webView.loadDataWithBaseURL(baseUrl, " ", "text/html", null, null)
}
}
suspendCoroutine { cont ->
webView.evaluateJavascript(script) { result ->
cont.resume(result?.takeUnless { it == "null" })
}
}
} finally {
webView.reset()
}
}
}
suspend fun tryResolveCaptcha(exception: CloudFlareException, timeout: Long): Boolean = mutex.withLock {
runCatchingCancellable {
withContext(Dispatchers.Main.immediate) {
val webView = obtainWebView()
try {
exception.source.getUserAgent()?.let {
webView.settings.userAgentString = it
}
withTimeout(timeout) {
suspendCancellableCoroutine { cont ->
webView.webViewClient = CaptchaContinuationClient(
cookieJar = cookieJar,
targetUrl = exception.url,
continuation = cont,
)
webView.loadUrl(exception.url)
}
}
} finally {
webView.reset()
}
}
}.onFailure { e ->
exception.addSuppressed(e)
e.printStackTraceDebug()
}.isSuccess
}
private suspend fun obtainWebView(): WebView {
webViewCached?.get()?.let {
return it
}
return withContext(Dispatchers.Main.immediate) {
webViewCached?.get()?.let {
return@withContext it
}
WebView(context).also {
it.configureForParser(null)
webViewCached = WeakReference(it)
proxyProvider.applyWebViewConfig()
it.onResume()
it.resumeTimers()
}
}
}
private fun MangaSource.getUserAgent(): String? {
val repository = mangaRepositoryFactoryProvider.get().create(this) as? ParserMangaRepository
return repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT)
}
@MainThread
private fun WebView.reset() {
stopLoading()
webViewClient = WebViewClient()
settings.userAgentString = defaultUserAgent
loadDataWithBaseURL(null, " ", "text/html", null, null)
clearHistory()
}
}

View File

@@ -80,12 +80,7 @@ class NetworkState(
if (settings.isOfflineCheckDisabled) {
return true
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activeNetwork?.let { isOnline(it) } == true
} else {
@Suppress("DEPRECATION")
activeNetworkInfo?.isConnected == true
}
return activeNetwork?.let { isOnline(it) } == true
}
private fun ConnectivityManager.isOnline(network: Network): Boolean {

View File

@@ -1,46 +0,0 @@
package org.koitharu.kotatsu.core.parser
import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.AbstractMangaParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.SortOrder
import java.util.EnumSet
/**
* This parser is just for parser development, it should not be used in releases
*/
class DummyParser(context: MangaLoaderContext) : AbstractMangaParser(context, MangaParserSource.DUMMY) {
override val configKeyDomain: ConfigKey.Domain
get() = ConfigKey.Domain("localhost")
override val availableSortOrders: Set<SortOrder>
get() = EnumSet.allOf(SortOrder::class.java)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities()
override suspend fun getDetails(manga: Manga): Manga = stub(manga)
override suspend fun getFilterOptions(): MangaListFilterOptions = stub(null)
override suspend fun getList(
offset: Int,
order: SortOrder,
filter: MangaListFilter
): List<Manga> = stub(null)
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> = stub(null)
private fun stub(manga: Manga?): Nothing {
throw UnsupportedSourceException("Usage of Dummy parser", manga)
}
}

View File

@@ -11,7 +11,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.SortOrder
import java.util.EnumSet
class EmptyMangaRepository(override val source: MangaSource) : MangaRepository {
open class EmptyMangaRepository(override val source: MangaSource) : MangaRepository {
override val sortOrders: Set<SortOrder>
get() = EnumSet.allOf(SortOrder::class.java)

View File

@@ -9,6 +9,8 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
import org.koitharu.kotatsu.core.db.TABLE_PREFERENCES
import org.koitharu.kotatsu.core.db.entity.ContentRating
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
@@ -189,6 +191,11 @@ class MangaDataRepository @Inject constructor(
emitInitialState = emitInitialState,
)
fun observeFavoritesTrigger(emitInitialState: Boolean) = db.invalidationTracker.createFlow(
tables = arrayOf(TABLE_FAVOURITES, TABLE_FAVOURITE_CATEGORIES),
emitInitialState = emitInitialState,
)
private suspend fun Manga.withCachedChaptersIfNeeded(flag: Boolean): Manga = if (flag && !isLocal && chapters.isNullOrEmpty()) {
val cachedChapters = db.getChaptersDao().findAll(id)
if (cachedChapters.isEmpty()) {

View File

@@ -3,15 +3,8 @@ package org.koitharu.kotatsu.core.parser
import android.annotation.SuppressLint
import android.content.Context
import android.util.Base64
import android.webkit.WebView
import androidx.annotation.MainThread
import androidx.core.os.LocaleListCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
@@ -22,11 +15,8 @@ import org.koitharu.kotatsu.core.exceptions.InteractiveActionRequiredException
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.network.webview.ContinuationResumeWebViewClient
import org.koitharu.kotatsu.core.network.webview.WebViewExecutor
import org.koitharu.kotatsu.core.prefs.SourceSettings
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.sanitizeHeaderValue
import org.koitharu.kotatsu.core.util.ext.toList
import org.koitharu.kotatsu.core.util.ext.toMimeType
import org.koitharu.kotatsu.core.util.ext.use
@@ -37,25 +27,19 @@ import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.util.map
import java.lang.ref.WeakReference
import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@Singleton
class MangaLoaderContextImpl @Inject constructor(
@MangaHttpClient override val httpClient: OkHttpClient,
override val cookieJar: MutableCookieJar,
@ApplicationContext private val androidContext: Context,
private val webViewExecutor: WebViewExecutor,
) : MangaLoaderContext() {
private var webViewCached: WeakReference<WebView>? = null
private val webViewUserAgent by lazy { obtainWebViewUserAgent() }
private val jsMutex = Mutex()
private val jsTimeout = TimeUnit.SECONDS.toMillis(4)
@Deprecated("Provide a base url")
@@ -63,25 +47,10 @@ class MangaLoaderContextImpl @Inject constructor(
override suspend fun evaluateJs(script: String): String? = evaluateJs("", script)
override suspend fun evaluateJs(baseUrl: String, script: String): String? = withTimeout(jsTimeout) {
jsMutex.withLock {
withContext(Dispatchers.Main.immediate) {
val webView = obtainWebView()
if (baseUrl.isNotEmpty()) {
suspendCoroutine { cont ->
webView.webViewClient = ContinuationResumeWebViewClient(cont)
webView.loadDataWithBaseURL(baseUrl, " ", "text/html", null, null)
}
}
suspendCoroutine { cont ->
webView.evaluateJavascript(script) { result ->
cont.resume(result?.takeUnless { it == "null" })
}
}
}
}
webViewExecutor.evaluateJs(baseUrl, script)
}
override fun getDefaultUserAgent(): String = webViewUserAgent
override fun getDefaultUserAgent(): String = webViewExecutor.defaultUserAgent ?: UserAgents.FIREFOX_MOBILE
override fun getConfig(source: MangaSource): MangaSourceConfig {
return SourceSettings(androidContext, source)
@@ -118,28 +87,4 @@ class MangaLoaderContextImpl @Inject constructor(
}
override fun createBitmap(width: Int, height: Int): Bitmap = BitmapWrapper.create(width, height)
@MainThread
private fun obtainWebView(): WebView = webViewCached?.get() ?: WebView(androidContext).also {
it.configureForParser(null)
webViewCached = WeakReference(it)
}
private fun obtainWebViewUserAgent(): String {
val mainDispatcher = Dispatchers.Main.immediate
return if (!mainDispatcher.isDispatchNeeded(EmptyCoroutineContext)) {
obtainWebViewUserAgentImpl()
} else {
runBlocking(mainDispatcher) {
obtainWebViewUserAgentImpl()
}
}
}
@MainThread
private fun obtainWebViewUserAgentImpl() = runCatching {
obtainWebView().settings.userAgentString.sanitizeHeaderValue()
}.onFailure { e ->
e.printStackTraceDebug()
}.getOrDefault(UserAgents.FIREFOX_MOBILE)
}

View File

@@ -1,12 +0,0 @@
package org.koitharu.kotatsu.core.parser
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
fun MangaParser(source: MangaParserSource, loaderContext: MangaLoaderContext): MangaParser {
return when (source) {
MangaParserSource.DUMMY -> DummyParser(loaderContext)
else -> loaderContext.newParserInstance(source)
}
}

View File

@@ -7,6 +7,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import org.koitharu.kotatsu.core.cache.MemoryContentCache
import org.koitharu.kotatsu.core.model.LocalMangaSource
import org.koitharu.kotatsu.core.model.MangaSourceInfo
import org.koitharu.kotatsu.core.model.TestMangaSource
import org.koitharu.kotatsu.core.model.UnknownMangaSource
import org.koitharu.kotatsu.core.parser.external.ExternalMangaRepository
import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource
@@ -85,11 +86,16 @@ interface MangaRepository {
private fun createRepository(source: MangaSource): MangaRepository? = when (source) {
is MangaParserSource -> ParserMangaRepository(
parser = MangaParser(source, loaderContext),
parser = loaderContext.newParserInstance(source),
cache = contentCache,
mirrorSwitcher = mirrorSwitcher,
)
TestMangaSource -> TestMangaRepository(
loaderContext = loaderContext,
cache = contentCache,
)
is ExternalMangaSource -> if (source.isAvailable(context)) {
ExternalMangaRepository(
contentResolver = context.contentResolver,

View File

@@ -53,6 +53,9 @@ class ExternalPluginContentSource(
filter.states.forEach { uri.appendQueryParameter("state", it.name) }
filter.locale?.let { uri.appendQueryParameter("locale", it.language) }
filter.contentRating.forEach { uri.appendQueryParameter("content_rating", it.name) }
if (!filter.author.isNullOrEmpty()) {
uri.appendQueryParameter("author", filter.author)
}
if (!filter.query.isNullOrEmpty()) {
uri.appendQueryParameter("query", filter.query)
}
@@ -196,6 +199,7 @@ class ExternalPluginContentSource(
isYearSupported = cursor.getBooleanOrDefault(COLUMN_YEAR, false),
isYearRangeSupported = cursor.getBooleanOrDefault(COLUMN_YEAR_RANGE, false),
isOriginalLocaleSupported = cursor.getBooleanOrDefault(COLUMN_ORIGINAL_LOCALE, false),
isAuthorSearchSupported = cursor.getBooleanOrDefault(COLUMN_AUTHOR, false),
),
)
} else {

View File

@@ -488,6 +488,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getBoolean(KEY_WEBTOON_GAPS, false)
set(value) = prefs.edit { putBoolean(KEY_WEBTOON_GAPS, value) }
var isWebtoonPullGestureEnabled: Boolean
get() = prefs.getBoolean(KEY_WEBTOON_PULL_GESTURE, false)
set(value) = prefs.edit { putBoolean(KEY_WEBTOON_PULL_GESTURE, value) }
@get:FloatRange(from = 0.0, to = 0.5)
val defaultWebtoonZoomOut: Float
get() = prefs.getInt(KEY_WEBTOON_ZOOM_OUT, 0).coerceIn(0, 50) / 100f
@@ -748,6 +752,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_WEBTOON_GAPS = "webtoon_gaps"
const val KEY_WEBTOON_ZOOM = "webtoon_zoom"
const val KEY_WEBTOON_ZOOM_OUT = "webtoon_zoom_out"
const val KEY_WEBTOON_PULL_GESTURE = "webtoon_pull_gesture"
const val KEY_PREFETCH_CONTENT = "prefetch_content"
const val KEY_APP_LOCALE = "app_locale"
const val KEY_SOURCES_GRID = "sources_grid"

View File

@@ -1,8 +0,0 @@
package org.koitharu.kotatsu.core.ui
import android.view.View
fun interface OnContextClickListenerCompat {
fun onContextClick(v: View): Boolean
}

View File

@@ -10,7 +10,7 @@ import coil3.asImage
import coil3.request.Disposable
import coil3.request.ImageRequest
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.CaptchaHandler.Companion.ignoreCaptchaErrors
import org.koitharu.kotatsu.core.exceptions.resolve.CaptchaHandler.Companion.suppressCaptchaErrors
import org.koitharu.kotatsu.core.image.CoilImageView
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
@@ -57,7 +57,7 @@ class FaviconView @JvmOverloads constructor(
.fallback(fallbackFactory)
.placeholder(placeholderFactory)
.mangaSourceExtra(mangaSource)
.ignoreCaptchaErrors()
.suppressCaptchaErrors()
.build(),
)
}

View File

@@ -2,17 +2,16 @@ package org.koitharu.kotatsu.core.ui.list
import android.view.View
import android.view.View.OnClickListener
import android.view.View.OnContextClickListener
import android.view.View.OnLongClickListener
import androidx.core.util.Function
import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateViewBindingViewHolder
import org.koitharu.kotatsu.core.ui.OnContextClickListenerCompat
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
class AdapterDelegateClickListenerAdapter<I, O>(
private val adapterDelegate: AdapterDelegateViewBindingViewHolder<out I, *>,
private val clickListener: OnListItemClickListener<O>,
private val itemMapper: Function<I, O>,
) : OnClickListener, OnLongClickListener, OnContextClickListenerCompat {
) : OnClickListener, OnLongClickListener, OnContextClickListener {
override fun onClick(v: View) {
clickListener.onItemClick(mappedItem(), v)
@@ -33,7 +32,7 @@ class AdapterDelegateClickListenerAdapter<I, O>(
fun attach(itemView: View) {
itemView.setOnClickListener(this)
itemView.setOnLongClickListener(this)
itemView.setOnContextClickListenerCompat(this)
itemView.setOnContextClickListener(this)
}
companion object {

View File

@@ -186,6 +186,7 @@ class ListSelectionController(
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_CREATE) {
source.lifecycle.removeObserver(this)
val registry = registryOwner.savedStateRegistry
registry.registerSavedStateProvider(PROVIDER_NAME, this@ListSelectionController)
val state = registry.consumeRestoredStateForKey(PROVIDER_NAME)

View File

@@ -1,7 +1,6 @@
package org.koitharu.kotatsu.core.ui.util
import android.graphics.Color
import android.os.Build
import android.view.ViewGroup
import android.view.Window
import androidx.activity.OnBackPressedCallback
@@ -14,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import com.google.android.material.R as materialR
@@ -37,14 +35,10 @@ class ActionModeDelegate : OnBackPressedCallback(false) {
listeners?.forEach { it.onActionModeStarted(mode) }
if (window != null) {
val ctx = window.context
val actionModeColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ColorUtils.compositeColors(
ContextCompat.getColor(ctx, materialR.color.m3_appbar_overlay_color),
ctx.getThemeColor(materialR.attr.colorSurface),
)
} else {
ContextCompat.getColor(ctx, R.color.kotatsu_surface)
}
val actionModeColor = ColorUtils.compositeColors(
ContextCompat.getColor(ctx, materialR.color.m3_appbar_overlay_color),
ctx.getThemeColor(materialR.attr.colorSurface),
)
defaultStatusBarColor = window.statusBarColor
window.statusBarColor = actionModeColor
val insets = ViewCompat.getRootWindowInsets(window.decorView)

View File

@@ -4,12 +4,10 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.core.ui.OnContextClickListenerCompat
import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat
class PopupMenuMediator(
private val provider: MenuProvider,
) : View.OnLongClickListener, OnContextClickListenerCompat, PopupMenu.OnMenuItemClickListener,
) : View.OnLongClickListener, View.OnContextClickListener, PopupMenu.OnMenuItemClickListener,
PopupMenu.OnDismissListener {
override fun onContextClick(v: View): Boolean = onLongClick(v)
@@ -37,6 +35,6 @@ class PopupMenuMediator(
fun attach(view: View) {
view.setOnLongClickListener(this)
view.setOnContextClickListenerCompat(this)
view.setOnContextClickListener(this)
}
}

View File

@@ -22,7 +22,7 @@ open class StackLayout @JvmOverloads constructor(
val h = b - t - paddingTop - paddingBottom
visibleChildren.clear()
children.filterNotTo(visibleChildren) { it.isGone }
if (w <= 0 || h <= 0 || visibleChildren.isEmpty) {
if (w <= 0 || h <= 0 || visibleChildren.isEmpty()) {
return
}
val xStep = w / (visibleChildren.size + 1)

View File

@@ -28,7 +28,6 @@ import com.google.android.material.progressindicator.BaseProgressIndicator
import com.google.android.material.slider.RangeSlider
import com.google.android.material.slider.Slider
import com.google.android.material.tabs.TabLayout
import org.koitharu.kotatsu.core.ui.OnContextClickListenerCompat
import kotlin.math.roundToInt
fun View.hasGlobalPoint(x: Int, y: Int): Boolean {
@@ -169,12 +168,6 @@ fun BaseProgressIndicator<*>.showOrHide(value: Boolean) {
}
}
fun View.setOnContextClickListenerCompat(listener: OnContextClickListenerCompat) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnContextClickListener(listener::onContextClick)
}
}
fun View.setTooltipCompat(tooltip: CharSequence?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
tooltipText = tooltip

View File

@@ -209,9 +209,7 @@ class DetailsActivity :
override fun onProvideAssistContent(outContent: AssistContent) {
super.onProvideAssistContent(outContent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
viewModel.getMangaOrNull()?.publicUrl?.toUriOrNull()?.let { outContent.webUri = it }
}
viewModel.getMangaOrNull()?.publicUrl?.toUriOrNull()?.let { outContent.webUri = it }
}
override fun isNsfwContent(): Flow<Boolean> = viewModel.manga.map { it?.contentRating == ContentRating.ADULT }

View File

@@ -7,6 +7,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
@@ -25,6 +26,8 @@ import org.koitharu.kotatsu.list.ui.MangaListViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.model.Manga
import javax.inject.Inject
@@ -35,7 +38,8 @@ class RelatedListViewModel @Inject constructor(
settings: AppSettings,
private val mangaListMapper: MangaListMapper,
mangaDataRepository: MangaDataRepository,
) : MangaListViewModel(settings, mangaDataRepository) {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) {
private val seed = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
private val repository = mangaRepositoryFactory.create(seed.source)

View File

@@ -202,7 +202,7 @@ class DownloadWorker @AssistedInject constructor(
?: error("Cannot obtain remote manga instance")
}
val repo = mangaRepositoryFactory.create(manga.source)
val mangaDetails = if (manga.chapters.isNullOrEmpty()) repo.getDetails(manga) else manga
val mangaDetails = if (manga.chapters.isNullOrEmpty() || manga.description.isNullOrEmpty()) repo.getDetails(manga) else manga
output = LocalMangaOutput.getOrCreate(
root = destination,
manga = mangaDetails,

View File

@@ -53,11 +53,7 @@ class MangaSourcesRepository @Inject constructor(
get() = db.getSourcesDao()
val allMangaSources: Set<MangaParserSource> = Collections.unmodifiableSet(
EnumSet.allOf(MangaParserSource::class.java).apply {
if (!BuildConfig.DEBUG) {
remove(MangaParserSource.DUMMY)
}
},
EnumSet.allOf(MangaParserSource::class.java)
)
suspend fun getEnabledSources(): List<MangaSource> {

View File

@@ -40,6 +40,9 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.parsers.model.Manga
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import kotlinx.coroutines.flow.SharedFlow
private const val PAGE_SIZE = 16
@@ -52,7 +55,8 @@ class FavouritesListViewModel @Inject constructor(
quickFilterFactory: FavoritesListQuickFilter.Factory,
settings: AppSettings,
mangaDataRepository: MangaDataRepository,
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener {
val categoryId: Long = savedStateHandle[AppRouter.KEY_ID] ?: NO_ID
private val quickFilter = quickFilterFactory.create(categoryId)

View File

@@ -51,9 +51,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
super.onViewBindingCreated(binding, savedInstanceState)
if (dialog == null) {
binding.layoutBody.updatePadding(top = binding.layoutBody.paddingBottom)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
binding.scrollView.scrollIndicators = 0
}
binding.scrollView.scrollIndicators = 0
}
val filter = FilterCoordinator.require(this)
filter.sortOrder.observe(viewLifecycleOwner, this::onSortOrderChanged)

View File

@@ -43,6 +43,9 @@ import org.koitharu.kotatsu.parsers.model.Manga
import java.time.Instant
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import kotlinx.coroutines.flow.SharedFlow
private const val PAGE_SIZE = 16
@@ -54,7 +57,8 @@ class HistoryListViewModel @Inject constructor(
private val markAsReadUseCase: MarkAsReadUseCase,
private val quickFilter: HistoryListQuickFilter,
mangaDataRepository: MangaDataRepository,
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
private val sortOrder: StateFlow<ListSortOrder> = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.IO,

View File

@@ -83,9 +83,7 @@ class CoverImageView @JvmOverloads constructor(
if (fallbackDrawable == null) {
fallbackDrawable = context.getThemeColor(materialR.attr.colorSurfaceContainer).toDrawable()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
addImageRequestListener(ErrorForegroundListener())
}
addImageRequestListener(ErrorForegroundListener())
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -169,7 +167,6 @@ class CoverImageView @JvmOverloads constructor(
}
}
@RequiresApi(Build.VERSION_CODES.M)
private inner class ErrorForegroundListener : ImageRequest.Listener {
override fun onSuccess(request: ImageRequest, result: SuccessResult) {

View File

@@ -3,10 +3,12 @@ package org.koitharu.kotatsu.list.ui
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
@@ -22,10 +24,13 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.list.domain.ListFilterOption
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
abstract class MangaListViewModel(
private val settings: AppSettings,
private val mangaDataRepository: MangaDataRepository,
@param:LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
) : BaseViewModel() {
abstract val content: StateFlow<List<ListModel>>
@@ -63,7 +68,11 @@ abstract class MangaListViewModel(
protected fun observeListModeWithTriggers(): Flow<ListMode> = combine(
listMode,
mangaDataRepository.observeOverridesTrigger(emitInitialState = true),
merge(
mangaDataRepository.observeOverridesTrigger(emitInitialState = true),
mangaDataRepository.observeFavoritesTrigger(emitInitialState = true),
localStorageChanges.onStart { emit(null) },
),
settings.observeChanges().filter { key ->
key == AppSettings.KEY_PROGRESS_INDICATORS
|| key == AppSettings.KEY_TRACKER_ENABLED

View File

@@ -45,7 +45,7 @@ class LocalListViewModel @Inject constructor(
mangaListMapper: MangaListMapper,
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
exploreRepository: ExploreRepository,
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
@param:LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
private val localStorageManager: LocalStorageManager,
sourcesRepository: MangaSourcesRepository,
mangaDataRepository: MangaDataRepository,
@@ -58,6 +58,7 @@ class LocalListViewModel @Inject constructor(
exploreRepository = exploreRepository,
sourcesRepository = sourcesRepository,
mangaDataRepository = mangaDataRepository,
localStorageChanges = localStorageChanges,
), SharedPreferences.OnSharedPreferenceChangeListener, QuickFilterListener {
val onMangaRemoved = MutableEventFlow<Unit>()

View File

@@ -131,7 +131,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
onBackPressedDispatcher.addCallback(exitCallback)
onBackPressedDispatcher.addCallback(navigationDelegate)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || !resources.getBoolean(R.bool.is_predictive_back_enabled)) {
val legacySearchCallback = SearchViewLegacyBackCallback(viewBinding.searchView)
viewBinding.searchView.addTransitionListener(legacySearchCallback)
onBackPressedDispatcher.addCallback(legacySearchCallback)

View File

@@ -20,6 +20,9 @@ import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState
import javax.inject.Inject
import kotlinx.coroutines.flow.SharedFlow
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
@HiltViewModel
class MangaPickerViewModel @Inject constructor(
@@ -28,7 +31,8 @@ class MangaPickerViewModel @Inject constructor(
private val historyRepository: HistoryRepository,
private val favouritesRepository: FavouritesRepository,
private val mangaListMapper: MangaListMapper,
) : MangaListViewModel(settings, mangaDataRepository) {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) {
override val content: StateFlow<List<ListModel>>
get() = flow {

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.reader.ui
import android.app.assist.AssistContent
import android.content.DialogInterface
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.KeyEvent
@@ -215,9 +214,7 @@ class ReaderActivity :
override fun onProvideAssistContent(outContent: AssistContent) {
super.onProvideAssistContent(outContent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
viewModel.getMangaOrNull()?.publicUrl?.toUriOrNull()?.let { outContent.webUri = it }
}
viewModel.getMangaOrNull()?.publicUrl?.toUriOrNull()?.let { outContent.webUri = it }
}
override fun isNsfwContent(): Flow<Boolean> = viewModel.isMangaNsfw
@@ -374,6 +371,7 @@ class ReaderActivity :
viewBinding.infoBar.isTimeVisible = isFullscreen
updateScrollTimerButton()
systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen)
viewBinding.root.requestApplyInsets()
}
}
@@ -395,8 +393,14 @@ class ReaderActivity :
viewBinding.infoBar.updatePadding(
top = systemBars.top,
)
val innerInsets = Insets.of(
systemBars.left,
if (viewBinding.appbarTop.isVisible) viewBinding.appbarTop.height else systemBars.top,
systemBars.right,
viewBinding.toolbarDocked?.takeIf { it.isVisible }?.height ?: systemBars.bottom,
)
return WindowInsetsCompat.Builder(insets)
.setInsets(WindowInsetsCompat.Type.systemBars(), Insets.NONE)
.setInsets(WindowInsetsCompat.Type.systemBars(), innerInsets)
.build()
}

View File

@@ -157,6 +157,12 @@ class ReaderViewModel @Inject constructor(
valueProducer = { isWebtoonGapsEnabled },
)
val isWebtoonPullGestureEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_WEBTOON_PULL_GESTURE,
valueProducer = { isWebtoonPullGestureEnabled },
)
val defaultWebtoonZoomOut = observeIsWebtoonZoomEnabled().flatMapLatest {
if (it) {
observeWebtoonZoomOut()
@@ -345,11 +351,14 @@ class ReaderViewModel @Inject constructor(
return@launchJob
}
ensureActive()
if (upperPos >= pages.lastIndex - BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.last().chapterId, isNext = true)
}
if (lowerPos <= BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.first().chapterId, isNext = false)
val autoLoadAllowed = readerMode.value != ReaderMode.WEBTOON || !isWebtoonPullGestureEnabled.value
if (autoLoadAllowed) {
if (upperPos >= pages.lastIndex - BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.last().chapterId, isNext = true)
}
if (lowerPos <= BOUNDS_PAGE_OFFSET) {
loadPrevNextChapter(pages.first().chapterId, isNext = false)
}
}
if (pageLoader.isPrefetchApplicable()) {
pageLoader.prefetch(pages.trySublist(upperPos + 1, upperPos + PREFETCH_LIMIT))

View File

@@ -86,6 +86,8 @@ class ReaderConfigSheet :
binding.buttonVertical.isChecked = mode == ReaderMode.VERTICAL
binding.switchDoubleReader.isChecked = settings.isReaderDoubleOnLandscape
binding.switchDoubleReader.isEnabled = mode == ReaderMode.STANDARD || mode == ReaderMode.REVERSED
binding.switchPullGesture.isChecked = settings.isWebtoonPullGestureEnabled
binding.switchPullGesture.isEnabled = mode == ReaderMode.WEBTOON
binding.checkableGroup.addOnButtonCheckedListener(this)
binding.buttonSavePage.setOnClickListener(this)
@@ -96,6 +98,7 @@ class ReaderConfigSheet :
binding.buttonScrollTimer.setOnClickListener(this)
binding.buttonBookmark.setOnClickListener(this)
binding.switchDoubleReader.setOnCheckedChangeListener(this)
binding.switchPullGesture.setOnCheckedChangeListener(this)
viewModel.isBookmarkAdded.observe(viewLifecycleOwner) {
binding.buttonBookmark.setText(if (it) R.string.bookmark_remove else R.string.bookmark_add)
@@ -172,6 +175,10 @@ class ReaderConfigSheet :
settings.isReaderDoubleOnLandscape = isChecked
findParentCallback(Callback::class.java)?.onDoubleModeChanged(isChecked)
}
R.id.switch_pull_gesture -> {
settings.isWebtoonPullGestureEnabled = isChecked
}
}
}
@@ -191,6 +198,7 @@ class ReaderConfigSheet :
else -> return
}
viewBinding?.switchDoubleReader?.isEnabled = newMode == ReaderMode.STANDARD || newMode == ReaderMode.REVERSED
viewBinding?.switchPullGesture?.isEnabled = newMode == ReaderMode.WEBTOON
if (newMode == mode) {
return
}

View File

@@ -86,9 +86,9 @@ class WebtoonImageView @JvmOverloads constructor(
desiredWidth = sWidth
desiredHeight = sHeight
} else if (resizeHeight) {
desiredHeight = (sHeight.toDouble() / sWidth.toDouble() * desiredWidth).roundToInt()
desiredHeight = (sHeight.toDouble() / sWidth.toDouble() * desiredWidth).toInt()
} else if (resizeWidth) {
desiredWidth = (sWidth.toDouble() / sHeight.toDouble() * desiredHeight).roundToInt()
desiredWidth = (sWidth.toDouble() / sHeight.toDouble() * desiredHeight).toInt()
}
}
desiredWidth = desiredWidth.coerceAtLeast(suggestedMinimumWidth)

View File

@@ -2,8 +2,13 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.animation.DecelerateInterpolator
import android.widget.TextView
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
@@ -27,7 +32,8 @@ import javax.inject.Inject
@AndroidEntryPoint
class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>(),
WebtoonRecyclerView.OnWebtoonScrollListener {
WebtoonRecyclerView.OnWebtoonScrollListener,
WebtoonRecyclerView.OnPullGestureListener {
@Inject
lateinit var networkState: NetworkState
@@ -38,6 +44,8 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
private val scrollInterpolator = DecelerateInterpolator()
private var recyclerLifecycleDispatcher: RecyclerViewLifecycleDispatcher? = null
private var canGoPrev = true
private var canGoNext = true
override fun onCreateViewBinding(
inflater: LayoutInflater,
@@ -53,6 +61,7 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
recyclerLifecycleDispatcher = RecyclerViewLifecycleDispatcher().also {
addOnScrollListener(it)
}
setOnPullGestureListener(this@WebtoonReaderFragment)
}
viewModel.isWebtoonZooEnabled.observe(viewLifecycleOwner) {
binding.frame.isZoomEnable = it
@@ -70,6 +79,18 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
viewModel.readerSettingsProducer.observe(viewLifecycleOwner) {
it.applyBackground(binding.root)
}
viewModel.isWebtoonPullGestureEnabled.observe(viewLifecycleOwner) { enabled ->
binding.recyclerView.isPullGestureEnabled = enabled
}
viewModel.uiState.observe(viewLifecycleOwner) { state ->
if (state != null) {
canGoPrev = state.chapterIndex > 0
canGoNext = state.chapterIndex < state.chaptersTotal - 1
} else {
canGoPrev = true
canGoNext = true
}
}
}
override fun onDestroyView() {
@@ -78,6 +99,19 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
super.onDestroyView()
}
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
val offsetInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
viewBinding?.apply {
feedbackTop.updateLayoutParams<MarginLayoutParams> {
topMargin = bottomMargin + offsetInsets.top
}
feedbackBottom.updateLayoutParams<MarginLayoutParams> {
bottomMargin = topMargin + offsetInsets.bottom
}
}
return super.onApplyWindowInsets(v, insets)
}
override fun onCreateAdapter() = WebtoonAdapter(
lifecycleOwner = viewLifecycleOwner,
loader = pageLoader,
@@ -168,6 +202,47 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
return true
}
override fun onPullProgressTop(progress: Float) {
val binding = viewBinding ?: return
if (canGoPrev) {
binding.feedbackTop.setFeedbackText(getString(R.string.pull_to_prev_chapter))
} else {
binding.feedbackTop.setFeedbackText(getString(R.string.pull_top_no_prev))
}
binding.feedbackTop.updateFeedback(progress)
}
override fun onPullProgressBottom(progress: Float) {
val binding = viewBinding ?: return
if (canGoNext) {
binding.feedbackBottom.setFeedbackText(getString(R.string.pull_to_next_chapter))
} else {
binding.feedbackBottom.setFeedbackText(getString(R.string.pull_bottom_no_next))
}
binding.feedbackBottom.updateFeedback(progress)
}
override fun onPullTriggeredTop() {
(viewBinding ?: return).feedbackTop.fadeOut()
if (canGoPrev) {
viewModel.switchChapterBy(-1)
}
}
override fun onPullTriggeredBottom() {
(viewBinding ?: return).feedbackBottom.fadeOut()
if (canGoNext) {
viewModel.switchChapterBy(1)
}
}
override fun onPullCancelled() {
viewBinding?.apply {
feedbackTop.fadeOut()
feedbackBottom.fadeOut()
}
}
private fun RecyclerView.findCurrentPagePosition(): Int {
val centerX = width / 2f
val centerY = height - resources.getDimension(R.dimen.webtoon_pages_gap)
@@ -177,4 +252,25 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
val view = findChildViewUnder(centerX, centerY) ?: return RecyclerView.NO_POSITION
return getChildAdapterPosition(view)
}
private fun TextView.updateFeedback(progress: Float) {
val clamped = progress.coerceIn(0f, 1.2f)
this.alpha = clamped.coerceAtMost(1f)
this.scaleX = 0.9f + 0.1f * clamped.coerceAtMost(1f)
this.scaleY = this.scaleX
}
private fun TextView.fadeOut() {
animate().alpha(0f).setDuration(150L).start()
}
private fun TextView.setFeedbackText(text: CharSequence) {
if (this.alpha <= 0f && text.isNotEmpty()) {
this.alpha = 0f
this.text = text
animate().alpha(1f).setDuration(120L).start()
} else {
this.text = text
}
}
}

View File

@@ -1,8 +1,10 @@
package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
import android.widget.EdgeEffect
import androidx.core.view.ViewCompat.TYPE_TOUCH
import androidx.core.view.forEach
import androidx.core.view.isEmpty
@@ -10,6 +12,8 @@ import androidx.core.view.isNotEmpty
import androidx.core.view.iterator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_BOTTOM
import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_TOP
import java.util.Collections
import java.util.LinkedList
import java.util.WeakHashMap
@@ -23,6 +27,18 @@ class WebtoonRecyclerView @JvmOverloads constructor(
private val detachedViews = Collections.newSetFromMap(WeakHashMap<View, Boolean>())
private var isFixingScroll = false
var isPullGestureEnabled: Boolean = false
var pullThreshold: Float = 0.3f
private var pullListener: OnPullGestureListener? = null
init {
setEdgeEffectFactory(PullEffect.Factory())
}
fun setOnPullGestureListener(listener: OnPullGestureListener?) {
pullListener = listener
}
override fun onChildDetachedFromWindow(child: View) {
super.onChildDetachedFromWindow(child)
detachedViews.add(child)
@@ -179,6 +195,68 @@ class WebtoonRecyclerView @JvmOverloads constructor(
}
}
private class PullEffect(
view: RecyclerView,
private val direction: Int,
private val pullThreshold: Float,
private val pullListener: OnPullGestureListener,
) : EdgeEffect(view.context) {
private var pullProgressTop: Float = 0f
private var pullProgressBottom: Float = 0f
override fun onPull(deltaDistance: Float) {
val sign = if (direction == DIRECTION_TOP) 1f else if (direction == DIRECTION_BOTTOM) 1f else 0f
if (sign != 0f) onPull(deltaDistance, 0.5f)
}
override fun onPull(deltaDistance: Float, displacement: Float) {
if (direction == DIRECTION_TOP) {
pullProgressTop = (pullProgressTop + deltaDistance).coerceAtLeast(0f)
pullListener.onPullProgressTop(pullProgressTop / pullThreshold)
} else if (direction == DIRECTION_BOTTOM) {
pullProgressBottom = (pullProgressBottom + deltaDistance).coerceAtLeast(0f)
pullListener.onPullProgressBottom(pullProgressBottom / pullThreshold)
}
}
override fun onRelease() {
var triggered = false
if (direction == DIRECTION_TOP) {
if (pullProgressTop >= pullThreshold) {
pullListener.onPullTriggeredTop()
triggered = true
}
pullProgressTop = 0f
pullListener.onPullProgressTop(0f)
} else if (direction == DIRECTION_BOTTOM) {
if (pullProgressBottom >= pullThreshold) {
pullListener.onPullTriggeredBottom()
triggered = true
}
pullProgressBottom = 0f
pullListener.onPullProgressBottom(0f)
}
if (!triggered) {
pullListener.onPullCancelled()
}
}
override fun draw(canvas: Canvas?): Boolean = false
class Factory : EdgeEffectFactory() {
override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect {
val pullListener = (view as? WebtoonRecyclerView)?.pullListener
return if (pullListener != null && view.isPullGestureEnabled) {
PullEffect(view, direction, view.pullThreshold, pullListener)
} else {
super.createEdgeEffect(view, direction)
}
}
}
}
interface OnWebtoonScrollListener {
fun onScrollChanged(
@@ -188,4 +266,12 @@ class WebtoonRecyclerView @JvmOverloads constructor(
lastVisiblePosition: Int,
)
}
interface OnPullGestureListener {
fun onPullProgressTop(progress: Float)
fun onPullProgressBottom(progress: Float)
fun onPullTriggeredTop()
fun onPullTriggeredBottom()
fun onPullCancelled()
}
}

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
@@ -40,6 +41,8 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorFooter
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.util.sizeOrZero
import javax.inject.Inject
@@ -55,8 +58,9 @@ open class RemoteListViewModel @Inject constructor(
protected val mangaListMapper: MangaListMapper,
private val exploreRepository: ExploreRepository,
sourcesRepository: MangaSourcesRepository,
mangaDataRepository: MangaDataRepository
) : MangaListViewModel(settings, mangaDataRepository), FilterCoordinator.Owner {
mangaDataRepository: MangaDataRepository,
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), FilterCoordinator.Owner {
val source = MangaSource(savedStateHandle[RemoteListFragment.ARG_SOURCE])
val isRandomLoading = MutableStateFlow(false)

View File

@@ -1,7 +1,6 @@
package org.koitharu.kotatsu.settings.protect
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.view.KeyEvent
@@ -115,7 +114,6 @@ class ProtectSetupActivity :
}
private fun isBiometricAvailable(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
return packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
}
}

View File

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
@@ -33,10 +32,6 @@ class DozeHelper(
}
fun startIgnoreDoseActivity(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Snackbar.make(fragment.listView ?: return false, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show()
return false
}
val context = fragment.context ?: return false
val packageName = context.packageName
val powerManager = context.powerManager ?: return false
@@ -58,9 +53,6 @@ class DozeHelper(
}
private fun isDozeIgnoreAvailable(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return false
}
val context = fragment.context ?: return false
val packageName = context.packageName
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager

View File

@@ -24,6 +24,9 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
import org.koitharu.kotatsu.suggestions.domain.SuggestionsListQuickFilter
import javax.inject.Inject
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import kotlinx.coroutines.flow.SharedFlow
@HiltViewModel
class SuggestionsViewModel @Inject constructor(
@@ -33,7 +36,8 @@ class SuggestionsViewModel @Inject constructor(
private val quickFilter: SuggestionsListQuickFilter,
private val suggestionsScheduler: SuggestionsWorker.Scheduler,
mangaDataRepository: MangaDataRepository,
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_SUGGESTIONS) { suggestionsListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.suggestionsListMode)

View File

@@ -57,6 +57,8 @@ class SyncAuthActivity : BaseActivity<ActivitySyncAuthBinding>(), View.OnClickLi
accountAuthenticatorResponse?.onRequestContinued()
viewBinding.buttonNext.setOnClickListener(this)
viewBinding.buttonBack.setOnClickListener(this)
viewBinding.buttonCancel.setOnClickListener(this)
viewBinding.buttonDone.setOnClickListener(this)
viewBinding.buttonSettings.setOnClickListener(this)
viewBinding.editEmail.addTextChangedListener(this)
viewBinding.editPassword.addTextChangedListener(this)

View File

@@ -31,6 +31,9 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.tracker.domain.UpdatesListQuickFilter
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
import javax.inject.Inject
import org.koitharu.kotatsu.local.data.LocalStorageChanges
import org.koitharu.kotatsu.local.domain.model.LocalManga
import kotlinx.coroutines.flow.SharedFlow
@HiltViewModel
class UpdatesViewModel @Inject constructor(
@@ -39,7 +42,8 @@ class UpdatesViewModel @Inject constructor(
private val mangaListMapper: MangaListMapper,
private val quickFilter: UpdatesListQuickFilter,
mangaDataRepository: MangaDataRepository,
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
override val content = combine(
quickFilter.appliedOptions.flatMapLatest { filterOptions ->

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.4" android:color="?android:colorBackground" />
</selector>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.6" android:color="?colorSurfaceBright" />
</selector>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorSurfaceContainerHigh" />
</selector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.3" android:color="?colorPrimary" android:state_checked="true"/>
<item android:color="@android:color/transparent"/>
</selector>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.27" android:color="?attr/colorOnSurface" />
</selector>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.4" android:color="@color/kotatsu_background" />
<item android:alpha="0.4" android:color="?android:colorBackground" />
</selector>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
<item android:alpha="0.6" android:color="@color/kotatsu_surfaceBright" />
<item android:alpha="0.6" android:color="?colorSurfaceBright" />
</selector>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/kotatsu_surfaceContainerHigh" />
<item android:color="?attr/colorSurfaceContainerHigh" />
</selector>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
<item android:alpha="0.3" android:color="@color/kotatsu_primary" android:state_checked="true"/>
<item android:alpha="0.3" android:color="?colorPrimary" android:state_checked="true"/>
<item android:color="@android:color/transparent"/>
</selector>
</selector>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
<item android:alpha="0.27" android:color="@color/kotatsu_onSurface" />
<item android:alpha="0.27" android:color="?attr/colorOnSurface" />
</selector>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="?attr/colorSurface" />
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
</shape>
</item>
<item>
<shape>
<solid android:color="@color/m3_popupmenu_overlay_color" />
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
</shape>
</item>
</layer-list>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="?attr/colorSurfaceContainerHigh" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="@color/m3_popupmenu_overlay_color" />
<corners android:radius="4dp" />
</shape>
</item>
</layer-list>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Fills the entire area with the divider's color first... -->
<item>
<shape
android:shape="rectangle">
<solid android:color="?attr/colorSurfaceVariant"/>
</shape>
</item>
<!-- ..., then draws a rectangle with the container color to cover the area not for the divider. -->
<item
android:bottom="1dp">
<shape
android:shape="rectangle">
<solid android:color="?android:attr/colorBackground"/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2A10,10 0 1,0 22,12A10,10 0 0,0 12,2Z"
android:strokeColor="@android:color/white"
android:strokeWidth="2"
android:fillColor="@android:color/transparent"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12,7l-3,3h2v4h2v-4h2z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M12,17l3,-3h-2v-4h-2v4h-2z"/>
</vector>

View File

@@ -2,13 +2,13 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@color/kotatsu_surface" />
<solid android:color="?attr/colorSurface" />
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
</shape>
</item>
<item>
<shape>
<solid android:color="@color/kotatsu_surface" />
<solid android:color="@color/m3_popupmenu_overlay_color" />
<corners android:radius="@dimen/m3_alert_dialog_corner_size" />
</shape>
</item>

View File

@@ -2,13 +2,13 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@color/kotatsu_surfaceContainerHighest" />
<solid android:color="?attr/colorSurfaceContainerHigh" />
<corners android:radius="4dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="@color/kotatsu_surfaceContainerHighest" />
<solid android:color="@color/m3_popupmenu_overlay_color" />
<corners android:radius="4dp" />
</shape>
</item>

View File

@@ -4,7 +4,7 @@
<item>
<shape
android:shape="rectangle">
<solid android:color="@color/kotatsu_surfaceVariant"/>
<solid android:color="?attr/colorSurfaceVariant"/>
</shape>
</item>
<!-- ..., then draws a rectangle with the container color to cover the area not for the divider. -->
@@ -12,7 +12,7 @@
android:bottom="1dp">
<shape
android:shape="rectangle">
<solid android:color="@color/kotatsu_background"/>
<solid android:color="?android:attr/colorBackground"/>
</shape>
</item>
</layer-list>

View File

@@ -2,6 +2,7 @@
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -16,4 +17,36 @@
android:orientation="vertical"
app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" />
<TextView
android:id="@+id/feedbackTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:layout_margin="@dimen/screen_padding"
android:alpha="0"
android:background="@drawable/bg_reader_indicator"
android:gravity="center"
android:paddingHorizontal="@dimen/margin_normal"
android:paddingVertical="@dimen/margin_small"
android:text="@string/pull_to_prev_chapter"
android:textAppearance="?textAppearanceBodyLarge"
android:theme="@style/ThemeOverlay.Material3.Dark"
tools:alpha="1" />
<TextView
android:id="@+id/feedbackBottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_margin="@dimen/screen_padding"
android:alpha="0"
android:background="@drawable/bg_reader_indicator"
android:gravity="center"
android:paddingHorizontal="@dimen/margin_normal"
android:paddingVertical="@dimen/margin_small"
android:text="@string/pull_to_next_chapter"
android:textAppearance="?textAppearanceBodyLarge"
android:theme="@style/ThemeOverlay.Material3.Dark"
tools:alpha="1" />
</org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame>

View File

@@ -129,6 +129,20 @@
android:textColor="?colorOnSurfaceVariant"
app:drawableStartCompat="@drawable/ic_split_horizontal" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_pull_gesture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:drawablePadding="?android:listPreferredItemPaddingStart"
android:minHeight="?android:listPreferredItemHeightSmall"
android:paddingStart="?android:listPreferredItemPaddingStart"
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/enable_pull_gesture_title"
android:textAppearance="?textAppearanceListItem"
android:textColor="?colorOnSurfaceVariant"
app:drawableStartCompat="@drawable/ic_gesture" />
<org.koitharu.kotatsu.core.ui.widgets.ListItemTextView
android:id="@+id/button_screen_rotate"
android:layout_width="match_parent"

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -859,4 +859,9 @@
<string name="no_chapters_in_manga">Гэтая манга не ўтрымлівае раздзелаў</string>
<string name="chapters_load_failed">Не атрымалася загрузіць спіс раздзелаў</string>
<string name="telegram_integration">Інтэграцыя з Telegram</string>
<string name="pull_top_no_prev">Няма папярэдняй главы</string>
<string name="pull_bottom_no_next">Няма наступнай главы</string>
<string name="enable_pull_gesture_title">Уключыць жэст перацягвання</string>
<string name="enable_pull_gesture_summary">Выкарыстоўвайце жэст пацягвання, каб пераключацца паміж главамі ў манхве</string>
<string name="test_parser">Праверыць крыніцу мангі</string>
</resources>

View File

@@ -123,7 +123,7 @@
<string name="manga_shelf">تاقچه</string>
<string name="not_available">در دسترس نیست</string>
<string name="cannot_find_available_storage">محل ذخیره سازی قابل دسترسی ئی یافت نشد</string>
<string name="scale_mode">حالت scale</string>
<string name="scale_mode">مقیاس</string>
<string name="zoom_mode_fit_center">متناسب به مرکز</string>
<string name="zoom_mode_keep_start">نگه داشتن در شروع</string>
<string name="reverse">معکوس</string>
@@ -280,4 +280,333 @@
<string name="report">گزارش</string>
<string name="status_planned">برنامه‌ریزی شده</string>
<string name="show_reading_indicators_summary">نشان دادن درصد خوانده شده در تاریخچه و پسندیده‌ها</string>
<string name="appwidget_recent_description">مانگا های اخیراً خوانده شده</string>
<string name="clear_cookies_summary">می‌تواند در صورت بروز برخی مشکلات کمک کند. همه مجوزها باطل خواهند شد</string>
<string name="show_all">نمایش همه</string>
<string name="invalid_domain_message">دامنه نامعتبر</string>
<string name="invalid_server_address_message">آدرس سرور نامعتیر</string>
<string name="select_range">انتخاب بازه</string>
<string name="clear_all_history">پاک کردن همه تاریخچه</string>
<string name="last_2_hours">۲ ساعت گذشته</string>
<string name="history_cleared">تاریخچه پاک شد</string>
<string name="manage">مدیریت</string>
<string name="no_bookmarks_yet">هنوز هیچ نشانکی وجود ندارد</string>
<string name="no_bookmarks_summary">می‌توانید هنگام خواندن مانگا نشانک ایجاد کنید</string>
<string name="bookmarks_removed">نشانک حذف شد</string>
<string name="no_manga_sources">هیچ منبعی برای مانگا وجود ندارد</string>
<string name="no_manga_sources_text">برای خواندن مانگا به صورت برخط، منابع مانگا را فعال کنید</string>
<string name="random">تصادفی</string>
<string name="categories_delete_confirm">آیا مطمئن هستید که می‌خواهید دسته‌های مورد علاقه انتخاب‌شده را حذف کنید؟\nتمام مانگاهای موجود در آن از بین خواهند رفت و این قابل بازگشت نیست.</string>
<string name="reorder">مرتب‌سازی مجدد</string>
<string name="empty">تهی</string>
<string name="explore">کاوش</string>
<string name="confirm_exit">برای خروج دکمه بازگشت را دوباره فشار دهید</string>
<string name="exit_confirmation_summary">دکمه باز گشت را دوبار بفشارید تا از برنامه خارج شوید</string>
<string name="exit_confirmation">تایید خروج</string>
<string name="saved_manga">مانگا های ذخیره شده</string>
<string name="pages_cache">حافظه نهان صفحات</string>
<string name="other_cache">دیگر حافظه های نهان</string>
<string name="storage_usage">فضای ذخیره‌سازی استفاده شده</string>
<string name="available">در دسترس</string>
<string name="removed_from_favourites">از مورد علاقه‌ها حذف شد</string>
<string name="options">گزینه ها</string>
<string name="not_found_404">محتوا حذف شده یا پیدا نشد</string>
<string name="incognito_mode">حالت ناشناس</string>
<string name="no_chapters">بدون فصل</string>
<string name="automatic_scroll">پیمایش خودکار</string>
<string name="reader_info_pattern">چپتر.%2$d%1$dصفحه.%4$d%3$d</string>
<string name="reader_info_bar">نمایش نوار اطلاعات در خوانشگر</string>
<string name="comics_archive">آرشیو کامیک ها</string>
<string name="folder_with_images">پوشه با تصویر</string>
<string name="importing_manga">وارد کردن مانگا</string>
<string name="import_completed">وارد کردن کامل شد</string>
<string name="import_completed_hint">شما میتوانید پرونده اصلی را از حافظه برای آزاد سازی فضای ذخیره سازی پاک کنید</string>
<string name="import_will_start_soon">وارد کردن به زودی آغاز خواهد شد</string>
<string name="feed">خوراک</string>
<string name="history_shortcuts">نمایش میانبرهای اخیر مانگا</string>
<string name="history_shortcuts_summary">دسترسی به مانگاهای اخیر با فشار طولانی روی آیکون برنامه</string>
<string name="reader_control_ltr_summary">جهت تغییر صفحه را به حالت خوانشگر تنظیم نکنید، به عنوان مثال، فشار دادن کلید سمت راست همیشه به صفحه بعدی می‌رود. این گزینه فقط بر دستگاه‌های ورودی سخت‌افزاری تأثیر می‌گذارد</string>
<string name="reader_control_ltr">کنترل خوانشگر ارگونومیک</string>
<string name="color_correction">تصحیح رنگ</string>
<string name="brightness">روشنایی</string>
<string name="contrast">کنتراست</string>
<string name="reset">بازنشانی</string>
<string name="text_unsaved_changes_prompt">ذخیره یا لغو تغییرات ذخیره نشده؟</string>
<string name="discard">لغو</string>
<string name="error_no_space_left">هیچ فضایی روی دستگاه باقی نمانده است</string>
<string name="reader_slider">نمایش اسلایدر تعویض صفحه</string>
<string name="webtoon_zoom">بزرگ نمایی وبتوون</string>
<string name="network_unavailable">شبکه در دسترس نیست</string>
<string name="network_unavailable_hint">برای خواندن مانگا به صورت برخط، وایفای یا دیتای تلفن همراه را روشن کنید</string>
<string name="server_error">خطای سمت سرویس دهنده (%1$d). لطفا بعداً دوباره تلاش کنید</string>
<string name="clear_new_chapters_counters">اطلاعات مربوط به فصل های جدید رو هم پاک کن</string>
<string name="compact">فشرده</string>
<string name="source_disabled">منبع غیر فعال شده است</string>
<string name="prefetch_content">پیش بازگذاری محتوا</string>
<string name="mark_as_current">نشانه گذاری به عنوان فعلی</string>
<string name="language">زبان</string>
<string name="share_logs">اشتراک گذاری گزارش</string>
<string name="enable_logging">فعال کردن گزارشات</string>
<string name="enable_logging_summary">ثبت برخی عمیات برای اهداف اشکال زدایی. اگه نمیدونی داری چیکار میکنی این گزینه رو روشن نکن</string>
<string name="show_suspicious_content">نمایش محتوای مشکوک</string>
<string name="theme_name_dynamic">پویا</string>
<string name="theme_name_expressive">صریح (تست)</string>
<string name="color_theme">طرح رنگ</string>
<string name="show_in_grid_view">نمایش به حالت شبکه ای</string>
<string name="theme_name_miku">میکو</string>
<string name="theme_name_asuka">آسوکا</string>
<string name="theme_name_mion">میون</string>
<string name="theme_name_rikka">ریکا</string>
<string name="theme_name_sakura">ساکورا</string>
<string name="theme_name_mamimi">مامیمی</string>
<string name="theme_name_kanade">کاناده</string>
<string name="nothing_here">چیزی اینجا نیست</string>
<string name="scrobbling_empty_hint">برای فعال کردن پیگیری پیشرفت خواندن، منو را انتخاب کنید ← پیگیری در صفحه جزئیات مانگا.</string>
<string name="services">خدمات</string>
<string name="allow_unstable_updates">اجازه بروزرسانی های ناپایدار</string>
<string name="allow_unstable_updates_summary">دریافت اعلان درباره ورژن های ناپایدار</string>
<string name="download_started">بارگیری آغاز شد</string>
<string name="got_it">متوجه شدم</string>
<string name="sources_reorder_tip">برای مرتب‌سازی مجدد آیتم‌ها، روی آنها ضربه بزنید و نگه دارید</string>
<string name="user_agent">هدر UserAgent</string>
<string name="settings_apply_restart_required">لطفا برای اعمال این تغییرات، برنامه را مجددا راه اندازی کنید</string>
<string name="comics_archive_import_description">میتونی یک یا چند فایل .cbz یا .zip رو انتخاب کنی، هر فایل به صورت یک مانگای مجزا شناخته میشه.</string>
<string name="folder_with_images_import_description">میتونی یک پوشه حاوی آرشیو ها یا تصاویر انتخاب کنی. هر آرشیو (یا زیرشاخه) به عنوان یک فصل شناخته میشن.</string>
<string name="speed">سرعت</string>
<string name="show_on_shelf">نمایش در ققسه</string>
<string name="sync_auth_hint">میتونی وارد یک اکانت بشی یا یک اکانت جدید بسازی</string>
<string name="find_similar">پیدا کردن مشابه</string>
<string name="sync_settings">تنظیمات همگام سازی</string>
<string name="server_address">آدرس سرور</string>
<string name="sync_host_description">می‌توانید از یک سرور همگام‌سازی خود-میزبان یا یک سرور پیش‌فرض استفاده کنید. اگر مطمئن نیستید که چه کاری انجام می‌دهید، این را تغییر ندهید.</string>
<string name="ignore_ssl_errors">نادیده گرفتن خطا های SSL</string>
<string name="mirror_switching">انتخاب خودکار آینه</string>
<string name="mirror_switching_summary">در صورت وجود آینه، دامنه‌ها را برای منابع مانگا به صورت خودکار تغییر دهید</string>
<string name="pause">توقف</string>
<string name="resume">ازسرگیری</string>
<string name="paused">متوقف شده</string>
<string name="remove_completed">منوی آیتم؛ اقدام برای حذف آیتم های کامل شده</string>
<string name="cancel_all">لغو همه</string>
<string name="downloads_wifi_only">بارگیری فقط از طریق وایفای</string>
<string name="downloads_wifi_only_summary">توقف دانلود در زمان تغییرشبکه به داده تلفن همراه</string>
<string name="suggestion_manga">پیشنهاد:%s</string>
<string name="suggestions_notifications_summary">گاهی اوقات اعلان‌هایی با مانگا پیشنهادی نمایش داده می‌شود</string>
<string name="more">بیشتر</string>
<string name="enable">فعال</string>
<string name="no_thanks">نه ممنون</string>
<string name="cancel_all_downloads_confirm">تمام دانلودهای فعال لغو می‌شوند، داده‌هایی که به‌طور ناقص دانلود شده‌اند از بین می‌روند</string>
<string name="remove_completed_downloads_confirm">تاریخچه بارگیری شما برای همیشه حذف خواهد شد. شامل فایل های بارگیری شده نمیشود</string>
<string name="text_downloads_list_holder">شما هیچ بارگیری ندارید</string>
<string name="downloads_paused">بارگیری ها متوقف شده اند</string>
<string name="downloads_removed">بارگیری ها حذف شده اند</string>
<string name="web_view_unavailable">وب ویو در دسترس نیست: بررسی کنید ارائه دهنده وب ویو نصب شده باشد</string>
<string name="clear_network_cache">پاک کردن حافظه نهان شبکه</string>
<string name="type">نویسه</string>
<string name="address">آدرس</string>
<string name="port">درگاه</string>
<string name="proxy">پروکسی</string>
<string name="invalid_value_message">مقدار نامعتبر</string>
<string name="email_password_enter_hint">برای ادامه رایانامه و گذرواژه را وارد کنید</string>
<string name="downloaded">بارگیری شده</string>
<string name="images_proxy_title">پروکسی بهینه سازی تصاویر</string>
<string name="images_procy_description">استفاده از سرویس wsrv.nl (در صورت امکان) برای کاهش مصرف ترافیک و افزایش سرعت بارگیری تصویر</string>
<string name="invert_colors">معکوس کردن رنگ ها</string>
<string name="username">نام کاربری</string>
<string name="password">گذرواژه</string>
<string name="authorization_optional">اعتبار سنجی (اختیاری)</string>
<string name="invalid_port_number">شماره درگاه نامعتبر</string>
<string name="network">شبکه</string>
<string name="data_and_privacy">داده ها و حریم خصوصی</string>
<string name="restore_summary">بازیابی نسخه پشتیبان تهیه شده قبلی</string>
<string name="webtoon_zoom_summary">امکان زوم در حالت وبتون</string>
<string name="reader_info_bar_summary">نمایش زمان و پیشرفت کنونی در بالای صفحه نمایش</string>
<string name="show_pages_numbers_summary">نمایش شماره صفحه در گوشیه پایین صفحه</string>
<string name="clear_source_cookies_summary">پاک کردن کوکی ها برای یک دامنه خاص. در بیشتر موارد، مجوز را باطل می‌کند</string>
<string name="download_option_all_chapters">تمام فصل ها ترجمه شده %s</string>
<string name="download_option_whole_manga">تمام مانگا</string>
<string name="download_option_first_n_chapters">ابتدا %s</string>
<string name="download_option_next_unread_n_chapters">خوانده نشده بعدی %s</string>
<string name="download_option_all_unread">تمام فصل های خوانده نشده</string>
<string name="download_option_all_unread_b">تمام فصل های خوانده نشده (%s)</string>
<string name="download_option_manual_selection">انتخاب فصل به صورت دستی</string>
<string name="pick_custom_directory">انتخاب پوشه سفارشی</string>
<string name="no_access_to_file">شما دسترسی به این پرونده یا پوشه را ندارید</string>
<string name="local_manga_directories">پوشه های مانگای محلی</string>
<string name="description">توضیحات</string>
<string name="this_month">این ماه</string>
<string name="voice_search">جستجوی صوتی</string>
<string name="related_manga">مانگا های مرتبط</string>
<string name="color_light">حالت روز</string>
<string name="color_dark">حالت شب</string>
<string name="color_white">سفید</string>
<string name="color_black">مشکی</string>
<string name="background">پسزمینه</string>
<string name="data_not_restored">داده ها بازیابی نشدند</string>
<string name="data_not_restored_text">از انتخاب درست پرونده پشتیبان اطمینان حاصل کنید</string>
<string name="manage_categories">مدیریت دسته بندی ها</string>
<string name="suggestions_wifi_only_summary">به‌روزرسانی نکردن پیشنهادها با استفاده از اتصالات شبکه محدود</string>
<string name="tracker_wifi_only_summary">عدم بررسی بروزرسانی فصل ها با اتصال شبکه محدود</string>
<string name="search_hint">عنوان مانگا را وارد کنید، ژانر یا نام منبع</string>
<string name="progress">پیشرفت</string>
<string name="order_added">اضافه شده</string>
<string name="show">نمایش</string>
<string name="captcha_required_summary">%s نیازمند حل کردن کپچا برای کار کردن است</string>
<string name="languages">زبان ها</string>
<string name="unknown">ناشناخته</string>
<string name="in_progress">درحال انجام</string>
<string name="disable_nsfw">غیر فعال سازی NSFW</string>
<string name="too_many_requests_message">درخواست‌ها خیلی زیاد است. بعداً دوباره امتحان کنید</string>
<string name="too_many_requests_message_retry">درخواست‌ها خیلی زیاد است. بعدا از %s دوباره تلاش کنید</string>
<string name="related_manga_summary">نمایش یک فهرست از مانگا های مرتبط. در برخی موارد ممکن است اشتباه یا ناقص باشد</string>
<string name="advanced">پیشرفته</string>
<string name="manga_list">مدیریت فهرست</string>
<string name="error_corrupted_file">داده نامعتبر است یا پرونده خراب است</string>
<string name="on_device">روی دستگاه</string>
<string name="directories">پوشه ها</string>
<string name="main_screen_sections">بخش‌های صفحه اصلی</string>
<string name="items_limit_exceeded">نمیتوان آیتم های بیشتری اضافه کرد</string>
<string name="to_top">به بالا</string>
<string name="moved_to_top">به بالا منتقل شد</string>
<string name="zoom_out">کوچک نمایی</string>
<string name="zoom_in">بزرگ نمایی</string>
<string name="reader_zoom_buttons">نمایش دکمه های بزرگنمایی</string>
<string name="reader_zoom_buttons_summary">اینکه آیا دکمه‌های کنترل بزرگنمایی در گوشه پایین سمت راست نمایش داده شوند یا خیر</string>
<string name="keep_screen_on">روشن نگه داشتن صفحه نمایش</string>
<string name="keep_screen_on_summary">جلوگیری از خاموش شدن صفحه نمایش در زمان خواندن مانگاه</string>
<string name="state_abandoned">رها شده</string>
<string name="enhanced_colors_summary">باندینگ را کاهش می‌دهد، اما ممکن است بر عملکرد تأثیر بگذارد</string>
<string name="enhanced_colors">حالت رنگ ۳۲ بیتی</string>
<string name="suggest_new_sources">پیشنهاد منابع جدید بعد از بروزرسانی برنامه</string>
<string name="suggest_new_sources_summary">درخواست فعال کردن منابع تازه اضافه شده پس از به‌روزرسانی برنامه</string>
<string name="list_options">گزینه های فهرست</string>
<string name="by_relevance">ارتباط</string>
<string name="categories">دسته بندی ها</string>
<string name="online_variant">نوع برخط</string>
<string name="periodic_backups">پشتیبان‌گیری‌های دوره‌ای</string>
<string name="backup_frequency">فرکانس ایجاد نسخه پشتیبان</string>
<string name="frequency_every_day">هر روز</string>
<string name="frequency_every_2_days">هر ۲ دوز</string>
<string name="frequency_once_per_week">هر هفته</string>
<string name="frequency_twice_per_month">دوبار در ماه</string>
<string name="frequency_once_per_month">یک بار درماه</string>
<string name="periodic_backups_enable">فعال کردن پشتیبان گیری دوره ای</string>
<string name="backups_output_directory">پوشه خروجی پشتیبان گیری</string>
<string name="last_successful_backup">آخرین پشتیبان گیری موفق:%s</string>
<string name="lock_screen_rotation">قفل چرخش صفحه</string>
<string name="content_type_manga">مانگا</string>
<string name="content_type_hentai">هنتای</string>
<string name="content_type_comics">کامیک بوک</string>
<string name="content_type_other">دیگر</string>
<string name="sources_catalog">کاتالوگ منابع</string>
<string name="source_enabled">منبع فعال شد</string>
<string name="no_manga_sources_catalog_text">هیچ منبع فعالی در این بخش وجود ندارد، یا همه آنها قبلا اضافه شده اند.\nبا ما همراه باشید</string>
<string name="no_manga_sources_found">هیچ منبع فعال بر اساس جستجوی شما یافت نشد</string>
<string name="catalog">کاتالوگ</string>
<string name="manage_sources">مدیریت منابع</string>
<string name="manual">دستی</string>
<string name="available_d">دردسترس: %1$d</string>
<string name="disable_nsfw_summary">در صورت امکان مانگای بزرگسالان و منابع NSFW را غیر فعال کنید</string>
<string name="state_paused">متوقف شده</string>
<string name="reader_optimize">کاهش مصرف حافظه (آزمایشی)</string>
<string name="reader_optimize_summary">کاهش کیفیت صفحات خارج از صفحه برای استفاده کمتر از حافظه</string>
<string name="state">حالت</string>
<string name="downloads_resumed">بارگیری‌ها ازسر گرفته شده‌اند</string>
<string name="downloads_cancelled">بارگیری‌ها لغو شده‌اند</string>
<string name="suggestions_enable_prompt">آیا می‌خواهید پیشنهادهای مانگای شخصی شده دریافت کنید؟</string>
<string name="error_multiple_genres_not_supported">فیلتر کردن با چندین ژانر توسط این منبع مانگا پشتیبانی نمیشود</string>
<string name="error_multiple_states_not_supported">فیلتر کردن با چندین وضعیت توسط این منبع مانگا پشتیبانی نمیشود</string>
<string name="error_search_not_supported">جستجو کردن توسط این منبع مانگا پشتیبانی نمیشود</string>
<string name="downloads_settings_info">اگر با مسدود شدن از طرف سرویس دهنده میشکل دارید، میتوانید کند کننده بارگیری را برای هر منبع مانگاه به صورت جداگانه در تنظیمات منبع فعال کنید</string>
<string name="skip">رد شدن</string>
<string name="grayscale">حالت خاکستری</string>
<string name="globally">سراسری</string>
<string name="this_manga">همین مانگا</string>
<string name="color_correction_apply_text">این تنظیم‌ها می‌توانند برای همه‌ی مانگاها یا فقط همین مانگا اعمال شوند. اگر برای همه اعمال شوند، تنظیم‌های تک‌به‌تک دست‌نخورده می‌مانند.</string>
<string name="apply">اعمال</string>
<string name="error_filter_locale_genre_not_supported">این منبع از فیلتر هم‌زمان بر پایه‌ی ژانر و زبان پشتیبانی نمی‌کند</string>
<string name="error_filter_states_genre_not_supported">این منبع از فیلتر هم‌زمان بر پایه‌ی ژانر و وضعیت پشتیبانی نمی‌کند</string>
<string name="genres_search_hint">برای شروع ژانر مورد نظر خود را بنویسید</string>
<string name="disable_battery_optimization_summary_downloads">اگر مشکلی با بارگیری دارید،‌ممکن است به شروع شدن آن کمک کند</string>
<string name="welcome_text">لطفا منابع محتوا مورد نظر خود را انتخاب کنید. این بخش را میتوان بعدا از طریق تنظیمات تغییر داد</string>
<string name="sync_auth">برای همگام سازی حساب وارد شوید</string>
<string name="restore">بازیابی</string>
<string name="backup_date_">تاریخ پشتیبان: %s</string>
<string name="state_upcoming">پیشرو</string>
<string name="by_name_reverse">نام برعکس</string>
<string name="content_rating">امتیاز دهی به محتوا</string>
<string name="genres_exclude">نادیده گرفتن ژانر ها</string>
<string name="rating_safe">امن</string>
<string name="rating_suggestive">تحریک‌آمیز</string>
<string name="rating_adult">بزرگسال</string>
<string name="default_tab">صفحه پیشفرض</string>
<string name="mark_as_completed">علامت به عنوان تمام شده</string>
<string name="mark_as_completed_prompt">علامت زدن مانگا به عنوان کاملا خوانده شده؟\n\nاخطار: پیشرفت فعلی از دست خواهد رفت.</string>
<string name="category_hidden_done">دسته بندی از صفحه اصلی پنهان شده و میتوان از طریق منو ← دسته بندی مانگا ها به ان دسترسی داشت</string>
<string name="volume_">جلد %d</string>
<string name="volume_unknown">جلد نامشخص</string>
<string name="incognito_mode_hint">پیشرفت خواندن شما ذخیره نخواهد شد</string>
<string name="vertical">عمودی</string>
<string name="last_read">اخرین خوانده شده</string>
<string name="show_menu">نمایش فهرست</string>
<string name="toggle_ui">نمایش/پنهان سازی رابط کاربری</string>
<string name="prev_chapter">فصل قبل</string>
<string name="next_chapter">فصل بعد</string>
<string name="prev_page">صفحه قبل</string>
<string name="next_page">صفحه بعد</string>
<string name="reader_actions">کنش‌های خواننده</string>
<string name="reader_actions_summary">پیکربندی کنش ها برای قسمت های قابل لمس صفحه</string>
<string name="switch_pages_volume_buttons">به‌کار انداختن دکمه‌های صدا</string>
<string name="switch_pages_volume_buttons_summary">استفاده از دکمه های صدا برای عوض کردن صفحات</string>
<string name="reader_navigation_inverted">معکوس کردن کنترل های ناوبری</string>
<string name="reader_navigation_inverted_summary">عوض کردن جهت دکمه‌های صدا و کلیدهای سخت‌افزاری (چپ/راست/بالا/پایین)</string>
<string name="tap_action">کنش های لمسی</string>
<string name="long_tap_action">کنش های لمس طولانی</string>
<string name="none">هیچکدام</string>
<string name="config_reset_confirm">تنظیمات با مقادیر پیشفرض بازنویسی شود؟ این عمل قابل بازگشت نیست.</string>
<string name="use_two_pages_landscape">نمایش دو صفحه در حالت افقی (آزمایشی)</string>
<string name="default_webtoon_zoom_out">کوچک‌نمایی پیش‌فرض وبتون</string>
<string name="fullscreen_mode">حالت تمام صفحه</string>
<string name="reader_fullscreen_summary">پنهان کردن نوار وضعیت و راهبری</string>
<string name="reading_time_estimation">نمایش زمان خواندن تخمینی</string>
<string name="reading_time_estimation_summary">مقدار زمان تخمینی ممکن است نادرست باشد</string>
<string name="suggestions_unavailable_text">پیشنهادات غیر فعال است</string>
<string name="check_for_new_chapters_disabled">بررسی برای فصول جدید غیر فعال است</string>
<string name="show_labels_in_navbar">نمایش برچسب‌ها در نوار راهبری</string>
<string name="pages_saving">ذخیره‌سازی صفحه‌ها</string>
<string name="ask_for_dest_dir_every_time">هر بار پوشه‌ی مقصد را بپرس</string>
<string name="default_page_save_dir">پوشه پیشفرض ذخیره سازی صفحات</string>
<string name="remove_from_history">پاک کردن از تاریخچه</string>
<string name="location">مکان</string>
<string name="preferred_download_format">قالب بارگیری ترجیحی</string>
<string name="single_cbz_file">پروندهٔ CBZ تکی</string>
<string name="multiple_cbz_files">پروندهٔ CBZ چندتایی</string>
<string name="reading_stats">آمار خواندن</string>
<string name="other_manga">مانگا های دیگر</string>
<string name="less_than_minute">کمتر از یک دقیقه</string>
<string name="statistics">آمار</string>
<string name="clear_stats">پاک کردن آمار ها</string>
<string name="stats_cleared">آمار ها پاک شدند</string>
<string name="clear_stats_confirm">آیا قصد پاک کردن تمام آمار ها را دارید؟ این عمل قابل بازگشت نیست.</string>
<string name="week">هفته</string>
<string name="month">ماه</string>
<string name="all_time">همه</string>
<string name="day">روز</string>
<string name="three_months">سه ماه</string>
<string name="empty_stats_text">هیچ آماری برای دوره انتخاب شده وجود ندارد</string>
<string name="pages_read_s">صفحات خوانده شده: %s</string>
<string name="alternatives">جایگزین‌ها</string>
<string name="migrate">انتقال</string>
<string name="migrate_confirmation">مانگا «%1$s» از «%2$s» با «%3$s» از «%4$s» (در صورت وجود داشتن) در تاریخچه و مورد علاقه ها جایگزین خواهد شد</string>
<string name="manga_migration">انتقال مانگا</string>
<string name="migration_completed">انتقال انجام شد</string>
<string name="delete_read_chapters">پاک کردن فصل های خوانده شده</string>
<string name="no_chapters_deleted">هیچ فصلی پاک نشد</string>
<string name="chapters_deleted_pattern">%1$s حذف شده، %2$s پاک شده</string>
<string name="delete_read_chapters_summary">فصل های خوانده شده برای آزاد سازی فضا ذخیره سازی حذف کنید</string>
<string name="delete_read_chapters_prompt">این کار تمام فصل هایی که به عنوان خوانده شده علامت گزاری شده اند را برای همیشه پاک خواهد کرد. شما میتونید بعدا آن هارا دانلود کنید اما فصل های افزوده‌شده ممکن است برای همیشه پاک شوند</string>
<string name="delete_read_chapters_auto">حذف فصل های خوانده شده به صورت خودکار</string>
<string name="runs_on_app_start">با آغاز برنامه اجرا می‌شود</string>
<string name="split_by_translations">بخش‌بندی بر اساس ترجمه‌ه</string>
<string name="split_by_translations_summary">نمایش فصل ها با ترجمه های مختلف به صورت جداگانه بجای نمایش در یک لیست</string>
<string name="order_oldest">قدیمی ترین</string>
</resources>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="hours">
<item quantity="one">%1$d 𐍈𐌴𐌹𐌻𐌰</item>
<item quantity="other">%1$d 𐍈𐌴𐌹𐌻𐍉𐍃</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%1$d 𐌼𐌹𐌽𐌿𐍄𐌿𐍃</item>
<item quantity="other">%1$d 𐌼𐌹𐌽𐌿𐍄𐌾𐌿𐍃</item>
</plurals>
<plurals name="days_ago">
<item quantity="one">𐍆𐌰𐌿𐍂𐌰 %1$d 𐌳𐌰𐌲</item>
<item quantity="other">𐍆𐌰𐌿𐍂𐌰 %1$d 𐌳𐌰𐌲𐌰𐌽𐍃</item>
</plurals>
<plurals name="months_ago">
<item quantity="one">𐍆𐌰𐌿𐍂𐌰 𐌼𐌴𐌽𐍉𐌸 %1$d</item>
<item quantity="other">𐍆𐌰𐌿𐍂𐌰 𐌼𐌴𐌽𐍉𐌸𐌿𐌼 %1$d</item>
</plurals>
</resources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="history">𐍃𐍀𐌹𐌻𐌻</string>
<string name="error_occurred">𐌰𐌹𐍂𐌶𐌴𐌹 𐍅𐌰𐍂𐌸</string>
<string name="remote_sources">𐌼𐌰𐌲𐌲𐌹𐌽𐍃 𐌱𐍂𐌿𐌽𐌽𐌰𐌽𐍃</string>
<string name="computing_">𐍂𐌰𐌷𐌽𐌾𐌰𐌳𐌰…</string>
<string name="read">𐌰𐌽𐌰𐌺𐌿𐌽𐌽𐌰𐌽</string>
<string name="share_s">%s 𐌳𐌰𐌹𐌻𐌾𐌰𐌽</string>
<string name="search">𐍃𐍉𐌺𐌾𐌰𐌽</string>
<string name="search_manga">𐌼𐌰𐌲𐌲𐌰 𐍃𐍉𐌺𐌾𐌰𐌽</string>
</resources>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="items">
<item quantity="other">%1$d butir</item>
<item quantity="other">%1$d butir</item>
</plurals>
<plurals name="new_chapters">
<item quantity="other">%1$d bab baru</item>
<item quantity="other">%1$d Jilid Baru</item>
</plurals>
<plurals name="chapters">
<item quantity="other">%1$d bab</item>
<item quantity="other">%1$d Jilid</item>
</plurals>
<plurals name="minutes_ago">
<item quantity="other">%1$d menit yang lalu</item>
@@ -27,4 +27,4 @@
<plurals name="minutes">
<item quantity="other">%1$d menit</item>
</plurals>
</resources>
</resources>

View File

@@ -30,8 +30,8 @@
<string name="search_manga">Cari manga</string>
<string name="manga_downloading_">Mengunduh…</string>
<string name="processing_">Memproses…</string>
<string name="download_complete">Diunduh</string>
<string name="downloads">Unduhan</string>
<string name="download_complete">Mengambil data (unduh)</string>
<string name="downloads">Ambil data (Unduh)</string>
<string name="by_name">Nama</string>
<string name="popular">Populer</string>
<string name="updated">Diperbarui</string>
@@ -65,7 +65,7 @@
<string name="text_delete_local_manga">Hapus \"%s\" dari perangkat secara permanen?</string>
<string name="reader_settings">Pengaturan pembaca</string>
<string name="switch_pages">Ganti halaman</string>
<string name="chapters">Bab</string>
<string name="chapters">Bagian jilid</string>
<string name="list">Daftar</string>
<string name="detailed_list">Daftar rinci</string>
<string name="webtoon">Webtoon</string>
@@ -74,13 +74,13 @@
<string name="clear_thumbs_cache">Bersihkan singgahan gambar mini</string>
<string name="clear_search_history">Bersihkan riwayat pencarian</string>
<string name="search_history_cleared">Dibersihkan</string>
<string name="domain">Domain</string>
<string name="domain">Ranah web</string>
<string name="app_update_available">Versi baru aplikasi tersedia</string>
<string name="open_in_browser">Buka di peramban web</string>
<string name="notifications">Notifikasi</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d dari %2$d diaktifkan</string>
<string name="new_chapters">Bab baru</string>
<string name="download">Unduh</string>
<string name="new_chapters">Sub-bab baru</string>
<string name="download">Ambil data (Unduh)</string>
<string name="notifications_settings">Pengaturan notifikasi</string>
<string name="notification_sound">Suara notifikasi</string>
<string name="text_empty_holder_primary">Sepi juga di sini…</string>
@@ -277,7 +277,7 @@
<string name="storage_usage">Penggunaan penyimpanan</string>
<string name="available">Tersedia</string>
<string name="incognito_mode">Mode penyamaran</string>
<string name="automatic_scroll">Gulir otomatis</string>
<string name="automatic_scroll">Pengguliran otomatis</string>
<string name="comics_archive">Arsip komik</string>
<string name="folder_with_images">Folder dengan gambar</string>
<string name="import_completed_hint">Anda bisa menghapus berkas asli dari penyimpanan untuk menghemat ruang</string>
@@ -307,18 +307,18 @@
<string name="confirm_exit">Tekan Kembali lagi untuk keluar</string>
<string name="no_chapters">Tidak ada bab</string>
<string name="history_shortcuts">Tampilkan pintasan manga baru-baru ini</string>
<string name="history_shortcuts_summary">Buat manga baru-baru ini tersedia dengan menekan panjang pada ikon aplikasi</string>
<string name="history_shortcuts_summary">Buat manga terbaru tersedia dengan menekan lama ikon aplikasi</string>
<string name="select_range">Pilih rentang</string>
<string name="disable_all">Matikan semua</string>
<string name="dns_over_https">DNS over HTTPS</string>
<string name="dns_over_https">DNS melalui HTTPS</string>
<string name="status_dropped">Didrop</string>
<string name="theme_name_mamimi">Mamimi</string>
<string name="server_error">Galat sisi server (%1$d). Silakan coba lagi nanti</string>
<string name="server_error">Kesalahan di sisi server (%1$d). Silakan coba lagi nanti</string>
<string name="compact">Padat</string>
<string name="prefetch_content">Pramuat konten</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="nothing_here">Tidak ada apapun di sini</string>
<string name="reader_control_ltr_summary">Jangan sesuaikan arah pindah halaman ke mode pembaca, mis. menekan tombol kanan selalu pindah ke halaman selanjutnya. Opsi ini hanya berdampak pada perangkat dengan tombol masukan perangkat keras</string>
<string name="reader_control_ltr_summary">Jangan ubah arah pergantian halaman ke mode pembaca, misalnya, menekan tombol kanan selalu beralih ke halaman berikutnya. Opsi ini hanya berlaku untuk perangkat masukan hardware</string>
<string name="source_disabled">Sumber dinonaktifkan</string>
<string name="mark_as_current">Tandai sebagai saat ini</string>
<string name="show_suspicious_content">Tampilkan konten yang mencurigakan</string>
@@ -355,7 +355,7 @@
<string name="reader_control_ltr">Kontrol pembaca ergonomis</string>
<string name="color_correction">Koreksi warna</string>
<string name="reader_slider">Perlihatkan penggeser peralihan halaman</string>
<string name="webtoon_zoom">Zum Webtoon</string>
<string name="webtoon_zoom">Pembesaran Webtoon</string>
<string name="network_unavailable">Jaringan tidak tersedia</string>
<string name="got_it">Oke</string>
<string name="sources_reorder_tip">Ketuk dan tahan item untuk menyusun ulang</string>
@@ -509,7 +509,7 @@
<string name="state_upcoming">Mendatang</string>
<string name="color_correction_apply_text">Pengaturan ini dapat diterapkan secara menyeluruh atau hanya pada manga saat ini. Jika diterapkan secara menyeluruh, pengaturan pada manga tidak akan ditimpa.</string>
<string name="source_enabled">Sumber yang diaktifkan</string>
<string name="disable_nsfw_summary">Nonaktifkan sumber TAUSB and sembunyikan manga dewasa dari daftar jika memungkinkan</string>
<string name="disable_nsfw_summary">Nonaktifkan sumber TAUSB dan sembunyikan manga dewasa dari daftar jika memungkinkan</string>
<string name="content_rating">Peringkat konten</string>
<string name="backup_date_">Tanggal dicadangkan %s</string>
<string name="available_d">Tersedia:%1$d</string>
@@ -729,13 +729,13 @@
<string name="filter_search_warning">Sumber ini tidak mendukung pencarian dengan filter. Filter Anda telah dikosongkan</string>
<string name="demographic_shoujo">Shoujo</string>
<string name="manga_replaced">Manga \"%1$s\" (%2$s) diganti dengan \"%3$s\" (%4$s)</string>
<string name="content_type_manhua">Manhua</string>
<string name="content_type_manhua">Komik Mandarin (manhua)</string>
<string name="user_manual">Manual pengguna</string>
<string name="destination_directory">Direktori tujuan</string>
<string name="demographic_josei">Josei</string>
<string name="download_new_chapters">Unduh bab baru</string>
<string name="content_type_novel">Novel</string>
<string name="content_type_manhwa">Manhwa</string>
<string name="content_type_manhwa">Komik Korea (Manhwa)</string>
<string name="source_code">Kode sumber</string>
<string name="download_cellular_confirm">Izinkan mengunduh melalui jaringan seluler?</string>
<string name="demographics">Demografis</string>
@@ -757,7 +757,7 @@
<string name="chapter_selection_hint">Anda bisa memilih bab untuk diunduh dengan menekan lama item di dalam daftar bab.</string>
<string name="rating">Nilai</string>
<string name="source">Sumber</string>
<string name="added_long_ago">Bahasa</string>
<string name="added_long_ago">Ditambahkan sejak lama</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>
@@ -807,10 +807,10 @@
<string name="tags_warnings_summary">Tandai genre yang mungkin tidak pantas untuk sebagian besar pengguna</string>
<string name="error_non_file_uri">Jalur yang dipilih tidak dapat digunakan karena tidak menunjukkan berkas atau direktori</string>
<string name="use_default_cover">gunakan penutup default</string>
<string name="pick_manga_page">Indonesian</string>
<string name="pick_custom_file">Indonesian</string>
<string name="change_cover">Indonesian</string>
<string name="theme_name_expressive">Indonesian</string>
<string name="pick_manga_page">Pilih halaman manga</string>
<string name="pick_custom_file">Pilih file khusus</string>
<string name="change_cover">Ganti penutup</string>
<string name="theme_name_expressive">Ekspresif (Test)</string>
<string name="page_switch_timer">Halaman akan berganti setiap ~%d detik</string>
<string name="expand">Memperluas</string>
<string name="adblock">Blokir iklan di browser</string>
@@ -846,4 +846,23 @@
<string name="discord_token_description">Masukkan Token Discord Anda atau klik %s untuk mendapatkannya melalui browser</string>
<string name="discord_token_hint">Tempelkan Token Discord Anda di sini</string>
<string name="discord_rpc_summary">Tampilkan status membaca Anda di Discord</string>
<string name="discord_rpc_description">Membaca manga di Kotatsu - aplikasi pembaca manga</string>
<string name="manga_restricted_description">Manga ini tidak tersedia untuk dibaca di sumber ini. Coba cari di sumber lain atau buka di browser untuk informasi lebih lanjut</string>
<string name="chapters_load_failed">Gagal memuat daftar bab</string>
<string name="rpc_skip_nsfw_summary">Jangan gunakan RPC untuk konten dewasa</string>
<string name="show_floating_control_button">Tampilkan tombol kontrol mengambang</string>
<string name="unavailable">Tidak tersedia</string>
<string name="invalid_token">Token tidak valid: %s</string>
<string name="no_chapters_in_manga">Manga ini tidak mengandung bab apa pun</string>
<string name="telegram_integration">Integrasi Telegram</string>
<string name="test_parser">Uji sumber manga</string>
<string name="reading_s">Membaca %s</string>
<string name="read_on_s">Baca terus %s</string>
<string name="obtain">Memperoleh</string>
<string name="pull_to_prev_chapter">Lepas untuk membuka bab sebelumnya</string>
<string name="pull_to_next_chapter">Lepas untuk membuka bab selanjutnya</string>
<string name="pull_top_no_prev">Tidak ada bab sebelumnya</string>
<string name="pull_bottom_no_next">Tidak ada bab berikutnya</string>
<string name="enable_pull_gesture_title">Aktifkan gerakan tarik</string>
<string name="enable_pull_gesture_summary">Gunakan gerakan tarik untuk pindah bab di webtoon</string>
</resources>

View File

@@ -859,4 +859,11 @@
<string name="no_chapters_in_manga">Questo manga non contiene capitoli</string>
<string name="telegram_integration">Integrazione con Telegram</string>
<string name="chapters_load_failed">Errore nel caricamento della lista dei capitoli</string>
<string name="test_parser">Testa fonte manga</string>
<string name="pull_to_prev_chapter">Rilascia per aprire il capitolo precedente</string>
<string name="pull_to_next_chapter">Rilascia per aprire il capitolo successivo</string>
<string name="pull_top_no_prev">Nessun capitolo precedente</string>
<string name="pull_bottom_no_next">Nessun capitolo successivo</string>
<string name="enable_pull_gesture_title">Abilita gesto di scorrimento</string>
<string name="enable_pull_gesture_summary">Abilita gesto di scorrimento per cambiare capitolo in webtoon</string>
</resources>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="items">
<item quantity="other">%1$d glintir</item>
</plurals>
<plurals name="new_chapters">
<item quantity="other">%1$d bagéan</item>
</plurals>
<plurals name="chapters">
<item quantity="other">%1$d bagéyan</item>
</plurals>
<plurals name="minutes_ago">
<item quantity="other">%1$d waktu kepungkur</item>
</plurals>
<plurals name="hours_ago">
<item quantity="other">%1$d jam kepungkur</item>
</plurals>
<plurals name="days_ago">
<item quantity="other">%1$d dinten ingkang rumiyin</item>
</plurals>
<plurals name="months_ago">
<item quantity="other">%1$d sasi ingkang rumiyin</item>
</plurals>
<plurals name="hours">
<item quantity="other">%1$d tabuh</item>
</plurals>
<plurals name="minutes">
<item quantity="other">%1$d menit</item>
</plurals>
</resources>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="demographic_kodomo">bocah-bocah</string>
<string name="local_storage">Panyimpenan tlatah</string>
<string name="favourites">Seneng</string>
<string name="history">Babadan</string>
<string name="error_occurred">Ana kesalahan</string>
<string name="network_error">kelepatan jaringan</string>
<string name="details">rinci</string>
<string name="chapters">Jilid</string>
<string name="list">dhaptar</string>
<string name="detailed_list">dhaptar rinci</string>
<string name="grid">wewacan</string>
<string name="list_mode">modus dhaptar</string>
<string name="settings">Pathokan</string>
<string name="remote_sources">Sumber Manga</string>
<string name="loading_">Ngewrat…</string>
<string name="computing_">Ngitung…</string>
<string name="close">Tudhung</string>
<string name="try_again">Jajal Maneh</string>
<string name="retry">Baleni</string>
<string name="clear_history">Busek riwayat</string>
<string name="nothing_found">Mboten dipunpanggihaken</string>
<string name="history_is_empty">Ora ana babad maneh</string>
<string name="read">Moco</string>
<string name="you_have_not_favourites_yet">Durung ana kang disenengi</string>
<string name="add_to_favourites">favoritkan puniki</string>
<string name="add_new_category">Kategori anyar</string>
<string name="add">Nambah</string>
<string name="save">Dekek</string>
<string name="share">Edumaken</string>
<string name="create_shortcut">Damel pintasan</string>
<string name="share_s">Edumaken %s</string>
<string name="search">Luru</string>
<string name="search_manga">Golek komik</string>
<string name="manga_downloading_">Ngunduh…</string>
<string name="processing_">Mroses…</string>
<string name="download_complete">Diundhuh</string>
<string name="downloads">Unggah</string>
<string name="by_name">Jeneng</string>
<string name="popular">Kondhang</string>
<string name="updated">Dianyari</string>
<string name="newest">Paling anyar</string>
<string name="by_rating">Bijine</string>
<string name="sort_order">Urutaken</string>
<string name="filter">Milah</string>
<string name="theme">Intining</string>
<string name="light">Padhang</string>
<string name="dark">Peteng</string>
<string name="follow_system">Dereki sistem</string>
<string name="pages">Kaca</string>
<string name="standard">Pajeging</string>
<string name="read_mode">Mode waos</string>
<string name="test_parser">Mriksa asal</string>
<string name="telegram_integration">Panyawijining Telegram</string>
<string name="clear_pages_cache">Resiki singgahan platar</string>
<string name="clear">Resiki</string>
<string name="remove">Busek</string>
<string name="_s_deleted_from_local_storage">\"%s\" dibusek saka panyimpenan</string>
<string name="save_page">Dekek Plataran</string>
<string name="page_saved">Plataran dipundekek</string>
<string name="pages_saved">Plataran dipundekek</string>
<string name="share_image">Edumaken citro</string>
<string name="_import">Pundhut barang jawi</string>
<string name="delete">Busek</string>
<string name="operation_not_supported">tindakan menika mboten dipunsengkuyung</string>
<string name="text_file_not_supported">Milih antawis berkas zip utawi cbz.</string>
</resources>

View File

@@ -29,7 +29,11 @@
<item quantity="other">%1$d сағат бұрын</item>
</plurals>
<plurals name="hours">
<item quantity="one">Бір</item>
<item quantity="one">%1$d сағат</item>
<item quantity="other">Нөл , екі, үш, төрт, бес, алты, жеті, сегіз, тоғыз, он</item>
</plurals>
</resources>
<plurals name="minutes">
<item quantity="one">%1$d минут</item>
<item quantity="other">%1$d минут</item>
</plurals>
</resources>

View File

@@ -31,7 +31,7 @@
<string name="add">Қосу</string>
<string name="save">Сақтау</string>
<string name="share">Бөлісу</string>
<string name="create_shortcut">Таңбаша жасау</string>
<string name="create_shortcut">Таңбаша жасау</string>
<string name="share_s">%s бөлісу</string>
<string name="search">Іздеу</string>
<string name="manga_downloading_">Жүктеліп жатыр…</string>
@@ -53,7 +53,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">Бет сақталды</string>
<string name="share_image">Суретті бөлісу</string>
<string name="_import">Импорт</string>
<string name="no_description">Сипаттамасы жоқ</string>
@@ -79,7 +79,7 @@
<string name="notification_sound">Мәлімдеме дыбысы</string>
<string name="light_indicator">LED индикаттау</string>
<string name="vibration">Діріл</string>
<string name="favourites_categories">Таңдаулы санаттар</string>
<string name="favourites_categories">Таңдаулы санаты</string>
<string name="remove_category">Жою</string>
<string name="text_empty_holder_primary">Мына жер бос екен…</string>
<string name="text_history_holder_primary">Бұ жерде оқығаныңыз тұрады</string>
@@ -257,7 +257,7 @@
<string name="hide">Жасыру</string>
<string name="exclude_nsfw_from_history_summary">ҰЯТСЫЗ деген маңганы оқығаныңыз тарихыңызда сақталмайды</string>
<string name="show_reading_indicators_summary">Таңдаулы мен тарихта оқылған туынды пайызын көрсету</string>
<string name="use_fingerprint">Қолжетімді болса саусақ ізін қолдану</string>
<string name="use_fingerprint">Қолжетімді болса биометрика қолдану</string>
<string name="onboard_text">Маңганы қай тілде оқығыңыз келетінін таңдаңыз. Кейінірек баптауда өзгертіп ала аласыз.</string>
<string name="suggestions_updating">Ұсынысты жаңарту</string>
<string name="percent_string_pattern">%%%1$s</string>
@@ -537,4 +537,8 @@
<string name="reader_actions">Оқымадағы әрекет</string>
<string name="sync_auth">Синхрондау тіркелгісіне кіру</string>
<string name="config_reset_confirm">Әдепкі баптауға қайтайық па? Әрекетті қайтаруға болмайды.</string>
<string name="retry">Қайталау</string>
<string name="pages_saved">Беттер сақталды</string>
<string name="text_empty_holder_secondary_filtered">Сүзіп алғаныңызға сай маңга жоқ</string>
<string name="nsfw_16">16+</string>
</resources>

View File

@@ -30,17 +30,17 @@
<string name="add">Tambah</string>
<string name="save">Simpan</string>
<string name="share">Kongsi</string>
<string name="create_shortcut">Buat pintasan</string>
<string name="create_shortcut">Buat pintasan</string>
<string name="search">Cari</string>
<string name="search_manga">Cari manga</string>
<string name="manga_downloading_">Memuat turun…</string>
<string name="download_complete">Dimuat turun</string>
<string name="downloads">Muat turun</string>
<string name="by_name">Nama</string>
<string name="popular">Popular</string>
<string name="popular">Populer</string>
<string name="updated">Dikemaskini</string>
<string name="newest">Terbaharu</string>
<string name="by_rating">Rating</string>
<string name="by_rating">Peringkat</string>
<string name="sort_order">Turutan isihan</string>
<string name="filter">Tapis</string>
<string name="light">Terang</string>
@@ -258,7 +258,7 @@
<string name="chapters_will_removed_background">Bab akan dibuang dalam latar belakang</string>
<string name="operation_not_supported">Operasi ini tidak disokong</string>
<string name="delete_manga">Buang manga</string>
<string name="page_saved">Disimpan</string>
<string name="page_saved">Halaman disimpan</string>
<string name="domain">Domain</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d daripada %2$d pada</string>
<string name="read_mode">Mod pembacaan</string>
@@ -302,4 +302,140 @@
<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>
<string name="last_read">Bacaan terakhir</string>
<string name="automatic">Automatik</string>
<string name="retry">Mencoba kembali</string>
<string name="pages_saved">Halaman disimpan</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="text_empty_holder_secondary_filtered">Tidak ada manga yang cocok dengan filter yang Anda pilih</string>
<string name="chapters_grid_view">Tampilan kisi</string>
<string name="nsfw_16">16+</string>
<string name="download_slowdown">Unduhan melambat</string>
<string name="invalid_server_address_message">Alamat pelayan tidak sah</string>
<string name="not_found_404">Kandungan tidak ditemui atau dialih keluar</string>
<string name="import_completed_hint">Anda boleh memadamkan fail asal daripada storan untuk menjimatkan ruang</string>
<string name="reader_control_ltr_summary">Jangan laraskan arah penukaran halaman kepada mod pembaca, e. g. menekan kekunci kanan sentiasa beralih ke halaman seterusnya. Pilihan ini hanya mempengaruhi peranti input perkakasan</string>
<string name="reader_control_ltr">Kawalan pembaca ergonomik</string>
<string name="history_shortcuts_summary">Jadikan manga terbaru tersedia dengan menekan lama pada ikon aplikasi</string>
<string name="import_completed">Import selesai</string>
<string name="history_shortcuts">Tunjukkan pintasan komik terbaru</string>
<string name="incognito_mode">Mod inkognito</string>
<string name="no_chapters">Tiada bab</string>
<string name="automatic_scroll">Penatalan automatik</string>
<string name="reader_info_bar">Tunjukkan bar maklumat dalam pembaca</string>
<string name="comics_archive">Arkib komik</string>
<string name="folder_with_images">Folder dengan imej</string>
<string name="importing_manga">Mengimport komik</string>
<string name="import_will_start_soon">Import akan bermula tidak lama lagi</string>
<string name="feed">Suapan</string>
<string name="manga_error_description_pattern">Butiran ralat:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Cuba &lt;a href=%2$s&gt;buka manga dalam pelayar web&lt;/a&gt; untuk memastikan ia tersedia pada sumbernya&lt;br&gt;2. Pastikan anda menggunakan &lt;a href=kotatsu://about&gt;versi terbaharu Kotatsu&lt;/a&gt;&lt;br&gt;3. Jika tersedia, hantar laporan ralat kepada pembangun.</string>
<string name="color_correction">Pembetulan warna</string>
<string name="brightness">Kecerahan</string>
<string name="contrast">Berbeza</string>
<string name="reset">Tetapkan semula</string>
<string name="text_unsaved_changes_prompt">Simpan atau buang perubahan yang belum disimpan?</string>
<string name="discard">Buang</string>
<string name="error_no_space_left">Tiada ruang yang tinggal pada peranti</string>
<string name="reader_slider">Tunjukkan peluncur penukaran halaman</string>
<string name="reader_info_pattern">Bab %1$d/%2$d Hlm. %3$d/%4$d</string>
<string name="webtoon_zoom">Webtoon zum</string>
<string name="network_unavailable">Rangkaian tidak tersedia</string>
<string name="network_unavailable_hint">Hidupkan rangkaian Wi-Fi atau mudah alih untuk membaca manga dalam talian</string>
<string name="server_error">Ralat Side Server (%1$d). Sila cuba lagi kemudian</string>
<string name="clear_new_chapters_counters">Juga jelas maklumat mengenai bab baru</string>
<string name="compact">Padat</string>
<string name="source_disabled">Sumber dilumpuhkan</string>
<string name="prefetch_content">Kandungan Preloading</string>
<string name="mark_as_current">Tandakan sebagai semasa</string>
<string name="language">Bahasa</string>
<string name="share_logs">Kongsi log</string>
<string name="enable_logging">Dayakan pembalakan</string>
<string name="enable_logging_summary">Catat beberapa tindakan untuk tujuan debug. Jangan menghidupkannya jika anda tidak pasti apa yang anda lakukan</string>
<string name="show_suspicious_content">Tunjukkan kandungan yang mencurigakan</string>
<string name="theme_name_dynamic">Dinamik</string>
<string name="theme_name_expressive">Ekspresif (ujian)</string>
<string name="color_theme">Skema warna</string>
<string name="show_in_grid_view">Tunjukkan dalam paparan grid</string>
<string name="theme_name_miku">Miku</string>
<string name="nothing_here">Tidak ada apa -apa di sini</string>
<string name="scrobbling_empty_hint">Untuk Melacak Progres Membaca, Pilih Menu → LaCak Di Layar Butiran Manga.</string>
<string name="services">Perkhidmatan</string>
<string name="allow_unstable_updates">Benarkan kemas kini yang tidak stabil</string>
<string name="allow_unstable_updates_summary">Terima pemberitahuan mengenai binaan yang tidak stabil</string>
<string name="download_started">Muat turun bermula</string>
<string name="got_it">Mendapatnya</string>
<string name="sources_reorder_tip">Ketik dan tahan item untuk menyusun semula mereka</string>
<string name="user_agent">Pengepala useragent</string>
<string name="settings_apply_restart_required">Sila mulakan semula permohonan untuk menerapkan perubahan ini</string>
<string name="comics_archive_import_description">Anda boleh memilih satu atau lebih fail .cbz atau .zip, setiap fail akan diiktiraf sebagai manga yang berasingan.</string>
<string name="folder_with_images_import_description">Anda boleh memilih direktori dengan arkib atau imej. Setiap arkib (atau subdirektori) akan diiktiraf sebagai bab.</string>
<string name="speed">Kelajuan</string>
<string name="show_on_shelf">Tunjukkan di rak</string>
<string name="sync_auth_hint">Anda boleh masuk ke akaun yang ada atau membuat yang baru</string>
<string name="find_similar">Cari serupa</string>
<string name="sync_settings">Tetapan Penyegerakan</string>
<string name="server_address">Alamat pelayan</string>
<string name="sync_host_description">Anda boleh menggunakan pelayan penyegerakan sendiri atau lalai. Jangan ubah ini jika anda tidak pasti apa yang anda lakukan.</string>
<string name="ignore_ssl_errors">Abaikan kesilapan SSL</string>
<string name="mirror_switching">Pilih cermin secara automatik</string>
<string name="mirror_switching_summary">Tukar domain secara automatik untuk sumber manga atas kesilapan jika cermin tersedia</string>
<string name="pause">Jeda</string>
<string name="resume">Teruskan</string>
<string name="paused">Jeda</string>
<string name="remove_completed">Keluarkan selesai</string>
<string name="cancel_all">Batalkan semua</string>
<string name="downloads_wifi_only">Muat turun Hanya melalui Wi-Fi</string>
<string name="downloads_wifi_only_summary">Berhenti memuat turun semasa beralih ke rangkaian mudah alih</string>
<string name="suggestion_manga">Cadangan: %s</string>
<string name="suggestions_notifications_summary">Kadang -kadang menunjukkan pemberitahuan dengan manga yang dicadangkan</string>
<string name="more">Lebih</string>
<string name="enable">Membolehkan</string>
<string name="no_thanks">Tidak terima kasih</string>
<string name="cancel_all_downloads_confirm">Semua muat turun aktif akan dibatalkan, data yang dimuat turun sebahagiannya akan hilang</string>
<string name="remove_completed_downloads_confirm">Sejarah muat turun anda akan dipadamkan secara kekal. Tiada fail yang dimuat turun akan terjejas</string>
<string name="text_downloads_list_holder">Anda tidak mempunyai muat turun</string>
<string name="downloads_resumed">Muat turun telah diteruskan</string>
<string name="downloads_paused">Muat turun telah dijeda</string>
<string name="downloads_removed">Muat turun telah dikeluarkan</string>
<string name="downloads_cancelled">Muat turun telah dibatalkan</string>
<string name="suggestions_enable_prompt">Adakah anda ingin menerima cadangan manga yang diperibadikan?</string>
<string name="web_view_unavailable">WebView Tidak Tersedia: Periksa sama ada pembekal WebView dipasang</string>
<string name="clear_network_cache">Cache Rangkaian cache</string>
<string name="type">Jenis</string>
<string name="address">Alamat</string>
<string name="port">Pelabuhan</string>
<string name="proxy">Proksi</string>
<string name="invalid_value_message">Nilai tidak sah</string>
<string name="email_password_enter_hint">Masukkan e -mel dan kata laluan anda untuk diteruskan</string>
<string name="downloaded">Dimuat turun</string>
<string name="images_proxy_title">Proksi Pengoptimuman Imej</string>
<string name="images_procy_description">Gunakan perkhidmatan WSRV.NL untuk mengurangkan penggunaan lalu lintas dan mempercepat pemuatan imej jika boleh</string>
<string name="invert_colors">Warna terbalik</string>
<string name="username">Nama pengguna</string>
<string name="password">Kata laluan</string>
<string name="authorization_optional">Kebenaran (pilihan)</string>
<string name="invalid_port_number">Nombor port tidak sah</string>
<string name="network">Rangkaian</string>
<string name="data_and_privacy">Data dan privasi</string>
<string name="restore_summary">Pulihkan sandaran yang dibuat sebelum ini</string>
<string name="webtoon_zoom_summary">Benarkan isyarat zum masuk dalam mod webtoon</string>
<string name="pull_to_prev_chapter">Siaran untuk membuka bab sebelumnya</string>
<string name="pull_to_next_chapter">Siaran untuk membuka bab seterusnya</string>
<string name="pull_top_no_prev">Tiada bab sebelumnya</string>
<string name="theme_name_asuka">Asuka</string>
<string name="theme_name_mion">Mion</string>
<string name="theme_name_rikka">Rikka</string>
<string name="theme_name_sakura">Sakura</string>
<string name="theme_name_mamimi">Mamimi</string>
<string name="theme_name_kanade">Kanade</string>
<string name="pull_bottom_no_next">Tiada bab seterusnya</string>
<string name="reader_info_bar_summary">Tunjukkan masa sekarang dan kemajuan membaca di bahagian atas skrin</string>
<string name="show_pages_numbers_summary">Tunjukkan nombor halaman di sudut bawah</string>
<string name="clear_source_cookies_summary">Clear cookies untuk domain yang ditentukan sahaja. Dalam kebanyakan kes akan membatalkan kebenaran</string>
<string name="download_option_all_chapters">Semua bab dengan terjemahan %s</string>
<string name="download_option_whole_manga">Seluruh manga</string>
<string name="download_option_first_n_chapters">Pertama %s</string>
<string name="download_option_next_unread_n_chapters">Seterusnya belum dibaca %s</string>
<string name="download_option_all_unread">Semua bab yang belum dibaca</string>
<string name="download_option_all_unread_b">Semua bab yang belum dibaca (%s)</string>
</resources>

View File

@@ -832,4 +832,12 @@
<string name="hide_from_main_screen">Ukryj na ekranie głównym</string>
<string name="theme_name_itsuka">Itsuka</string>
<string name="theme_name_totoro">Totoro</string>
<string name="book_effect">Żółtawe tło (niebieski filtr)</string>
<string name="packup_creation_failed">Nie udało się stworzyć kopii zapasowej</string>
<string name="main_screen">Ekran główny</string>
<string name="main_screen_fab_summary">Pozwala wznowić czytanie jednym kliknięciem. Ten przycisk nie będzie pojawiał się w trybie inkognito, albo kiedy historia będzie pusta</string>
<string name="unavailable">Niedostępne</string>
<string name="no_chapters_in_manga">Ta manga nie zawiera żadnych rozdziałów</string>
<string name="chapters_load_failed">Nie udało się załadować listy rozdziałów</string>
<string name="telegram_integration">Integracja z Telegramem</string>
</resources>

View File

@@ -838,4 +838,22 @@
<string name="main_screen_fab">Mostrar botão Continuar flutuante</string>
<string name="main_screen_fab_summary">Permite continuar a leitura com um clique. Este botão não aparecerá no modo anônimo ou quando o histórico estiver vazio</string>
<string name="error_corrupted_zip">Arquivo ZIP corrompido (%s)</string>
<string name="discord_token_hint">Cole seu Token do Discord aqui</string>
<string name="discord_rpc_summary">Mostra seu status de leitura no Discord</string>
<string name="obtain">Obtenha</string>
<string name="discord_rpc_description">Lendo mangá no Kotatsu - um aplicativo de leitura de mangá</string>
<string name="reading_s">Lendo %s</string>
<string name="rpc_skip_nsfw_summary">Não usar RPC para conteúdo adulto</string>
<string name="invalid_token">Token inválido: %s</string>
<string name="show_floating_control_button">Mostrar botão de controle flutuante</string>
<string name="unavailable">Indisponível</string>
<string name="manga_restricted_description">Este mangá não está disponível para leitura nessa fonte. Tente pesquisar por ele em outras fontes ou abrir em um navegador para mais informação</string>
<string name="no_chapters_in_manga">Este mangá não contém nenhum capítulo</string>
<string name="chapters_load_failed">Falha ao carregar lista de capítulos</string>
<string name="telegram_integration">Integração com Telegram</string>
<string name="discord_token">Token do Discord</string>
<string name="discord_token_summary">Insira seu Token do Discord para habilitar a Rich Presence</string>
<string name="discord_rpc">Discord Rich Presence</string>
<string name="discord_token_description">Insira seu Token do Discord ou clique em %s para obtê-lo usando o navegador</string>
<string name="read_on_s">Leia em %s</string>
</resources>

View File

@@ -839,4 +839,23 @@
<string name="main_screen">Ecrã principal</string>
<string name="main_screen_fab">Mostrar botão flutuante \"Continuar\"</string>
<string name="discord_token">Código do Discord</string>
<string name="main_screen_fab_summary">Permite que continue lendo em um clique. Esse botão não irá aparecer no modo incógnito or quando o histórico estiver vazio</string>
<string name="error_corrupted_zip">Arquivo ZIP corrompido (%s)</string>
<string name="discord_rpc">Rich Presence do Discord</string>
<string name="discord_token_summary">Entre seu Token do Discord para habilitar Rich Presence</string>
<string name="discord_token_description">Entre seu Token do Discord ou clique %s para obtê-lo pelo navegador</string>
<string name="discord_token_hint">Cole seu Token do Discord aqui</string>
<string name="discord_rpc_summary">Mostrar seu estado de leitura no Discord</string>
<string name="obtain">Obter</string>
<string name="discord_rpc_description">Lendo manga no Kotatsu - um app leitor de manga</string>
<string name="reading_s">Lendo %s</string>
<string name="read_on_s">Lendo em %s</string>
<string name="rpc_skip_nsfw_summary">Não use RPC para conteúdo adulto</string>
<string name="invalid_token">Token inválido: %s</string>
<string name="show_floating_control_button">Mostrar botão de controle flutuante</string>
<string name="unavailable">Indisponível</string>
<string name="manga_restricted_description">Esse manga não está disponível para leitura nessa fonte. Tente pesquisá-lo em outras fontes ou abra-o no navegador para mais informação</string>
<string name="no_chapters_in_manga">Esse manga não contém capítulos</string>
<string name="chapters_load_failed">Falha ao carregar lista de capítulos</string>
<string name="telegram_integration">Integração Telegram</string>
</resources>

View File

@@ -54,7 +54,7 @@
<string name="add">கூட்டு</string>
<string name="save">சேமி</string>
<string name="share">பங்கு</string>
<string name="create_shortcut">குறுக்குவழியை உருவாக்கவும்</string>
<string name="create_shortcut">குறுக்குவழியை உருவாக்கவும்</string>
<string name="search">தேடல்</string>
<string name="search_manga">மங்காவைத் தேடுங்கள்</string>
<string name="manga_downloading_">பதிவிறக்கம்…</string>
@@ -792,4 +792,70 @@
<string name="link_to_manga_on_s">மங்காவிற்கான இணைப்பு இயக்கப்பட்டது %s</string>
<string name="search_everywhere">எல்லா இடங்களிலும் தேடுங்கள்</string>
<string name="error_disclaimer_report">நீங்கள் டெவலப்பர்களிடம் பிழை அறிக்கையைச் சமர்ப்பிக்கலாம். இது சிக்கலை ஆராய்ந்து சரிசெய்ய எங்களுக்கு உதவும்.</string>
<string name="nsfw_16">16+</string>
<string name="theme_name_expressive">வெளிப்படையான (சோதனை)</string>
<string name="reader_navigation_inverted">வழிசெலுத்தல் கட்டுப்பாடுகள் தலைகீழ்</string>
<string name="reader_navigation_inverted_summary">தொகுதி பொத்தான் மற்றும் திசை வன்பொருள் விசை வழிசெலுத்தலின் திசையை மாற்றவும் (இடது/மேல்/கீழ்/வலது)</string>
<string name="exclude_nsfw_from_suggestions_summary">அகவை வந்த மங்கா பரிந்துரைகளில் காட்டப்படாது. இந்த விருப்பம் சில ஆதாரங்களுடன் தவறானது</string>
<string name="include_disabled_sources">ஊனமுற்ற ஆதாரங்களைச் சேர்க்கவும்</string>
<string name="suggestions_disabled_sources_summary">ஊனமுற்றோர் உட்பட அனைத்து மங்கா மூலங்களிலிருந்தும் பரிந்துரைகளைக் காட்டுங்கள்</string>
<string name="tags_warnings">ஆபத்தான வகைகளை முன்னிலைப்படுத்தவும்</string>
<string name="tags_warnings_summary">பெரும்பாலான பயனர்களுக்கு பொருத்தமற்றதாக இருக்கும் வகைகளை முன்னிலைப்படுத்தவும்</string>
<string name="error_non_file_uri">தேர்ந்தெடுக்கப்பட்ட பாதையை பயன்படுத்த முடியாது, ஏனெனில் அது ஒரு கோப்பு அல்லது கோப்பகத்தைக் குறிக்கவில்லை</string>
<string name="manga_override_hint">இந்த மாற்றங்கள் பயன்பாட்டில் மங்கா எவ்வாறு காட்டப்படும் என்பதை பாதிக்கும்</string>
<string name="use_default_cover">இயல்புநிலை அட்டையைப் பயன்படுத்தவும்</string>
<string name="pick_manga_page">மங்கா பக்கத்தைத் தேர்ந்தெடுங்கள்</string>
<string name="pick_custom_file">தனிப்பயன் கோப்பைத் தேர்ந்தெடுக்கவும்</string>
<string name="change_cover">கவர் மாற்றவும்</string>
<string name="page_switch_timer">பக்கம் ஒவ்வொரு ~%d விநாடிகளிலும் மாறும்</string>
<string name="dont_ask_again">மீண்டும் கேட்க வேண்டாம்</string>
<string name="incognito_mode_hint_nsfw">இந்த மங்காவில் அகவை வந்தோர் உள்ளடக்கம் இருக்கலாம். நீங்கள் மறைநிலை பயன்முறையைப் பயன்படுத்த விரும்புகிறீர்களா?</string>
<string name="incognito_for_nsfw">NSFW மங்காவிற்கான மறைநிலை பயன்முறை</string>
<string name="additional_action_required">கூடுதல் நடவடிக்கை தேவை</string>
<string name="hide_from_main_screen">முதன்மையான திரையில் இருந்து மறைக்கவும்</string>
<string name="changelog">மாற்றபதிவு</string>
<string name="changelog_summary">அண்மைக் காலத்தில் வெளியிடப்பட்ட பதிப்புகளுக்கான வரலாற்றை மாற்றுகிறது</string>
<string name="collapse">சரிவு</string>
<string name="expand">விரிவாக்கு</string>
<string name="adblock">உலாவியில் விளம்பரங்களைத் தடுக்கவும்</string>
<string name="adblock_summary">உள்ளமைக்கப்பட்ட உலாவியில் (பீட்டா) தடுப்பு விளம்பரம்</string>
<string name="collapse_long_description">நீண்ட விளக்கத்தை சரிவு</string>
<string name="creating_backup">காப்புப்பிரதியை உருவாக்குதல்</string>
<string name="share_backup">காப்புப்பிரதியைப் பகிரவும்</string>
<string name="reader_multitask">ஒரு தனி பணியில் வாசகரைத் திறக்கவும்</string>
<string name="reader_multitask_summary">ஒரே நேரத்தில் வெவ்வேறு மங்காவுடன் பல வாசகர்களை திறந்து வைக்க உங்களை அனுமதிக்கிறது</string>
<string name="theme_name_itsuka">5 நாட்கள்</string>
<string name="theme_name_totoro">டுடோரோ</string>
<string name="book_effect">மஞ்சள் நிற பின்னணி (நீல வடிகட்டி)</string>
<string name="local_storage_cleanup">உள்ளக சேமிப்பக தூய்மைப்படுத்தல்</string>
<string name="packup_creation_failed">காப்புப்பிரதியை உருவாக்கத் தவறிவிட்டது</string>
<string name="main_screen">முதன்மையான திரை</string>
<string name="main_screen_fab">மிதக்கும் தொடர்ச்சியான பொத்தானைக் காட்டு</string>
<string name="main_screen_fab_summary">ஒரே கிளிக்கில் தொடர்ந்து படிக்க அனுமதிக்கிறது. இந்த பொத்தான் மறைநிலை பயன்முறையில் அல்லது வரலாறு காலியாக இருக்கும்போது தோன்றாது</string>
<string name="error_corrupted_zip">சிதைந்த சிப் காப்பகம் (%s)</string>
<string name="discord_rpc">முரண்பாடு பணக்கார இருப்பு</string>
<string name="discord_token">முரண்பாடு கிள்ளாக்கு</string>
<string name="discord_token_summary">பணக்கார இருப்பை செயல்படுத்த உங்கள் டிச்கார்ட் கிள்ளாக்கை உள்ளிடவும்</string>
<string name="discord_token_description">உங்கள் டிச்கார்ட் கிள்ளாக்கை உள்ளிடவும் அல்லது உலாவியைப் பயன்படுத்தி அதைப் பெற %s ஐக் சொடுக்கு செய்க</string>
<string name="discord_token_hint">உங்கள் டிச்கார்ட் கிள்ளாக்கை இங்கே ஒட்டவும்</string>
<string name="discord_rpc_summary">முரண்பாட்டில் உங்கள் வாசிப்பு நிலையைக் காட்டுங்கள்</string>
<string name="obtain">பெறுங்கள்</string>
<string name="discord_rpc_description">கோட்டாட்சுவில் மங்காவைப் படித்தல் - ஒரு மங்கா ரீடர் பயன்பாடு</string>
<string name="reading_s">படித்தல் %s</string>
<string name="read_on_s">%s படிக்கவும்</string>
<string name="rpc_skip_nsfw_summary">வயதுவந்த உள்ளடக்கத்திற்கு RPC ஐப் பயன்படுத்த வேண்டாம்</string>
<string name="invalid_token">தவறான டோக்கன்: %s</string>
<string name="show_floating_control_button">மிதக்கும் கட்டுப்பாட்டு பொத்தானைக் காட்டு</string>
<string name="unavailable">கிடைக்கவில்லை</string>
<string name="manga_restricted_description">இந்த மூலத்தில் படிக்க இந்த மங்கா கிடைக்கவில்லை. மற்ற மூலங்களில் இதைத் தேட முயற்சிக்கவும் அல்லது மேலும் தகவலுக்கு உலாவியில் திறக்கவும்</string>
<string name="no_chapters_in_manga">இந்த மங்காவில் எந்த அத்தியாயங்களும் இல்லை</string>
<string name="chapters_load_failed">அத்தியாய பட்டியலை ஏற்றுவதில் தோல்வி</string>
<string name="telegram_integration">தந்தி ஒருங்கிணைப்பு</string>
<string name="test_parser">சோதனை மங்கா மூல</string>
<string name="pull_to_prev_chapter">முந்தைய அத்தியாயத்தைத் திறக்க வெளியீடு</string>
<string name="pull_to_next_chapter">அடுத்த அத்தியாயத்தைத் திறக்க வெளியீடு</string>
<string name="pull_top_no_prev">முந்தைய அத்தியாயம் இல்லை</string>
<string name="pull_bottom_no_next">அடுத்த அத்தியாயம் இல்லை</string>
<string name="enable_pull_gesture_title">இழுக்கும் சைகையை இயக்கவும்</string>
<string name="enable_pull_gesture_summary">வெப்டூனில் அத்தியாயங்களை மாற்ற புல் சைகையைப் பயன்படுத்தவும்</string>
</resources>

View File

@@ -443,4 +443,6 @@
<string name="ask_every_time">ถามทุกครั้ง</string>
<string name="allow_always">อนุญาตเสมอ</string>
<string name="dont_allow">ไม่อนุญาต</string>
<string name="pages_saved">บันทึกแล้ว</string>
<string name="clear_thumbs_cache">ล้างแคชรูปย่อ</string>
</resources>

View File

@@ -859,4 +859,11 @@
<string name="no_chapters_in_manga">Bu manga bölüm içermiyor</string>
<string name="chapters_load_failed">Bölüm listesi yüklenemedi</string>
<string name="telegram_integration">Telegram entegrasyonu</string>
<string name="test_parser">Manga kaynağını test et</string>
<string name="pull_to_prev_chapter">Önceki bölümü açmak için bırakın</string>
<string name="pull_to_next_chapter">Sıradaki bölümü açmak için bırakın</string>
<string name="pull_top_no_prev">Önceki bölüm yok</string>
<string name="pull_bottom_no_next">Sonraki bölüm yok</string>
<string name="enable_pull_gesture_title">Çekme hareketini etkinleştir</string>
<string name="enable_pull_gesture_summary">Webtoon modunda bölüm değiştirmek için çekme hareketi kullan</string>
</resources>

View File

@@ -794,7 +794,7 @@
<string name="unnamed_chapter">Безіменний розділ</string>
<string name="search_disabled_sources">Пошук за відключеними джерелами</string>
<string name="error_details">Подробиці помилки</string>
<string name="book_effect">Жовтуватий фон (фільтр синього)</string>
<string name="book_effect">Жовтуватий фон (синій фільтр)</string>
<string name="theme_name_totoro">Тоторо</string>
<string name="theme_name_itsuka">Іцука</string>
<string name="reader_multitask_summary">Дозволяє тримати відкритими кілька читалок із різною манґою одночасно</string>
@@ -804,7 +804,7 @@
<string name="collapse_long_description">Згорнути довгий опис</string>
<string name="adblock_summary">Блокування реклами у вбудованому браузері (бета)</string>
<string name="adblock">Блокувати рекламу у браузері</string>
<string name="expand">Розширити</string>
<string name="expand">Розгорнути</string>
<string name="collapse">Згорнути</string>
<string name="changelog_summary">Історія змін для нещодавно випущених версій</string>
<string name="changelog">Журнал змін</string>
@@ -841,21 +841,29 @@
<string name="main_screen_fab">Показати плаваючу кнопку «Продовжити»</string>
<string name="main_screen_fab_summary">Дозволяє продовжити читання одним кліком. Ця кнопка не з\'являється в режимі інкогніто або коли історія порожня</string>
<string name="error_corrupted_zip">Пошкоджений архів ZIP (%s)</string>
<string name="discord_rpc">Discord Rich Presence</string>
<string name="discord_rpc">Багатий на присутність у Discord</string>
<string name="discord_token">Токен Discord</string>
<string name="discord_token_summary">Введіть свій токен Discord, щоб увімкнути Rich Presence</string>
<string name="discord_token_description">Введіть свій Discord токен або натисніть %s, щоб отримати його за допомогою браузера</string>
<string name="discord_token_hint">Вставте тут свій токен Discord</string>
<string name="discord_rpc_summary">Показувати свій статус читання в Discord</string>
<string name="discord_rpc_summary">Покажіть свій статус читання в Discord</string>
<string name="obtain">Отримати</string>
<string name="discord_rpc_description">Читання манги на Kotatsu - програма для читання манґи</string>
<string name="reading_s">Читання %s</string>
<string name="read_on_s">Читати на %s</string>
<string name="rpc_skip_nsfw_summary">Не використовувати RPC для контенту для дорослих</string>
<string name="rpc_skip_nsfw_summary">Не використовуйте RPC для контенту для дорослих</string>
<string name="invalid_token">Недійсний токен: %s</string>
<string name="show_floating_control_button">Показати плаваючу кнопку керування</string>
<string name="unavailable">Недоступно</string>
<string name="manga_restricted_description">Ця манга недоступна для читання в цьому джерелі. Спробуйте знайти її в інших джерелах або відкрити в браузері, щоб отримати додаткову інформацію</string>
<string name="no_chapters_in_manga">Ця манга не містить жодних розділів</string>
<string name="chapters_load_failed">Не вдалося завантажити список розділів</string>
<string name="telegram_integration">Інтеграція з Telegram</string>
<string name="test_parser">Джерело тестової манги</string>
<string name="pull_to_prev_chapter">Відпустіть, щоб відкрити попередній розділ</string>
<string name="pull_to_next_chapter">Відпустіть, щоб відкрити наступний розділ</string>
<string name="pull_top_no_prev">Немає попереднього розділу</string>
<string name="pull_bottom_no_next">Немає наступного розділу</string>
<string name="enable_pull_gesture_title">Увімкнути жест потягування</string>
<string name="enable_pull_gesture_summary">Використовуйте жест потягування для перемикання розділів у вебтуні</string>
</resources>

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