Compare commits

..

54 Commits
v7.0 ... v7.1

Author SHA1 Message Date
Koitharu
a7e2cfc878 Udpate parsers 2024-05-24 08:30:24 +03:00
Koitharu
da6db9c1b4 Refactor descrambling bitmap 2024-05-23 16:55:41 +03:00
AwkwardPeak7
88b3e5cf34 implement basic methods for descrambling images 2024-05-23 16:28:42 +03:00
Koitharu
7347f0ba10 Pagination in history and favorites 2024-05-23 12:44:10 +03:00
Koitharu
4c55682552 Move tracker debug activity to common code 2024-05-22 16:42:24 +03:00
Koitharu
324031aa2a Update untranslatable strings 2024-05-22 14:13:14 +03:00
Koitharu
1355c3d75c Option to disable nsfw updates notifications 2024-05-22 13:05:33 +03:00
Infy's Tagalog Translations
8533168155 Translated using Weblate (Filipino)
Currently translated at 99.8% (640 of 641 strings)

Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fil/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Asmodeus
51f6ec6e55 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (641 of 641 strings)

Co-authored-by: Asmodeus <colligare1Asmodeum@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Deivinni Silva
7e3f67c14d Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.1% (623 of 641 strings)

Co-authored-by: Deivinni Silva <deivinnimds3656@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
gallegonovato
c51320f033 Translated using Weblate (Spanish)
Currently translated at 100.0% (641 of 641 strings)

Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Hosted Weblate
9c50a47abc Update translation files
Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Scrambled777
473d273d18 Translated using Weblate (Hindi)
Currently translated at 100.0% (641 of 641 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (639 of 639 strings)

Co-authored-by: Scrambled777 <weblate.scrambled777@simplelogin.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
gekka
f19b628655 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (641 of 641 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (639 of 639 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Oğuz Ersen
fa74d4b27a Translated using Weblate (Turkish)
Currently translated at 100.0% (641 of 641 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (639 of 639 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Nicola Bortoletto
cdb6655e37 Translated using Weblate (Italian)
Currently translated at 93.4% (597 of 639 strings)

Co-authored-by: Nicola Bortoletto <nicola.bortoletto@live.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
maryush
4f19f7ebdf Translated using Weblate (Polish)
Currently translated at 100.0% (641 of 641 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (639 of 639 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: maryush <maryush@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pl/
Translation: Kotatsu/Strings
2024-05-22 13:05:06 +03:00
Koitharu
bf8838f943 Save and share manga cover #253 2024-05-22 12:33:23 +03:00
Koitharu
1e1e9fabdc Merge pull request #885 from ranzou06/devel 2024-05-20 18:51:21 +03:00
Koitharu
745972a717 Added 0ms.dev images proxy support #771 2024-05-20 17:03:18 +03:00
Koitharu
6055776329 Fix crashes 2024-05-20 11:31:00 +03:00
Koitharu
4074791f9a Resolve SSL excetpions 2024-05-20 11:18:38 +03:00
Koitharu
b1ab48e912 Option to disable connectivity check 2024-05-17 11:36:42 +03:00
Koitharu
a71e2dd289 Update settings ui and fix crash 2024-05-17 10:31:15 +03:00
Clebio
b8283acd0d feat: Implement Spen integration for enhanced stylus support
TY Alexander!
2024-05-16 22:15:10 -03:00
Koitharu
bbdf1c756e Update dependencies 2024-05-16 11:14:40 +03:00
Koitharu
283878879b Update parsers 2024-05-16 10:44:08 +03:00
Koitharu
b74ec98d68 Merge branch 'devel' of https://hosted.weblate.org/git/kotatsu/strings into devel 2024-05-16 10:13:07 +03:00
Koitharu
3691db8e8e App udpate activity #880 2024-05-15 18:20:58 +03:00
Paing Frow
e25ccf6b25 Added translation using Weblate (Burmese)
Co-authored-by: Paing Frow <paingphyoe66@gmail.com>
2024-05-14 14:48:40 +03:00
Infy's Tagalog Translations
ffebdb0c49 Translated using Weblate (Filipino)
Currently translated at 99.8% (637 of 638 strings)

Translated using Weblate (Filipino)

Currently translated at 99.8% (637 of 638 strings)

Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fil/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Scrambled777
6accdbced5 Translated using Weblate (Hindi)
Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Scrambled777 <weblate.scrambled777@simplelogin.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Oğuz Ersen
2fcb94e1d7 Translated using Weblate (Turkish)
Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Carlota-gif
6211ef974d Translated using Weblate (Portuguese)
Currently translated at 99.3% (634 of 638 strings)

Co-authored-by: Carlota-gif <gamefox1407@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Eduardo Malaspina
0eacf7bb98 Translated using Weblate (Spanish)
Currently translated at 99.8% (637 of 638 strings)

Co-authored-by: Eduardo Malaspina <vaio0@swismail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Anon
c9b7d650a8 Translated using Weblate (Serbian)
Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Anon <anonymousprivate76@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Anonymous
a29f7d6533 Translated using Weblate (Hungarian)
Currently translated at 96.7% (617 of 638 strings)

Translated using Weblate (Malay)

Currently translated at 48.4% (309 of 638 strings)

Translated using Weblate (Estonian)

Currently translated at 66.1% (422 of 638 strings)

Translated using Weblate (Kazakh)

Currently translated at 81.5% (520 of 638 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 59.2% (378 of 638 strings)

Translated using Weblate (Hindi)

Currently translated at 99.5% (635 of 638 strings)

Translated using Weblate (Korean)

Currently translated at 52.8% (337 of 638 strings)

Translated using Weblate (Greek)

Currently translated at 84.1% (537 of 638 strings)

Translated using Weblate (Arabic)

Currently translated at 52.6% (336 of 638 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (622 of 638 strings)

Translated using Weblate (Japanese)

Currently translated at 72.1% (460 of 638 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.7% (617 of 638 strings)

Translated using Weblate (Italian)

Currently translated at 84.6% (540 of 638 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/el/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/et/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hu/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/kk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/nn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Kristian de Frutos
72f8c626d7 Translated using Weblate (Czech)
Currently translated at 100.0% (636 of 636 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (636 of 636 strings)

Translated using Weblate (Czech)

Currently translated at 83.0% (528 of 636 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (9 of 9 strings)

Co-authored-by: Kristian de Frutos <kristiandef@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/cs/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2024-05-14 14:48:40 +03:00
gekka
f05ef5125d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (636 of 636 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Макар Разин
40b3d8e6fd Translated using Weblate (Belarusian)
Currently translated at 100.0% (636 of 636 strings)

Co-authored-by: Макар Разин <makarrazin14@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translation: Kotatsu/Strings
2024-05-14 14:48:40 +03:00
Paing Frow
a695bdc565 Translated using Weblate (Burmese)
Currently translated at 88.8% (8 of 9 strings)

Added translation using Weblate (Burmese)

Co-authored-by: Paing Frow <paingphyoe66@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/my/
Translation: Kotatsu/plurals
2024-05-14 11:48:01 +00:00
Infy's Tagalog Translations
9700fabd9a Translated using Weblate (Filipino)
Currently translated at 99.8% (637 of 638 strings)

Translated using Weblate (Filipino)

Currently translated at 99.8% (637 of 638 strings)

Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/fil/
Translation: Kotatsu/Strings
2024-05-14 11:47:59 +00:00
Scrambled777
4877db42f9 Translated using Weblate (Hindi)
Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Scrambled777 <weblate.scrambled777@simplelogin.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translation: Kotatsu/Strings
2024-05-14 11:47:58 +00:00
Oğuz Ersen
9b418fd63b Translated using Weblate (Turkish)
Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translation: Kotatsu/Strings
2024-05-14 11:47:57 +00:00
Carlota-gif
b2eef0df11 Translated using Weblate (Portuguese)
Currently translated at 99.3% (634 of 638 strings)

Co-authored-by: Carlota-gif <gamefox1407@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translation: Kotatsu/Strings
2024-05-14 11:47:56 +00:00
Eduardo Malaspina
34462829ff Translated using Weblate (Spanish)
Currently translated at 99.8% (637 of 638 strings)

Co-authored-by: Eduardo Malaspina <vaio0@swismail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
2024-05-14 11:47:55 +00:00
Anon
2afcbef8d0 Translated using Weblate (Serbian)
Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Serbian)

Currently translated at 100.0% (638 of 638 strings)

Co-authored-by: Anon <anonymousprivate76@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translation: Kotatsu/Strings
2024-05-14 11:47:53 +00:00
Anonymous
695becbda0 Translated using Weblate (Hungarian)
Currently translated at 96.7% (617 of 638 strings)

Translated using Weblate (Malay)

Currently translated at 48.4% (309 of 638 strings)

Translated using Weblate (Estonian)

Currently translated at 66.1% (422 of 638 strings)

Translated using Weblate (Kazakh)

Currently translated at 81.5% (520 of 638 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 59.2% (378 of 638 strings)

Translated using Weblate (Hindi)

Currently translated at 99.5% (635 of 638 strings)

Translated using Weblate (Korean)

Currently translated at 52.8% (337 of 638 strings)

Translated using Weblate (Greek)

Currently translated at 84.1% (537 of 638 strings)

Translated using Weblate (Arabic)

Currently translated at 52.6% (336 of 638 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 97.4% (622 of 638 strings)

Translated using Weblate (Japanese)

Currently translated at 72.1% (460 of 638 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.7% (617 of 638 strings)

Translated using Weblate (Italian)

Currently translated at 84.6% (540 of 638 strings)

Co-authored-by: Anonymous <noreply@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/el/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/et/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/hu/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/it/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/kk/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/nn/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2024-05-14 11:47:52 +00:00
Kristian de Frutos
5877d8215d Translated using Weblate (Czech)
Currently translated at 100.0% (636 of 636 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (636 of 636 strings)

Translated using Weblate (Czech)

Currently translated at 83.0% (528 of 636 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (9 of 9 strings)

Co-authored-by: Kristian de Frutos <kristiandef@hotmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/plurals/cs/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
Translation: Kotatsu/plurals
2024-05-14 11:47:50 +00:00
gekka
48b357dfef Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (638 of 638 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (636 of 636 strings)

Co-authored-by: gekka <1778962971@qq.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/zh_Hans/
Translation: Kotatsu/Strings
2024-05-14 11:47:49 +00:00
Макар Разин
b20cc7c0d9 Translated using Weblate (Belarusian)
Currently translated at 100.0% (636 of 636 strings)

Co-authored-by: Макар Разин <makarrazin14@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translation: Kotatsu/Strings
2024-05-14 11:47:48 +00:00
Koitharu
0f43f02fad Update parsers 2024-05-14 14:47:18 +03:00
Koitharu
9b658cf0b8 Fix offline details loading 2024-05-14 14:47:18 +03:00
MrChocolatine
ce705e12a8 Use format yyyyMMdd for backups
Resolves #791 .
2024-05-12 18:58:55 +03:00
97 changed files with 1948 additions and 760 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 34
versionCode = 641
versionName = '7.0'
versionCode = 643
versionName = '7.1'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {
@@ -82,7 +82,7 @@ afterEvaluate {
}
dependencies {
//noinspection GradleDependency
implementation('com.github.KotatsuApp:kotatsu-parsers:3e32a6280a') {
implementation('com.github.KotatsuApp:kotatsu-parsers:d218ad5a67') {
exclude group: 'org.json', module: 'json'
}
@@ -93,19 +93,19 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.activity:activity-ktx:1.9.0'
implementation 'androidx.fragment:fragment-ktx:1.7.0'
implementation 'androidx.fragment:fragment-ktx:1.7.1'
implementation 'androidx.collection:collection-ktx:1.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-service:2.7.0'
implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0'
implementation 'androidx.lifecycle:lifecycle-service:2.8.0'
implementation 'androidx.lifecycle:lifecycle-process:2.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.viewpager2:viewpager2:1.1.0-rc01'
implementation 'androidx.viewpager2:viewpager2:1.1.0'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha05'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.7.0'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.8.0'
implementation 'androidx.webkit:webkit:1.11.0'
implementation 'androidx.work:work-runtime:2.9.0'

View File

@@ -1,12 +0,0 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".tracker.ui.debug.TrackerDebugActivity"
android:label="@string/check_for_new_chapters" />
</application>
</manifest>

View File

@@ -100,6 +100,13 @@
<intent-filter>
<action android:name="${applicationId}.action.READ_MANGA" />
</intent-filter>
<intent-filter>
<action android:name="com.samsung.android.support.REMOTE_ACTION" />
</intent-filter>
<meta-data
android:name="com.samsung.android.support.REMOTE_ACTION"
android:resource="@xml/remote_action" />
</activity>
<activity
android:name="org.koitharu.kotatsu.search.ui.SearchActivity"
@@ -245,6 +252,12 @@
<activity
android:name="org.koitharu.kotatsu.alternatives.ui.AlternativesActivity"
android:label="@string/alternatives" />
<activity
android:name="org.koitharu.kotatsu.settings.about.AppUpdateActivity"
android:label="@string/app_update_available" />
<activity
android:name="org.koitharu.kotatsu.tracker.ui.debug.TrackerDebugActivity"
android:label="@string/tracker_debug_info" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
@@ -357,13 +370,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_recent" />
</receiver>
<receiver
android:name="org.koitharu.kotatsu.settings.about.UpdateDownloadReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
<receiver
android:name="org.koitharu.kotatsu.core.ErrorReporterReceiver"
android:exported="false">

View File

@@ -27,13 +27,14 @@ import okhttp3.OkHttpClient
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.network.ImageProxyInterceptor
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.os.NetworkState
import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.favicon.FaviconFetcher
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.image.CoilImageGetter
import org.koitharu.kotatsu.core.ui.util.ActivityRecreationHandle
import org.koitharu.kotatsu.core.util.AcraScreenLogger
@@ -70,8 +71,9 @@ interface AppModule {
@Provides
@Singleton
fun provideNetworkState(
@ApplicationContext context: Context
) = NetworkState(context.connectivityManager)
@ApplicationContext context: Context,
settings: AppSettings,
) = NetworkState(context.connectivityManager, settings)
@Provides
@Singleton

View File

@@ -39,7 +39,7 @@ suspend fun BackupZipOutput(context: Context): BackupZipOutput = runInterruptibl
val filename = buildString {
append(context.getString(R.string.app_name).replace(' ', '_').lowercase(Locale.ROOT))
append('_')
append(LocalDate.now().format(DateTimeFormatter.ofPattern("ddMMyyyy")))
append(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
append(".bk.zip")
}
BackupZipOutput(File(dir, filename))

View File

@@ -16,16 +16,16 @@ class MemoryContentCache @Inject constructor(application: Application) : Compone
private val isLowRam = application.isLowRamDevice()
init {
application.registerComponentCallbacks(this)
}
private val detailsCache = ExpiringLruCache<SafeDeferred<Manga>>(if (isLowRam) 1 else 4, 5, TimeUnit.MINUTES)
private val pagesCache =
ExpiringLruCache<SafeDeferred<List<MangaPage>>>(if (isLowRam) 1 else 4, 10, TimeUnit.MINUTES)
private val relatedMangaCache =
ExpiringLruCache<SafeDeferred<List<Manga>>>(if (isLowRam) 1 else 3, 10, TimeUnit.MINUTES)
init {
application.registerComponentCallbacks(this)
}
suspend fun getDetails(source: MangaSource, url: String): Manga? {
return detailsCache[Key(source, url)]?.awaitOrNull()
}

View File

@@ -1,36 +1,48 @@
package org.koitharu.kotatsu.core.exceptions.resolve
import android.content.Context
import android.widget.Toast
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
import androidx.collection.ArrayMap
import androidx.collection.MutableScatterMap
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.EntryPointAccessors
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.alternatives.ui.AlternativesActivity
import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseActivity.BaseActivityEntryPoint
import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog
import org.koitharu.kotatsu.core.util.TaggedActivityResult
import org.koitharu.kotatsu.core.util.ext.findActivity
import org.koitharu.kotatsu.parsers.exception.AuthRequiredException
import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
import java.security.cert.CertPathValidatorException
import javax.net.ssl.SSLException
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class ExceptionResolver : ActivityResultCallback<TaggedActivityResult> {
private val continuations = ArrayMap<String, Continuation<Boolean>>(1)
private val continuations = MutableScatterMap<String, Continuation<Boolean>>(1)
private val activity: FragmentActivity?
private val fragment: Fragment?
private val sourceAuthContract: ActivityResultLauncher<MangaSource>
private val cloudflareContract: ActivityResultLauncher<CloudFlareProtectedException>
val context: Context?
get() = activity ?: fragment?.context
constructor(activity: FragmentActivity) {
this.activity = activity
fragment = null
@@ -56,6 +68,12 @@ class ExceptionResolver : ActivityResultCallback<TaggedActivityResult> {
suspend fun resolve(e: Throwable): Boolean = when (e) {
is CloudFlareProtectedException -> resolveCF(e)
is AuthRequiredException -> resolveAuthException(e.source)
is SSLException,
is CertPathValidatorException -> {
showSslErrorDialog()
false
}
is NotFoundException -> {
openInBrowser(e.url)
false
@@ -80,13 +98,37 @@ class ExceptionResolver : ActivityResultCallback<TaggedActivityResult> {
}
private fun openInBrowser(url: String) {
val context = activity ?: fragment?.activity ?: return
context.startActivity(BrowserActivity.newIntent(context, url, null, null))
context?.run {
startActivity(BrowserActivity.newIntent(this, url, null, null))
}
}
private fun openAlternatives(manga: Manga) {
val context = activity ?: fragment?.activity ?: return
context.startActivity(AlternativesActivity.newIntent(context, manga))
context?.run {
startActivity(AlternativesActivity.newIntent(this, manga))
}
}
private fun showSslErrorDialog() {
val ctx = context ?: return
val settings = getAppSettings(ctx)
if (settings.isSSLBypassEnabled) {
Toast.makeText(ctx, R.string.operation_not_supported, Toast.LENGTH_SHORT).show()
return
}
MaterialAlertDialogBuilder(ctx)
.setTitle(R.string.ignore_ssl_errors)
.setMessage(R.string.ignore_ssl_errors_summary)
.setPositiveButton(R.string.apply) { _, _ ->
settings.isSSLBypassEnabled = true
Toast.makeText(ctx, R.string.settings_apply_restart_required, Toast.LENGTH_SHORT).show()
ctx.findActivity()?.finishAffinity()
}.setNegativeButton(android.R.string.cancel, null)
.show()
}
private fun getAppSettings(context: Context): AppSettings {
return EntryPointAccessors.fromApplication<BaseActivityEntryPoint>(context).settings
}
private fun getFragmentManager() = checkNotNull(fragment?.childFragmentManager ?: activity?.supportFragmentManager)
@@ -99,6 +141,9 @@ class ExceptionResolver : ActivityResultCallback<TaggedActivityResult> {
is AuthRequiredException -> R.string.sign_in
is NotFoundException -> if (e.url.isNotEmpty()) R.string.open_in_browser else 0
is UnsupportedSourceException -> if (e.manga != null) R.string.alternatives else 0
is SSLException,
is CertPathValidatorException -> R.string.fix
else -> 0
}

View File

@@ -13,9 +13,8 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.util.formatSimple
import org.koitharu.kotatsu.parsers.util.mapToSet
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import com.google.android.material.R as materialR
@JvmName("mangaIds")
@@ -119,17 +118,10 @@ val Manga.appUrl: Uri
.appendQueryParameter("url", url)
.build()
private val chaptersNumberFormat = DecimalFormat("#.#").also { f ->
f.decimalFormatSymbols = DecimalFormatSymbols.getInstance().also {
it.decimalSeparator = '.'
}
}
fun MangaChapter.formatNumber(): String? {
if (number <= 0f) {
return null
}
return chaptersNumberFormat.format(number.toDouble())
fun MangaChapter.formatNumber(): String? = if (number > 0f) {
number.formatSimple()
} else {
null
}
fun Manga.chaptersCount(): Int {

View File

@@ -83,6 +83,11 @@ class DoHManager(
tryGetByIp("2a10:50c0::2:ff"),
),
).build()
DoHProvider.ZERO_MS -> DnsOverHttps.Builder().client(bootstrapClient)
.url("https://0ms.dev/dns-query".toHttpUrl())
.resolvePublicAddresses(true)
.build()
}
private fun tryGetByIp(ip: String): InetAddress? = try {

View File

@@ -2,5 +2,5 @@ package org.koitharu.kotatsu.core.network
enum class DoHProvider {
NONE, GOOGLE, CLOUDFLARE, ADGUARD
}
NONE, GOOGLE, CLOUDFLARE, ADGUARD, ZERO_MS
}

View File

@@ -1,106 +0,0 @@
package org.koitharu.kotatsu.core.network
import android.util.Log
import androidx.collection.ArraySet
import coil.intercept.Interceptor
import coil.request.ErrorResult
import coil.request.ImageResult
import coil.request.SuccessResult
import coil.size.Dimension
import coil.size.isOriginal
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.isHttpOrHttps
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.util.Collections
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ImageProxyInterceptor @Inject constructor(
private val settings: AppSettings,
) : Interceptor {
private val blacklist = Collections.synchronizedSet(ArraySet<String>())
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
val request = chain.request
if (!settings.isImagesProxyEnabled) {
return chain.proceed(request)
}
val url: HttpUrl? = when (val data = request.data) {
is HttpUrl -> data
is String -> data.toHttpUrlOrNull()
else -> null
}
if (url == null || !url.isHttpOrHttps || url.host in blacklist) {
return chain.proceed(request)
}
val newUrl = HttpUrl.Builder()
.scheme("https")
.host("wsrv.nl")
.addQueryParameter("url", url.toString())
.addQueryParameter("we", null)
val size = request.sizeResolver.size()
if (!size.isOriginal) {
newUrl.addQueryParameter("crop", "cover")
(size.height as? Dimension.Pixels)?.let { newUrl.addQueryParameter("h", it.toString()) }
(size.width as? Dimension.Pixels)?.let { newUrl.addQueryParameter("w", it.toString()) }
}
val newRequest = request.newBuilder()
.data(newUrl.build())
.build()
val result = chain.proceed(newRequest)
return if (result is SuccessResult) {
result
} else {
logDebug((result as? ErrorResult)?.throwable)
chain.proceed(request).also {
if (it is SuccessResult) {
blacklist.add(url.host)
}
}
}
}
suspend fun interceptPageRequest(request: Request, okHttp: OkHttpClient): Response {
if (!settings.isImagesProxyEnabled) {
return okHttp.newCall(request).await()
}
val sourceUrl = request.url
val targetUrl = HttpUrl.Builder()
.scheme("https")
.host("wsrv.nl")
.addQueryParameter("url", sourceUrl.toString())
.addQueryParameter("we", null)
val newRequest = request.newBuilder()
.url(targetUrl.build())
.build()
return runCatchingCancellable {
okHttp.doCall(newRequest)
}.recover {
logDebug(it)
okHttp.doCall(request).also {
blacklist.add(sourceUrl.host)
}
}.getOrThrow()
}
private suspend fun OkHttpClient.doCall(request: Request): Response {
return newCall(request).await().ensureSuccess()
}
private fun logDebug(e: Throwable?) {
if (BuildConfig.DEBUG) {
Log.w("ImageProxy", e.toString())
}
}
}

View File

@@ -15,6 +15,8 @@ import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.network.cookies.AndroidCookieJar
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.network.cookies.PreferencesCookieJar
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.network.imageproxy.RealImageProxyInterceptor
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.assertNotInMainThread
import org.koitharu.kotatsu.local.data.LocalStorageManager
@@ -29,6 +31,9 @@ interface NetworkModule {
@Binds
fun bindCookieJar(androidCookieJar: MutableCookieJar): CookieJar
@Binds
fun bindImageProxyInterceptor(impl: RealImageProxyInterceptor): ImageProxyInterceptor
companion object {
@Provides

View File

@@ -0,0 +1,87 @@
package org.koitharu.kotatsu.core.network.imageproxy
import android.util.Log
import androidx.collection.ArraySet
import coil.intercept.Interceptor
import coil.network.HttpException
import coil.request.ErrorResult
import coil.request.ImageRequest
import coil.request.ImageResult
import coil.request.SuccessResult
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException
import org.koitharu.kotatsu.core.util.ext.ensureSuccess
import org.koitharu.kotatsu.core.util.ext.isHttpOrHttps
import org.koitharu.kotatsu.parsers.util.await
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.net.HttpURLConnection
import java.util.Collections
abstract class BaseImageProxyInterceptor : ImageProxyInterceptor {
private val blacklist = Collections.synchronizedSet(ArraySet<String>())
final override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
val request = chain.request
val url: HttpUrl? = when (val data = request.data) {
is HttpUrl -> data
is String -> data.toHttpUrlOrNull()
else -> null
}
if (url == null || !url.isHttpOrHttps || url.host in blacklist) {
return chain.proceed(request)
}
val newRequest = onInterceptImageRequest(request, url)
return when (val result = chain.proceed(newRequest)) {
is SuccessResult -> result
is ErrorResult -> {
logDebug(result.throwable, newRequest.data)
chain.proceed(request).also {
if (it is SuccessResult && result.throwable.isBlockedByServer()) {
blacklist.add(url.host)
}
}
}
}
}
final override suspend fun interceptPageRequest(request: Request, okHttp: OkHttpClient): Response {
val newRequest = onInterceptPageRequest(request)
return runCatchingCancellable {
okHttp.doCall(newRequest)
}.recover { error ->
logDebug(error, newRequest.url)
okHttp.doCall(request).also {
if (error.isBlockedByServer()) {
blacklist.add(request.url.host)
}
}
}.getOrThrow()
}
protected abstract suspend fun onInterceptImageRequest(request: ImageRequest, url: HttpUrl): ImageRequest
protected abstract suspend fun onInterceptPageRequest(request: Request): Request
private suspend fun OkHttpClient.doCall(request: Request): Response {
return newCall(request).await().ensureSuccess()
}
private fun logDebug(e: Throwable, url: Any) {
if (BuildConfig.DEBUG) {
Log.w("ImageProxy", "${e.message}: $url", e)
}
}
private fun Throwable.isBlockedByServer(): Boolean {
return this is CloudFlareBlockedException
|| (this is HttpException && response.code == HttpURLConnection.HTTP_FORBIDDEN)
|| (this is HttpStatusException && statusCode == HttpURLConnection.HTTP_FORBIDDEN)
}
}

View File

@@ -0,0 +1,11 @@
package org.koitharu.kotatsu.core.network.imageproxy
import coil.intercept.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
interface ImageProxyInterceptor : Interceptor {
suspend fun interceptPageRequest(request: Request, okHttp: OkHttpClient): Response
}

View File

@@ -0,0 +1,42 @@
package org.koitharu.kotatsu.core.network.imageproxy
import coil.intercept.Interceptor
import coil.request.ImageResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.plus
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
import org.koitharu.kotatsu.parsers.util.await
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class RealImageProxyInterceptor @Inject constructor(
private val settings: AppSettings,
) : ImageProxyInterceptor {
private val delegate = settings.observeAsStateFlow(
scope = processLifecycleScope + Dispatchers.Default,
key = AppSettings.KEY_IMAGES_PROXY,
valueProducer = { createDelegate() },
)
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
return delegate.value?.intercept(chain) ?: chain.proceed(chain.request)
}
override suspend fun interceptPageRequest(request: Request, okHttp: OkHttpClient): Response {
return delegate.value?.interceptPageRequest(request, okHttp) ?: okHttp.newCall(request).await()
}
private fun createDelegate(): ImageProxyInterceptor? = when (val proxy = settings.imagesProxy) {
-1 -> null
0 -> WsrvNlProxyInterceptor()
1 -> ZeroMsProxyInterceptor()
else -> error("Unsupported images proxy $proxy")
}
}

View File

@@ -0,0 +1,40 @@
package org.koitharu.kotatsu.core.network.imageproxy
import coil.request.ImageRequest
import coil.size.Dimension
import coil.size.isOriginal
import okhttp3.HttpUrl
import okhttp3.Request
class WsrvNlProxyInterceptor : BaseImageProxyInterceptor() {
override suspend fun onInterceptImageRequest(request: ImageRequest, url: HttpUrl): ImageRequest {
val newUrl = HttpUrl.Builder()
.scheme("https")
.host("wsrv.nl")
.addQueryParameter("url", url.toString())
.addQueryParameter("we", null)
val size = request.sizeResolver.size()
if (!size.isOriginal) {
newUrl.addQueryParameter("crop", "cover")
(size.height as? Dimension.Pixels)?.let { newUrl.addQueryParameter("h", it.toString()) }
(size.width as? Dimension.Pixels)?.let { newUrl.addQueryParameter("w", it.toString()) }
}
return request.newBuilder()
.data(newUrl.build())
.build()
}
override suspend fun onInterceptPageRequest(request: Request): Request {
val sourceUrl = request.url
val targetUrl = HttpUrl.Builder()
.scheme("https")
.host("wsrv.nl")
.addQueryParameter("url", sourceUrl.toString())
.addQueryParameter("we", null)
return request.newBuilder()
.url(targetUrl.build())
.build()
}
}

View File

@@ -0,0 +1,26 @@
package org.koitharu.kotatsu.core.network.imageproxy
import coil.request.ImageRequest
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
class ZeroMsProxyInterceptor : BaseImageProxyInterceptor() {
override suspend fun onInterceptImageRequest(request: ImageRequest, url: HttpUrl): ImageRequest {
if (url.host == "x.0ms.dev" || url.host == "0ms.dev") {
return request
}
val newUrl = ("https://x.0ms.dev/q70/$url").toHttpUrl()
return request.newBuilder()
.data(newUrl)
.build()
}
override suspend fun onInterceptPageRequest(request: Request): Request {
val newUrl = ("https://x.0ms.dev/q70/${request.url}").toHttpUrl()
return request.newBuilder()
.url(newUrl)
.build()
}
}

View File

@@ -5,13 +5,15 @@ import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import kotlinx.coroutines.flow.first
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.MediatorStateFlow
import org.koitharu.kotatsu.core.util.ext.isOnline
class NetworkState(
private val connectivityManager: ConnectivityManager,
) : MediatorStateFlow<Boolean>(connectivityManager.isOnline()) {
private val settings: AppSettings,
) : MediatorStateFlow<Boolean>(connectivityManager.isOnline(settings)) {
private val callback = NetworkCallbackImpl()
@@ -19,7 +21,10 @@ class NetworkState(
override fun onActive() {
invalidate()
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.build()
connectivityManager.registerNetworkCallback(request, callback)
}
@@ -37,7 +42,7 @@ class NetworkState(
}
private fun invalidate() {
publishValue(connectivityManager.isOnline())
publishValue(connectivityManager.isOnline(settings))
}
private inner class NetworkCallbackImpl : NetworkCallback() {
@@ -48,4 +53,27 @@ class NetworkState(
override fun onUnavailable() = invalidate()
}
private companion object {
fun ConnectivityManager.isOnline(settings: AppSettings): Boolean {
if (settings.isOfflineCheckDisabled) {
return true
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activeNetwork?.let { isOnline(it) } ?: false
} else {
@Suppress("DEPRECATION")
activeNetworkInfo?.isConnected == true
}
}
private fun ConnectivityManager.isOnline(network: Network): Boolean {
val capabilities = getNetworkCapabilities(network) ?: return false
return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
|| capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)
}
}
}

View File

@@ -0,0 +1,43 @@
package org.koitharu.kotatsu.core.parser
import android.graphics.Canvas
import org.koitharu.kotatsu.parsers.bitmap.Bitmap
import org.koitharu.kotatsu.parsers.bitmap.Rect
import java.io.OutputStream
import android.graphics.Bitmap as AndroidBitmap
import android.graphics.Rect as AndroidRect
class BitmapWrapper private constructor(
private val androidBitmap: AndroidBitmap,
) : Bitmap {
private val canvas by lazy { Canvas(androidBitmap) } // is not always used, so initialized lazily
override val height: Int
get() = androidBitmap.height
override val width: Int
get() = androidBitmap.width
override fun drawBitmap(sourceBitmap: Bitmap, src: Rect, dst: Rect) {
val androidSourceBitmap = (sourceBitmap as BitmapWrapper).androidBitmap
canvas.drawBitmap(androidSourceBitmap, src.toAndroidRect(), dst.toAndroidRect(), null)
}
fun compressTo(output: OutputStream) {
androidBitmap.compress(AndroidBitmap.CompressFormat.PNG, 100, output)
}
companion object {
fun create(width: Int, height: Int): Bitmap = BitmapWrapper(
AndroidBitmap.createBitmap(width, height, AndroidBitmap.Config.ARGB_8888),
)
fun create(bitmap: AndroidBitmap): Bitmap = BitmapWrapper(
if (bitmap.isMutable) bitmap else bitmap.copy(AndroidBitmap.Config.ARGB_8888, true),
)
private fun Rect.toAndroidRect() = AndroidRect(left, top, right, bottom)
}
}

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.parser
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.BitmapFactory
import android.util.Base64
import android.webkit.WebView
import androidx.annotation.MainThread
@@ -10,15 +11,21 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.ResponseBody.Companion.asResponseBody
import okio.Buffer
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
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.requireBody
import org.koitharu.kotatsu.core.util.ext.sanitizeHeaderValue
import org.koitharu.kotatsu.core.util.ext.toList
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.bitmap.Bitmap
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.network.UserAgents
@@ -68,6 +75,27 @@ class MangaLoaderContextImpl @Inject constructor(
return LocaleListCompat.getAdjustedDefault().toList()
}
override fun redrawImageResponse(response: Response, redraw: (image: Bitmap) -> Bitmap): Response {
val image = response.requireBody().byteStream()
val opts = BitmapFactory.Options()
opts.inMutable = true
val bitmap = BitmapFactory.decodeStream(image, null, opts) ?: error("Cannot decode bitmap")
val result = redraw(BitmapWrapper.create(bitmap)) as BitmapWrapper
val body = Buffer().also {
result.compressTo(it.outputStream())
}.asResponseBody("image/jpeg".toMediaType())
return response.newBuilder()
.body(body)
.build()
}
override fun createBitmap(width: Int, height: Int): Bitmap {
return BitmapWrapper.create(width, height)
}
@MainThread
private fun obtainWebView(): WebView {
return webViewCached?.get() ?: WebView(androidContext).also {

View File

@@ -26,6 +26,7 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.util.ext.requireBody
import org.koitharu.kotatsu.core.util.ext.writeAllCancellable
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.util.withExtraCloseable
@@ -150,10 +151,6 @@ class FaviconFetcher(
return if (networkResponse != null) DataSource.NETWORK else DataSource.DISK
}
private fun Response.requireBody(): ResponseBody {
return checkNotNull(body) { "response body == null" }
}
private fun Size.toCacheKey() = buildString {
append(width.toString())
append('x')

View File

@@ -136,6 +136,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getBoolean(KEY_TRAFFIC_WARNING, true)
set(value) = prefs.edit { putBoolean(KEY_TRAFFIC_WARNING, value) }
val isOfflineCheckDisabled: Boolean
get() = prefs.getBoolean(KEY_OFFLINE_DISABLED, false)
var isAllFavouritesVisible: Boolean
get() = prefs.getBoolean(KEY_ALL_FAVOURITES_VISIBLE, true)
set(value) = prefs.edit { putBoolean(KEY_ALL_FAVOURITES_VISIBLE, value) }
@@ -152,6 +155,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
val isTrackerNotificationsEnabled: Boolean
get() = prefs.getBoolean(KEY_TRACKER_NOTIFICATIONS, true)
val isTrackerNsfwDisabled: Boolean
get() = prefs.getBoolean(KEY_TRACKER_NO_NSFW, false)
var notificationSound: Uri
get() = prefs.getString(KEY_NOTIFICATIONS_SOUND, null)?.toUriOrNull()
?: Settings.System.DEFAULT_NOTIFICATION_URI
@@ -377,14 +383,18 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
}
}
val isImagesProxyEnabled: Boolean
get() = prefs.getBoolean(KEY_IMAGES_PROXY, false)
val imagesProxy: Int
get() {
val raw = prefs.getString(KEY_IMAGES_PROXY, null)?.toIntOrNull()
return raw ?: if (prefs.getBoolean(KEY_IMAGES_PROXY_OLD, false)) 0 else -1
}
val dnsOverHttps: DoHProvider
get() = prefs.getEnumValue(KEY_DOH, DoHProvider.NONE)
val isSSLBypassEnabled: Boolean
var isSSLBypassEnabled: Boolean
get() = prefs.getBoolean(KEY_SSL_BYPASS, false)
set(value) = prefs.edit { putBoolean(KEY_SSL_BYPASS, value) }
val proxyType: Proxy.Type
get() {
@@ -544,8 +554,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
companion object {
const val PAGE_SWITCH_VOLUME_KEYS = "volume"
const val TRACK_HISTORY = "history"
const val TRACK_FAVOURITES = "favourites"
@@ -557,6 +565,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_COLOR_THEME = "color_theme"
const val KEY_THEME_AMOLED = "amoled_theme"
const val KEY_TRAFFIC_WARNING = "traffic_warning"
const val KEY_OFFLINE_DISABLED = "no_offline"
const val KEY_PAGES_CACHE_CLEAR = "pages_cache_clear"
const val KEY_HTTP_CACHE_CLEAR = "http_cache_clear"
const val KEY_COOKIES_CLEAR = "cookies_clear"
@@ -581,6 +590,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_TRACK_CATEGORIES = "track_categories"
const val KEY_TRACK_WARNING = "track_warning"
const val KEY_TRACKER_NOTIFICATIONS = "tracker_notifications"
const val KEY_TRACKER_NO_NSFW = "tracker_no_nsfw"
const val KEY_NOTIFICATIONS_SETTINGS = "notifications_settings"
const val KEY_NOTIFICATIONS_SOUND = "notifications_sound"
const val KEY_NOTIFICATIONS_VIBRATE = "notifications_vibrate"
@@ -593,7 +603,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_APP_PASSWORD_NUMERIC = "app_password_num"
const val KEY_PROTECT_APP = "protect_app"
const val KEY_PROTECT_APP_BIOMETRIC = "protect_app_bio"
const val KEY_APP_VERSION = "app_version"
const val KEY_ZOOM_MODE = "zoom_mode"
const val KEY_BACKUP = "backup"
const val KEY_RESTORE = "restore"
@@ -643,7 +652,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_PREFETCH_CONTENT = "prefetch_content"
const val KEY_APP_LOCALE = "app_locale"
const val KEY_LOGGING_ENABLED = "logging"
const val KEY_LOGS_SHARE = "logs_share"
const val KEY_SOURCES_GRID = "sources_grid"
const val KEY_SOURCES_NEW = "sources_new"
const val KEY_UPDATES_UNSTABLE = "updates_unstable"
@@ -658,7 +666,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_PROXY_AUTH = "proxy_auth"
const val KEY_PROXY_LOGIN = "proxy_login"
const val KEY_PROXY_PASSWORD = "proxy_password"
const val KEY_IMAGES_PROXY = "images_proxy"
const val KEY_IMAGES_PROXY = "images_proxy_2"
const val KEY_LOCAL_MANGA_DIRS = "local_manga_dirs"
const val KEY_DISABLE_NSFW = "no_nsfw"
const val KEY_RELATED_MANGA = "related_manga"
@@ -672,7 +680,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_CF_CONTRAST = "cf_contrast"
const val KEY_CF_INVERTED = "cf_inverted"
const val KEY_CF_GRAYSCALE = "cf_grayscale"
const val KEY_IGNORE_DOZE = "ignore_dose"
const val KEY_PAGES_TAB = "pages_tab"
const val KEY_DETAILS_TAB = "details_tab"
const val KEY_DETAILS_LAST_TAB = "details_last_tab"
@@ -680,9 +687,18 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_PAGES_SAVE_DIR = "pages_dir"
const val KEY_PAGES_SAVE_ASK = "pages_dir_ask"
const val KEY_STATS_ENABLED = "stats_on"
const val KEY_APP_UPDATE = "app_update"
const val KEY_APP_TRANSLATION = "about_app_translation"
const val KEY_FEED_HEADER = "feed_header"
const val KEY_SEARCH_SUGGESTION_TYPES = "search_suggest_types"
// keys for non-persistent preferences
const val KEY_APP_VERSION = "app_version"
const val KEY_IGNORE_DOZE = "ignore_dose"
const val KEY_TRACKER_DEBUG = "tracker_debug"
const val KEY_LOGS_SHARE = "logs_share"
const val KEY_APP_UPDATE = "app_update"
const val KEY_APP_TRANSLATION = "about_app_translation"
// old keys are for migration only
private const val KEY_IMAGES_PROXY_OLD = "images_proxy"
}
}

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.core.prefs
import android.content.Context
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.core.content.edit
import okhttp3.internal.isSensitiveHeader
import org.koitharu.kotatsu.core.util.ext.getEnumValue
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty
import org.koitharu.kotatsu.core.util.ext.putEnumValue
@@ -12,6 +11,7 @@ import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.settings.utils.validation.DomainValidator
class SourceSettings(context: Context, source: MangaSource) : MangaSourceConfig {
@@ -31,7 +31,11 @@ class SourceSettings(context: Context, source: MangaSource) : MangaSourceConfig
.ifNullOrEmpty { key.defaultValue }
.sanitizeHeaderValue()
is ConfigKey.Domain -> prefs.getString(key.key, key.defaultValue).ifNullOrEmpty { key.defaultValue }
is ConfigKey.Domain -> prefs.getString(key.key, key.defaultValue)
?.trim()
?.takeIf { DomainValidator.isValidDomain(it) }
?: key.defaultValue
is ConfigKey.ShowSuspiciousContent -> prefs.getBoolean(key.key, key.defaultValue)
is ConfigKey.SplitByTranslations -> prefs.getBoolean(key.key, key.defaultValue)
} as T

View File

@@ -92,8 +92,7 @@ abstract class BaseActivity<B : ViewBinding> :
}
override fun onSupportNavigateUp(): Boolean {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
if (supportFragmentManager.popBackStackImmediate()) {
return false
}
dispatchNavigateUp()

View File

@@ -19,6 +19,7 @@ import android.content.pm.ResolveInfo
import android.database.SQLException
import android.graphics.Bitmap
import android.graphics.Color
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -75,6 +76,9 @@ val Context.activityManager: ActivityManager?
val Context.powerManager: PowerManager?
get() = getSystemService(POWER_SERVICE) as? PowerManager
val Context.connectivityManager: ConnectivityManager
get() = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
fun String.toUriOrNull() = if (isEmpty()) null else Uri.parse(this)
suspend fun CoroutineWorker.trySetForeground(): Boolean = runCatchingCancellable {

View File

@@ -1,13 +1,12 @@
package org.koitharu.kotatsu.core.util.ext
import okhttp3.Cookie
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.internal.closeQuietly
import okhttp3.internal.isSensitiveHeader
import okio.IOException
import org.json.JSONObject
import org.jsoup.HttpStatusException
@@ -42,6 +41,8 @@ fun Response.ensureSuccess() = apply {
}
}
fun Response.requireBody(): ResponseBody = checkNotNull(body) { "Response body is null" }
fun Cookie.newBuilder(): Cookie.Builder = Cookie.Builder().also { c ->
c.name(name)
c.value(value)

View File

@@ -1,24 +0,0 @@
package org.koitharu.kotatsu.core.util.ext
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.os.Build
val Context.connectivityManager: ConnectivityManager
get() = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
fun ConnectivityManager.isOnline(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activeNetwork?.let { isOnline(it) } ?: false
} else {
@Suppress("DEPRECATION")
activeNetworkInfo?.isConnected == true
}
}
private fun ConnectivityManager.isOnline(network: Network): Boolean {
val capabilities = getNetworkCapabilities(network)
return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}

View File

@@ -60,8 +60,7 @@ class DetailsLoadUseCase @Inject constructor(
} catch (e: IOException) {
local?.await()?.manga?.also { localManga ->
send(MangaDetails(localManga, null, localManga.description?.parseAsHtml(withImages = false), true))
}
throw e
} ?: close(e)
}
}

View File

@@ -667,7 +667,7 @@ class DetailsActivity :
companion object {
private const val FAV_LABEL_LIMIT = 10
private const val FAV_LABEL_LIMIT = 16
private const val AUTHOR_LABEL_LIMIT = 16
fun newIntent(context: Context, manga: Manga): Intent {

View File

@@ -19,8 +19,8 @@ import okhttp3.OkHttpClient
import okio.Path.Companion.toOkioPath
import okio.buffer
import okio.source
import org.koitharu.kotatsu.core.network.ImageProxyInterceptor
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.local.data.isFileUri

View File

@@ -13,7 +13,7 @@ fun downloadChapterAD() = adapterDelegateViewBinding<DownloadChapter, DownloadCh
val iconDone = ContextCompat.getDrawable(context, R.drawable.ic_check)
bind {
binding.textViewNumber.text = item.number.toString()
binding.textViewNumber.text = item.number
binding.textViewTitle.text = item.name
binding.textViewTitle.drawableEnd = if (item.isDownloaded) iconDone else null
}

View File

@@ -27,15 +27,20 @@ abstract class FavouritesDao {
@Query("SELECT * FROM favourites WHERE deleted_at = 0 GROUP BY manga_id ORDER BY created_at DESC LIMIT :limit")
abstract suspend fun findLast(limit: Int): List<FavouriteManga>
fun observeAll(order: ListSortOrder): Flow<List<FavouriteManga>> {
fun observeAll(order: ListSortOrder, limit: Int): Flow<List<FavouriteManga>> {
val orderBy = getOrderBy(order)
@Language("RoomSql")
val query = SimpleSQLiteQuery(
"SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
"WHERE favourites.deleted_at = 0 GROUP BY favourites.manga_id ORDER BY $orderBy",
)
return observeAllImpl(query)
val query = buildString {
append(
"SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
"WHERE favourites.deleted_at = 0 GROUP BY favourites.manga_id ORDER BY ",
)
append(orderBy)
if (limit > 0) {
append(" LIMIT ")
append(limit)
}
}
return observeAllImpl(SimpleSQLiteQuery(query))
}
@Transaction
@@ -52,16 +57,21 @@ abstract class FavouritesDao {
)
abstract suspend fun findAll(categoryId: Long): List<FavouriteManga>
fun observeAll(categoryId: Long, order: ListSortOrder): Flow<List<FavouriteManga>> {
fun observeAll(categoryId: Long, order: ListSortOrder, limit: Int): Flow<List<FavouriteManga>> {
val orderBy = getOrderBy(order)
val query = buildString {
append(
"SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
"WHERE category_id = ? AND deleted_at = 0 GROUP BY favourites.manga_id ORDER BY ",
)
append(orderBy)
if (limit > 0) {
append(" LIMIT ")
append(limit)
}
}
@Language("RoomSql")
val query = SimpleSQLiteQuery(
"SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
"WHERE category_id = ? AND deleted_at = 0 GROUP BY favourites.manga_id ORDER BY $orderBy",
arrayOf<Any>(categoryId),
)
return observeAllImpl(query)
return observeAllImpl(SimpleSQLiteQuery(query, arrayOf<Any>(categoryId)))
}
suspend fun findCovers(categoryId: Long, order: ListSortOrder): List<Cover> {

View File

@@ -38,8 +38,8 @@ class FavouritesRepository @Inject constructor(
return entities.toMangaList()
}
fun observeAll(order: ListSortOrder): Flow<List<Manga>> {
return db.getFavouritesDao().observeAll(order)
fun observeAll(order: ListSortOrder, limit: Int): Flow<List<Manga>> {
return db.getFavouritesDao().observeAll(order, limit)
.mapItems { it.toManga() }
}
@@ -48,14 +48,14 @@ class FavouritesRepository @Inject constructor(
return entities.toMangaList()
}
fun observeAll(categoryId: Long, order: ListSortOrder): Flow<List<Manga>> {
return db.getFavouritesDao().observeAll(categoryId, order)
fun observeAll(categoryId: Long, order: ListSortOrder, limit: Int): Flow<List<Manga>> {
return db.getFavouritesDao().observeAll(categoryId, order, limit)
.mapItems { it.toManga() }
}
fun observeAll(categoryId: Long): Flow<List<Manga>> {
fun observeAll(categoryId: Long, limit: Int): Flow<List<Manga>> {
return observeOrder(categoryId)
.flatMapLatest { order -> observeAll(categoryId, order) }
.flatMapLatest { order -> observeAll(categoryId, order, limit) }
}
fun observeMangaCount(): Flow<Int> {
@@ -63,12 +63,6 @@ class FavouritesRepository @Inject constructor(
.distinctUntilChanged()
}
suspend fun getCategories(): List<FavouriteCategory> {
return db.getFavouriteCategoriesDao().findAll().map {
it.toFavouriteCategory()
}
}
fun observeCategories(): Flow<List<FavouriteCategory>> {
return db.getFavouriteCategoriesDao().observeAll().mapItems {
it.toFavouriteCategory()

View File

@@ -33,7 +33,7 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis
binding.recyclerView.isVP2BugWorkaroundEnabled = true
}
override fun onScrolledToEnd() = Unit
override fun onScrolledToEnd() = viewModel.requestMoreItems()
override fun onFilterClick(view: View?) {
val menu = PopupMenu(view?.context ?: return, view)

View File

@@ -32,8 +32,11 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.parsers.model.Manga
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
private const val PAGE_SIZE = 20
@HiltViewModel
class FavouritesListViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
@@ -46,6 +49,8 @@ class FavouritesListViewModel @Inject constructor(
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
private val refreshTrigger = MutableStateFlow(Any())
private val limit = MutableStateFlow(PAGE_SIZE)
private val isReady = AtomicBoolean(false)
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode)
@@ -61,13 +66,7 @@ class FavouritesListViewModel @Inject constructor(
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
override val content = combine(
if (categoryId == NO_ID) {
sortOrder.filterNotNull().flatMapLatest {
repository.observeAll(it)
}
} else {
repository.observeAll(categoryId)
},
observeFavorites(),
listMode,
refreshTrigger,
) { list, mode, _ ->
@@ -85,7 +84,10 @@ class FavouritesListViewModel @Inject constructor(
),
)
else -> list.toUi(mode, listExtraProvider)
else -> {
isReady.set(true)
list.toUi(mode, listExtraProvider)
}
}
}.catch {
emit(listOf(it.toErrorState(canRetry = false)))
@@ -126,4 +128,19 @@ class FavouritesListViewModel @Inject constructor(
repository.setCategoryOrder(categoryId, order)
}
}
fun requestMoreItems() {
if (isReady.compareAndSet(true, false)) {
limit.value += PAGE_SIZE
}
}
private fun observeFavorites() = if (categoryId == NO_ID) {
combine(sortOrder.filterNotNull(), limit, ::Pair)
.flatMapLatest { repository.observeAll(it.first, it.second) }
} else {
limit.flatMapLatest {
repository.observeAll(categoryId, it)
}
}
}

View File

@@ -9,7 +9,6 @@ import androidx.room.Transaction
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import kotlinx.coroutines.flow.Flow
import org.intellij.lang.annotations.Language
import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.list.domain.ListSortOrder
@@ -28,8 +27,7 @@ abstract class HistoryDao {
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit")
abstract fun observeAll(limit: Int): Flow<List<HistoryWithManga>>
// TODO pagination
fun observeAll(order: ListSortOrder): Flow<List<HistoryWithManga>> {
fun observeAll(order: ListSortOrder, limit: Int): Flow<List<HistoryWithManga>> {
val orderBy = when (order) {
ListSortOrder.LAST_READ -> "history.updated_at DESC"
ListSortOrder.LONG_AGO_READ -> "history.updated_at ASC"
@@ -43,13 +41,18 @@ abstract class HistoryDao {
ListSortOrder.UPDATED -> "IFNULL((SELECT last_chapter_date FROM tracks WHERE tracks.manga_id = manga.manga_id), 0) DESC"
else -> throw IllegalArgumentException("Sort order $order is not supported")
}
@Language("RoomSql")
val query = SimpleSQLiteQuery(
"SELECT * FROM history LEFT JOIN manga ON history.manga_id = manga.manga_id " +
"WHERE history.deleted_at = 0 GROUP BY history.manga_id ORDER BY $orderBy",
)
return observeAllImpl(query)
val query = buildString {
append(
"SELECT * FROM history LEFT JOIN manga ON history.manga_id = manga.manga_id " +
"WHERE history.deleted_at = 0 GROUP BY history.manga_id ORDER BY ",
)
append(orderBy)
if (limit > 0) {
append(" LIMIT ")
append(limit)
}
}
return observeAllImpl(SimpleSQLiteQuery(query))
}
@Query("SELECT manga_id FROM history WHERE deleted_at = 0")

View File

@@ -74,8 +74,8 @@ class HistoryRepository @Inject constructor(
}
}
fun observeAllWithHistory(order: ListSortOrder): Flow<List<MangaWithHistory>> {
return db.getHistoryDao().observeAll(order).mapItems {
fun observeAllWithHistory(order: ListSortOrder, limit: Int): Flow<List<MangaWithHistory>> {
return db.getHistoryDao().observeAll(order, limit).mapItems {
MangaWithHistory(
it.manga.toManga(it.tags.toMangaTags()),
it.history.toMangaHistory(),

View File

@@ -32,7 +32,7 @@ class HistoryListFragment : MangaListFragment() {
viewModel.isStatsEnabled.observe(viewLifecycleOwner, MenuInvalidator(requireActivity()))
}
override fun onScrolledToEnd() = Unit
override fun onScrolledToEnd() = viewModel.requestMoreItems()
override fun onEmptyActionClick() {
startActivity(NetworkManageIntent())

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.history.ui
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
@@ -43,8 +44,11 @@ import org.koitharu.kotatsu.list.ui.model.toListModel
import org.koitharu.kotatsu.local.data.LocalMangaRepository
import org.koitharu.kotatsu.parsers.model.Manga
import java.time.Instant
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
private const val PAGE_SIZE = 20
@HiltViewModel
class HistoryListViewModel @Inject constructor(
private val repository: HistoryRepository,
@@ -62,8 +66,11 @@ class HistoryListViewModel @Inject constructor(
valueProducer = { historySortOrder },
)
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_HISTORY) { historyListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.historyListMode)
override val listMode = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_LIST_MODE_HISTORY,
valueProducer = { historyListMode },
)
private val isGroupingEnabled = settings.observeAsFlow(
key = AppSettings.KEY_HISTORY_GROUPING,
@@ -72,6 +79,9 @@ class HistoryListViewModel @Inject constructor(
g && s.isGroupingSupported()
}
private val limit = MutableStateFlow(PAGE_SIZE)
private val isReady = AtomicBoolean(false)
val isStatsEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_STATS_ENABLED,
@@ -79,7 +89,7 @@ class HistoryListViewModel @Inject constructor(
)
override val content = combine(
sortOrder.flatMapLatest { repository.observeAllWithHistory(it) },
observeHistory(),
isGroupingEnabled,
listMode,
networkState,
@@ -95,7 +105,10 @@ class HistoryListViewModel @Inject constructor(
),
)
else -> mapList(list, grouped, mode, online, incognito)
else -> {
isReady.set(true)
mapList(list, grouped, mode, online, incognito)
}
}
}.onStart {
loadingCounter.increment()
@@ -138,6 +151,15 @@ class HistoryListViewModel @Inject constructor(
}
}
fun requestMoreItems() {
if (isReady.compareAndSet(true, false)) {
limit.value += PAGE_SIZE
}
}
private fun observeHistory() = combine(sortOrder, limit, ::Pair)
.flatMapLatest { repository.observeAllWithHistory(it.first, it.second) }
private suspend fun mapList(
list: List<MangaWithHistory>,
grouped: Boolean,

View File

@@ -7,11 +7,12 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.activity.viewModels
import androidx.core.graphics.Insets
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isVisible
import androidx.core.view.marginBottom
import androidx.core.view.updateLayoutParams
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import coil.ImageLoader
import coil.request.CachePolicy
import coil.request.ErrorResult
@@ -20,17 +21,26 @@ import coil.request.SuccessResult
import coil.target.ViewTarget
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.util.PopupMenuMediator
import org.koitharu.kotatsu.core.util.ShareHelper
import org.koitharu.kotatsu.core.util.ext.enqueueWith
import org.koitharu.kotatsu.core.util.ext.getDisplayIcon
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.source
import org.koitharu.kotatsu.databinding.ActivityImageBinding
import org.koitharu.kotatsu.databinding.ItemErrorStateBinding
import org.koitharu.kotatsu.parsers.model.MangaSource
import javax.inject.Inject
import com.google.android.material.R as materialR
@AndroidEntryPoint
class ImageActivity : BaseActivity<ActivityImageBinding>(), ImageRequest.Listener, View.OnClickListener {
@@ -39,27 +49,45 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(), ImageRequest.Listene
lateinit var coil: ImageLoader
private var errorBinding: ItemErrorStateBinding? = null
private val viewModel: ImageViewModel by viewModels()
private lateinit var menuMediator: PopupMenuMediator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityImageBinding.inflate(layoutInflater))
viewBinding.buttonBack.setOnClickListener(this)
loadImage(intent.data)
viewBinding.buttonMenu.setOnClickListener(this)
val imageUrl = requireNotNull(intent.data)
val menuProvider = ImageMenuProvider(
activity = this,
snackbarHost = viewBinding.root,
viewModel = viewModel,
)
menuMediator = PopupMenuMediator(menuProvider)
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.root, null))
viewModel.onImageSaved.observeEvent(this, ::onImageSaved)
loadImage(imageUrl)
}
override fun onWindowInsetsChanged(insets: Insets) {
with(viewBinding.buttonBack) {
updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top + marginBottom
leftMargin = insets.left + marginBottom
rightMargin = insets.right + marginBottom
}
viewBinding.buttonBack.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top + bottomMargin
leftMargin = insets.left + bottomMargin
rightMargin = insets.right + bottomMargin
}
viewBinding.buttonMenu.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top + bottomMargin
leftMargin = insets.left + bottomMargin
rightMargin = insets.right + bottomMargin
}
}
override fun onClick(v: View) {
when (v.id) {
R.id.button_back -> dispatchNavigateUp()
R.id.button_menu -> menuMediator.onLongClick(v)
else -> loadImage(intent.data)
}
}
@@ -92,11 +120,34 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(), ImageRequest.Listene
.memoryCachePolicy(CachePolicy.DISABLED)
.lifecycle(this)
.listener(this)
.tag(intent.getSerializableExtraCompat<MangaSource>(EXTRA_SOURCE))
.source(intent.getSerializableExtraCompat<MangaSource>(EXTRA_SOURCE))
.target(SsivTarget(viewBinding.ssiv))
.enqueueWith(coil)
}
private fun onImageSaved(uri: Uri) {
Snackbar.make(viewBinding.root, R.string.page_saved, Snackbar.LENGTH_LONG)
.setAction(R.string.share) {
ShareHelper(this).shareImage(uri)
}.show()
}
private fun onLoadingStateChanged(isLoading: Boolean) {
val button = viewBinding.buttonMenu
button.isClickable = !isLoading
if (isLoading) {
button.setImageDrawable(
CircularProgressDrawable(this).also {
it.setStyle(CircularProgressDrawable.LARGE)
it.setColorSchemeColors(getThemeColor(com.google.android.material.R.attr.colorControlNormal))
it.start()
},
)
} else {
button.setImageResource(materialR.drawable.abc_ic_menu_overflow_material)
}
}
private class SsivTarget(
override val view: SubsamplingScaleImageView,
) : ViewTarget<SubsamplingScaleImageView> {
@@ -124,7 +175,7 @@ class ImageActivity : BaseActivity<ActivityImageBinding>(), ImageRequest.Listene
companion object {
private const val EXTRA_SOURCE = "source"
const val EXTRA_SOURCE = "source"
fun newIntent(context: Context, url: String, source: MangaSource?): Intent {
return Intent(context, ImageActivity::class.java)

View File

@@ -0,0 +1,68 @@
package org.koitharu.kotatsu.image.ui
import android.Manifest
import android.os.Build
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.MenuProvider
import com.google.android.material.snackbar.Snackbar
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.tryLaunch
import org.koitharu.kotatsu.local.data.isZipUri
class ImageMenuProvider(
private val activity: ComponentActivity,
private val snackbarHost: View,
private val viewModel: ImageViewModel,
) : MenuProvider {
private val permissionLauncher = activity.registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted ->
if (isGranted) {
saveImage()
}
}
private val saveLauncher = activity.registerForActivityResult(
ActivityResultContracts.CreateDocument("image/png"),
) { uri ->
if (uri != null) {
viewModel.saveImage(uri)
}
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_image, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_save -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
saveImage()
}
true
}
else -> false
}
private fun saveImage() {
val name = activity.intent.data?.let {
if (it.isZipUri()) {
it.fragment
} else {
it.lastPathSegment
}?.substringBeforeLast('.')?.plus(".png")
}
if (name == null || !saveLauncher.tryLaunch(name)) {
Snackbar.make(snackbarHost, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show()
}
}
}

View File

@@ -0,0 +1,50 @@
package org.koitharu.kotatsu.image.ui
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.SavedStateHandle
import coil.ImageLoader
import coil.request.CachePolicy
import coil.request.ImageRequest
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow
import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.core.util.ext.source
import javax.inject.Inject
@HiltViewModel
class ImageViewModel @Inject constructor(
@ApplicationContext private val context: Context,
private val savedStateHandle: SavedStateHandle,
private val coil: ImageLoader,
) : BaseViewModel() {
val onImageSaved = MutableEventFlow<Uri>()
fun saveImage(destination: Uri) {
launchLoadingJob(Dispatchers.Default) {
val request = ImageRequest.Builder(context)
.memoryCachePolicy(CachePolicy.READ_ONLY)
.data(savedStateHandle.require<Uri>(BaseActivity.EXTRA_DATA))
.memoryCachePolicy(CachePolicy.DISABLED)
.source(savedStateHandle[ImageActivity.EXTRA_SOURCE])
.build()
val bitmap = coil.execute(request).getDrawableOrThrow().toBitmap()
runInterruptible(Dispatchers.IO) {
context.contentResolver.openOutputStream(destination)?.use { output ->
check(bitmap.compress(Bitmap.CompressFormat.PNG, 100, output))
} ?: error("Cannot open output stream")
}
onImageSaved.call(destination)
}
}
}

View File

@@ -0,0 +1,32 @@
package org.koitharu.kotatsu.local.data
import androidx.collection.MutableLongObjectMap
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
import org.koitharu.kotatsu.local.domain.model.LocalManga
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import java.io.File
class LocalMangaMappingCache {
private val map = MutableLongObjectMap<File>()
suspend fun get(mangaId: Long): LocalManga? {
val file = synchronized(this) {
map[mangaId]
} ?: return null
return runCatchingCancellable {
LocalMangaInput.of(file).getManga()
}.onFailure {
it.printStackTraceDebug()
}.getOrNull()
}
operator fun set(mangaId: Long, localManga: LocalManga?) = synchronized(this) {
if (localManga == null) {
map.remove(mangaId)
} else {
map[mangaId] = localManga.file
}
}
}

View File

@@ -51,6 +51,7 @@ class LocalMangaRepository @Inject constructor(
override val source = MangaSource.LOCAL
private val locks = MultiMutex<Long>()
private val localMappingCache = LocalMangaMappingCache()
override val isMultipleTagsSupported: Boolean = true
override val isTagsExclusionSupported: Boolean = true
@@ -141,6 +142,10 @@ class LocalMangaRepository @Inject constructor(
}
suspend fun findSavedManga(remoteManga: Manga): LocalManga? = runCatchingCancellable {
// very fast path
localMappingCache.get(remoteManga.id)?.let {
return@runCatchingCancellable it
}
// fast path
LocalMangaInput.find(storageManager.getReadableDirs(), remoteManga)?.let {
return it.getManga()
@@ -162,6 +167,8 @@ class LocalMangaRepository @Inject constructor(
}
}
}.firstOrNull()?.getManga()
}.onSuccess { x: LocalManga? ->
localMappingCache[remoteManga.id] = x
}.onFailure {
it.printStackTraceDebug()
}.getOrNull()

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.main.ui
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import android.os.Bundle
@@ -66,7 +67,7 @@ import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.settings.about.AppUpdateDialog
import org.koitharu.kotatsu.settings.about.AppUpdateActivity
import javax.inject.Inject
import com.google.android.material.R as materialR
@@ -84,7 +85,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
private val viewModel by viewModels<MainViewModel>()
private val searchSuggestionViewModel by viewModels<SearchSuggestionViewModel>()
private val closeSearchCallback = CloseSearchCallback()
private val appUpdateDialog = AppUpdateDialog(this)
private lateinit var navigationDelegate: MainNavigationDelegate
private lateinit var appUpdateBadge: OptionsMenuBadgeHelper
@@ -190,9 +190,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
}
R.id.action_app_update -> {
viewModel.appUpdate.value?.also {
appUpdateDialog.show(it)
} != null
startActivity(Intent(this, AppUpdateActivity::class.java))
true
}
else -> super.onOptionsItemSelected(item)

View File

@@ -7,8 +7,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import okhttp3.OkHttpClient
import org.koitharu.kotatsu.core.model.findChapter
import org.koitharu.kotatsu.core.network.ImageProxyInterceptor
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.parser.MangaDataRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings

View File

@@ -27,8 +27,8 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okio.use
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.network.ImageProxyInterceptor
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings

View File

@@ -36,6 +36,17 @@ class ReaderControlDelegate(
}
fun onKeyDown(keyCode: Int): Boolean = when (keyCode) {
KeyEvent.KEYCODE_R -> {
listener.switchPageBy(1)
true
}
KeyEvent.KEYCODE_L -> {
listener.switchPageBy(-1)
true
}
KeyEvent.KEYCODE_VOLUME_UP -> if (settings.isReaderVolumeButtonsEnabled) {
listener.switchPageBy(-1)
true

View File

@@ -11,7 +11,6 @@ import androidx.core.graphics.Insets
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import androidx.preference.Preference
@@ -21,15 +20,12 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat
import org.koitharu.kotatsu.core.util.ext.isScrolledToTop
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
import org.koitharu.kotatsu.settings.about.AppUpdateDialog
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment
@@ -40,19 +36,18 @@ import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment
class SettingsActivity :
BaseActivity<ActivitySettingsBinding>(),
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
AppBarOwner,
FragmentManager.OnBackStackChangedListener {
val appUpdateDialog = AppUpdateDialog(this)
AppBarOwner {
override val appBar: AppBarLayout
get() = viewBinding.appbar
private val isMasterDetails
get() = viewBinding.containerMaster != null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivitySettingsBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val isMasterDetails = viewBinding.containerMaster != null
val fm = supportFragmentManager
val currentFragment = fm.findFragmentById(R.id.container)
if (currentFragment == null || (isMasterDetails && currentFragment is RootSettingsFragment)) {
@@ -66,21 +61,6 @@ class SettingsActivity :
}
}
override fun onTitleChanged(title: CharSequence?, color: Int) {
super.onTitleChanged(title, color)
viewBinding.collapsingToolbarLayout?.title = title
}
override fun onStart() {
super.onStart()
supportFragmentManager.addOnBackStackChangedListener(this)
}
override fun onStop() {
supportFragmentManager.removeOnBackStackChangedListener(this)
super.onStop()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.opt_settings, menu)
@@ -113,14 +93,6 @@ class SettingsActivity :
else -> super.onOptionsItemSelected(item)
}
override fun onBackStackChanged() {
val fragment = supportFragmentManager.findFragmentById(R.id.container) as? RecyclerViewOwner ?: return
val recyclerView = fragment.recyclerView
recyclerView.post {
viewBinding.appbar.setExpanded(recyclerView.isScrolledToTop, false)
}
}
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference,
@@ -150,19 +122,17 @@ class SettingsActivity :
fun openFragment(fragment: Fragment, isFromRoot: Boolean) {
val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null
val isMasterDetail = viewBinding.containerMaster != null
supportFragmentManager.commit {
setReorderingAllowed(true)
replace(R.id.container, fragment)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN)
if (!isMasterDetail || (hasFragment && !isFromRoot)) {
if (!isMasterDetails || (hasFragment && !isFromRoot)) {
addToBackStack(null)
}
}
}
private fun openDefaultFragment() {
val hasMaster = viewBinding.containerMaster != null
val fragment = when (intent?.action) {
ACTION_READER -> ReaderSettingsFragment()
ACTION_SUGGESTIONS -> SuggestionsSettingsFragment()
@@ -184,7 +154,7 @@ class SettingsActivity :
}
else -> null
} ?: if (hasMaster) AppearanceSettingsFragment() else RootSettingsFragment()
} ?: if (isMasterDetails) AppearanceSettingsFragment() else RootSettingsFragment()
supportFragmentManager.commit {
setReorderingAllowed(true)
replace(R.id.container, fragment)

View File

@@ -20,7 +20,7 @@ import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.util.ShareHelper
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.tracker.ui.debug.TrackerDebugActivity
import javax.inject.Inject
@AndroidEntryPoint
@@ -41,6 +41,12 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
isEnabled = VersionId(BuildConfig.VERSION_NAME).isStable
if (!isEnabled) isChecked = true
}
if (!settings.isTrackerEnabled) {
findPreference<Preference>(AppSettings.KEY_TRACKER_DEBUG)?.run {
isEnabled = false
setSummary(R.string.check_for_new_chapters_disabled)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -68,6 +74,12 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
true
}
AppSettings.KEY_TRACKER_DEBUG -> {
startActivity(Intent(preference.context, TrackerDebugActivity::class.java))
true
}
else -> super.onPreferenceTreeClick(preference)
}
}
@@ -77,7 +89,7 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
Snackbar.make(listView, R.string.no_update_available, Snackbar.LENGTH_SHORT).show()
return
}
(activity as SettingsActivity).appUpdateDialog.show(version)
startActivity(Intent(requireContext(), AppUpdateActivity::class.java))
}
private fun openLink(url: String, title: CharSequence?) {

View File

@@ -0,0 +1,180 @@
package org.koitharu.kotatsu.settings.about
import android.Manifest
import android.app.DownloadManager
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets
import androidx.core.net.toUri
import androidx.core.text.buildSpannedString
import dagger.hilt.android.AndroidEntryPoint
import io.noties.markwon.Markwon
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
import org.koitharu.kotatsu.core.util.ext.showOrHide
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ActivityAppUpdateBinding
@AndroidEntryPoint
class AppUpdateActivity : BaseActivity<ActivityAppUpdateBinding>(), View.OnClickListener {
private val viewModel: AppUpdateViewModel by viewModels()
private lateinit var downloadReceiver: UpdateDownloadReceiver
private val permissionRequest = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) {
if (it) {
viewModel.startDownload()
} else {
openInBrowser()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityAppUpdateBinding.inflate(layoutInflater))
downloadReceiver = UpdateDownloadReceiver(viewModel)
viewModel.nextVersion.observe(this, ::onNextVersionChanged)
viewBinding.buttonCancel.setOnClickListener(this)
viewBinding.buttonUpdate.setOnClickListener(this)
ContextCompat.registerReceiver(
this,
downloadReceiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
ContextCompat.RECEIVER_EXPORTED,
)
combine(viewModel.isLoading, viewModel.downloadProgress, ::Pair)
.observe(this, ::onProgressChanged)
viewModel.downloadState.observe(this, ::onDownloadStateChanged)
viewModel.onError.observeEvent(this, ::onError)
viewModel.onDownloadDone.observeEvent(this) { intent ->
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
e.printStackTraceDebug()
}
}
}
override fun onDestroy() {
unregisterReceiver(downloadReceiver)
super.onDestroy()
}
override fun onClick(v: View) {
when (v.id) {
R.id.button_cancel -> finishAfterTransition()
R.id.button_update -> doUpdate()
}
}
override fun onWindowInsetsChanged(insets: Insets) {
val basePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding)
viewBinding.root.setPadding(
basePadding + insets.left,
basePadding + insets.top,
basePadding + insets.right,
basePadding + insets.bottom,
)
}
private suspend fun onNextVersionChanged(version: AppVersion?) {
viewBinding.buttonUpdate.isEnabled = version != null && !viewModel.isLoading.value
if (version == null) {
viewBinding.textViewContent.setText(R.string.loading_)
return
}
val message = withContext(Dispatchers.Default) {
buildSpannedString {
append(getString(R.string.new_version_s, version.name))
appendLine()
append(getString(R.string.size_s, FileSize.BYTES.format(this@AppUpdateActivity, version.apkSize)))
appendLine()
appendLine()
append(Markwon.create(this@AppUpdateActivity).toMarkdown(version.description))
}
}
viewBinding.textViewContent.setText(message, TextView.BufferType.SPANNABLE)
}
private fun doUpdate() {
viewModel.installIntent.value?.let { intent ->
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
onError(e)
}
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
permissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
viewModel.startDownload()
}
}
private fun openInBrowser() {
val latestVersion = viewModel.nextVersion.value ?: return
val intent = Intent(Intent.ACTION_VIEW, latestVersion.url.toUri())
startActivity(Intent.createChooser(intent, getString(R.string.open_in_browser)))
}
private fun onProgressChanged(value: Pair<Boolean, Float>) {
val (isLoading, downloadProgress) = value
val indicator = viewBinding.progressBar
indicator.showOrHide(isLoading)
indicator.isIndeterminate = downloadProgress <= 0f
if (downloadProgress > 0f) {
indicator.setProgressCompat((indicator.max * downloadProgress).toInt(), true)
}
viewBinding.buttonUpdate.isEnabled = !isLoading && viewModel.nextVersion.value != null
}
private fun onDownloadStateChanged(state: Int) {
val message = when (state) {
DownloadManager.STATUS_FAILED -> R.string.error_occurred
DownloadManager.STATUS_PAUSED -> R.string.downloads_paused
else -> 0
}
viewBinding.textViewError.setTextAndVisible(message)
}
private fun onError(e: Throwable) {
viewBinding.textViewError.textAndVisible = e.getDisplayMessage(resources)
}
private class UpdateDownloadReceiver(
private val viewModel: AppUpdateViewModel,
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
viewModel.onDownloadComplete(intent)
}
}
}
}
}

View File

@@ -1,89 +0,0 @@
package org.koitharu.kotatsu.settings.about
import android.Manifest
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.core.text.buildSpannedString
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.noties.markwon.Markwon
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ext.DIALOG_THEME_CENTERED
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
class AppUpdateDialog(private val activity: AppCompatActivity) {
private lateinit var latestVersion: AppVersion
private val permissionRequest = activity.registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) {
if (it) {
downloadUpdateImpl()
} else {
openInBrowser()
}
}
fun show(version: AppVersion) {
latestVersion = version
val message = buildSpannedString {
append(activity.getString(R.string.new_version_s, version.name))
appendLine()
append(activity.getString(R.string.size_s, FileSize.BYTES.format(activity, version.apkSize)))
appendLine()
appendLine()
append(Markwon.create(activity).toMarkdown(version.description))
}
MaterialAlertDialogBuilder(activity, DIALOG_THEME_CENTERED)
.setTitle(R.string.app_update_available)
.setMessage(message)
.setIcon(R.drawable.ic_app_update)
.setNeutralButton(R.string.open_in_browser) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW, version.url.toUri())
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.open_in_browser)))
}.setPositiveButton(R.string.update) { _, _ ->
downloadUpdate()
}.setNegativeButton(android.R.string.cancel, null)
.setCancelable(false)
.create()
.show()
}
private fun downloadUpdate() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
permissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
downloadUpdateImpl()
}
}
private fun downloadUpdateImpl() = runCatching {
val version = latestVersion
val url = version.apkUrl.toUri()
val dm = activity.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val request = DownloadManager.Request(url)
.setTitle("${activity.getString(R.string.app_name)} v${version.name}")
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.lastPathSegment)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setMimeType("application/vnd.android.package-archive")
dm.enqueue(request)
}.onSuccess {
Toast.makeText(activity, R.string.download_started, Toast.LENGTH_SHORT).show()
}.onFailure { e ->
Toast.makeText(activity, e.getDisplayMessage(activity.resources), Toast.LENGTH_SHORT).show()
}
private fun openInBrowser() {
val intent = Intent(Intent.ACTION_VIEW, latestVersion.url.toUri())
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.open_in_browser)))
}
}

View File

@@ -0,0 +1,102 @@
package org.koitharu.kotatsu.settings.about
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.os.Environment
import androidx.core.net.toUri
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.isActive
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppUpdateRepository
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.requireValue
import javax.inject.Inject
import kotlin.coroutines.coroutineContext
@HiltViewModel
class AppUpdateViewModel @Inject constructor(
private val repository: AppUpdateRepository,
@ApplicationContext context: Context,
) : BaseViewModel() {
val nextVersion = repository.observeAvailableUpdate()
val downloadProgress = MutableStateFlow(-1f)
val downloadState = MutableStateFlow(DownloadManager.STATUS_PENDING)
val installIntent = MutableStateFlow<Intent?>(null)
val onDownloadDone = MutableEventFlow<Intent>()
private val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
private val appName = context.getString(R.string.app_name)
init {
if (nextVersion.value == null) {
launchLoadingJob(Dispatchers.Default) {
repository.fetchUpdate()
}
}
}
fun startDownload() {
launchLoadingJob(Dispatchers.Default) {
val version = nextVersion.requireValue()
val url = version.apkUrl.toUri()
val request = DownloadManager.Request(url)
.setTitle("$appName v${version.name}")
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.lastPathSegment)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setMimeType("application/vnd.android.package-archive")
val downloadId = downloadManager.enqueue(request)
observeDownload(downloadId)
}
}
fun onDownloadComplete(intent: Intent) {
launchLoadingJob(Dispatchers.Default) {
val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L)
if (downloadId == 0L) {
return@launchLoadingJob
}
@Suppress("DEPRECATION")
val installerIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
installerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
installerIntent.setDataAndType(
downloadManager.getUriForDownloadedFile(downloadId),
downloadManager.getMimeTypeForDownloadedFile(downloadId),
)
installerIntent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
installIntent.value = installerIntent
onDownloadDone.call(installerIntent)
}
}
private suspend fun observeDownload(id: Long) {
val query = DownloadManager.Query()
query.setFilterById(id)
while (coroutineContext.isActive) {
downloadManager.query(query).use { cursor ->
if (cursor.moveToFirst()) {
val bytesDownloaded = cursor.getLong(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR),
)
val bytesTotal = cursor.getLong(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES),
)
downloadProgress.value = bytesDownloaded.toFloat() / bytesTotal
val state = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
downloadState.value = state
if (state == DownloadManager.STATUS_SUCCESSFUL || state == DownloadManager.STATUS_FAILED) {
return
}
}
}
delay(100)
}
}
}

View File

@@ -1,38 +0,0 @@
package org.koitharu.kotatsu.settings.about
import android.app.DownloadManager
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
class UpdateDownloadReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L)
if (downloadId == 0L) {
return
}
val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
@Suppress("DEPRECATION")
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
installIntent.setDataAndType(
dm.getUriForDownloadedFile(downloadId),
dm.getMimeTypeForDownloadedFile(downloadId),
)
installIntent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
try {
context.startActivity(installIntent)
} catch (e: ActivityNotFoundException) {
e.printStackTraceDebug()
}
}
}
}
}

View File

@@ -11,20 +11,24 @@ class DomainValidator : EditTextValidator() {
if (trimmed.isEmpty()) {
return ValidationResult.Success
}
return if (!checkCharacters(trimmed)) {
return if (!isValidDomain(trimmed)) {
ValidationResult.Failed(context.getString(R.string.invalid_domain_message))
} else {
ValidationResult.Success
}
}
private fun checkCharacters(value: String): Boolean = runCatching {
val parts = value.split(':')
require(parts.size <= 2)
val urlBuilder = HttpUrl.Builder()
urlBuilder.host(parts.first())
if (parts.size == 2) {
urlBuilder.port(parts[1].toInt())
}
}.isSuccess
companion object {
fun isValidDomain(value: String): Boolean = runCatching {
require(value.isNotEmpty())
val parts = value.split(':')
require(parts.size <= 2)
val urlBuilder = HttpUrl.Builder()
urlBuilder.host(parts.first())
if (parts.size == 2) {
urlBuilder.port(parts[1].toInt())
}
}.isSuccess
}
}

View File

@@ -32,6 +32,7 @@ class TrackerDebugActivity : BaseActivity<ActivityTrackerDebugBinding>(), OnList
val tracksAdapter = BaseListAdapter<TrackDebugItem>()
.addDelegate(ListItemType.FEED, trackDebugAD(this, coil, this))
with(viewBinding.recyclerView) {
setHasFixedSize(true)
adapter = tracksAdapter
addItemDecoration(TypedListSpacingDecoration(context, false))
}

View File

@@ -49,6 +49,9 @@ class TrackerNotificationHelper @Inject constructor(
if (newChapters.isEmpty() || !applicationContext.checkNotificationPermission(CHANNEL_ID)) {
return null
}
if (manga.isNsfw && settings.isTrackerNsfwDisabled) {
return null
}
val id = manga.url.hashCode()
val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
val summary = applicationContext.resources.getQuantityString(

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/screen_padding">
<TextView
android:id="@+id/textView_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawablePadding="16dp"
android:gravity="center_horizontal"
android:text="@string/app_update_available"
android:textAppearance="?textAppearanceHeadline5"
app:drawableTint="?colorPrimary"
app:drawableTopCompat="@drawable/ic_app_update"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/screen_padding"
android:max="100"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_title"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textColor="?colorError"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/progressBar"
tools:text="@string/error_corrupted_file"
tools:visibility="visible" />
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginVertical="@dimen/screen_padding"
app:layout_constraintBottom_toTopOf="@id/barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_error">
<TextView
android:id="@+id/textView_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?textAppearanceBodyMedium"
tools:text="@tools:sample/lorem/random" />
</ScrollView>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_cancel"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_update"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/update"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="button_cancel,button_update" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -24,6 +24,18 @@
android:scaleType="center"
app:srcCompat="?homeAsUpIndicator" />
<ImageButton
android:id="@+id/button_menu"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end"
android:layout_margin="@dimen/screen_padding"
android:background="@drawable/bg_circle_button"
android:contentDescription="@string/back"
android:elevation="@dimen/m3_sys_elevation_level1"
android:scaleType="center"
app:srcCompat="@drawable/abc_ic_menu_overflow_material" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar"
android:layout_width="wrap_content"

View File

@@ -11,21 +11,11 @@
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
style="?collapsingToolbarLayoutMediumStyle"
<com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="?collapsingToolbarLayoutMediumSize"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:toolbarId="@id/toolbar">
<com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin" />
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -14,6 +14,7 @@
android:layout_height="40dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_save"
android:title="@string/save" />
</menu>

View File

@@ -334,4 +334,4 @@
<string name="import_completed_hint">يمكنك حذف الملف الأصلي من التخزين لتوفير مساحة</string>
<string name="import_will_start_soon">الإستيراد سيبدأ عن قريب</string>
<string name="history_shortcuts">إظهار اختصارات المانجا الحديثة</string>
</resources>
</resources>

View File

@@ -530,8 +530,8 @@
<string name="rating_adult">Дарослы</string>
<string name="default_tab">Ўкладка па змаўчанні</string>
<string name="state_upcoming">Чакаецца</string>
<string name="volume_">Гучнасць %d</string>
<string name="volume_unknown">Невядомая гучнасць</string>
<string name="volume_">Том %d</string>
<string name="volume_unknown">Невядомы том</string>
<string name="incognito_mode_hint">Ваш прагрэс чытання не будзе захаваны</string>
<string name="vertical">Вертыкальны</string>
<string name="category_hidden_done">Гэтая катэгорыя была схаваная з галоўнага экрана і даступная праз Меню → Кіраванне катэгорыямі</string>

View File

@@ -3,31 +3,46 @@
<plurals name="items">
<item quantity="one">%1$d předmět</item>
<item quantity="few">%1$d předměty</item>
<item quantity="other">%1$d předměty</item>
<item quantity="other">%1$d předmětů</item>
</plurals>
<plurals name="new_chapters">
<item quantity="one">%1$d nová kapitola</item>
<item quantity="few">%1$d nové kapitoly</item>
<item quantity="other">%1$d nové kapitoly</item>
<item quantity="other">%1$d nových kapitol</item>
</plurals>
<plurals name="chapters">
<item quantity="one">%1$d kapitola</item>
<item quantity="few">%1$d kapitoly</item>
<item quantity="other">%1$d kapitoly</item>
<item quantity="other">%1$d kapitol</item>
</plurals>
<plurals name="minutes_ago">
<item quantity="one">%1$d před minutou</item>
<item quantity="few">%1$d před pár minutami</item>
<item quantity="other">%1$d před několika minutami</item>
<item quantity="one">před %1$d minutou</item>
<item quantity="few">před %1$d minutami</item>
<item quantity="other">před %1$d minutami</item>
</plurals>
<plurals name="hours_ago">
<item quantity="one">%1$d před hodinou</item>
<item quantity="few">%1$d před pár hodinamy</item>
<item quantity="other">%1$d před několika hodinamy</item>
<item quantity="one">před %1$d hodinou</item>
<item quantity="few">před %1$d hodinami</item>
<item quantity="other">před %1$d hodinami</item>
</plurals>
<plurals name="days_ago">
<item quantity="one">%1$d před dnem</item>
<item quantity="few">%1$d před pár dny</item>
<item quantity="other">%1$d před několika dny</item>
<item quantity="one">před %1$d dnem</item>
<item quantity="few">před %1$d dny</item>
<item quantity="other">před %1$d dny</item>
</plurals>
<plurals name="months_ago">
<item quantity="one">před %1$d měsícem</item>
<item quantity="few">před %1$d měsíci</item>
<item quantity="other">před %1$d měsíci</item>
</plurals>
<plurals name="hours">
<item quantity="one">%1$d hodina</item>
<item quantity="few">%1$d hodiny</item>
<item quantity="other">%1$d hodin</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%1$d minuta</item>
<item quantity="few">%1$d minuty</item>
<item quantity="other">%1$d minut</item>
</plurals>
</resources>

View File

@@ -39,7 +39,7 @@
<string name="share_image">Sdílet obrázek</string>
<string name="_import">Importovat</string>
<string name="delete">Smazat</string>
<string name="text_file_not_supported">Vyberte buď soubor ZIP či CBZ.</string>
<string name="text_file_not_supported">Vyberte soubor ZIP či CBZ.</string>
<string name="clear_pages_cache">Vyčistit mezipaměť stran</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="standard">Standardní</string>
@@ -47,7 +47,7 @@
<string name="read_mode">Režim čtení</string>
<string name="grid_size">Velikost mřížky</string>
<string name="delete_manga">Smazat mangu</string>
<string name="text_delete_local_manga">Smazat \"%s\" permanentně z tohoto zařízení\?</string>
<string name="text_delete_local_manga">Odstranit trvale \"%s\" z tohoto zařízení?</string>
<string name="reader_settings">Nastavení čtečky</string>
<string name="switch_pages">Prohodit strany</string>
<string name="_continue">Pokračovat</string>
@@ -61,9 +61,9 @@
<string name="text_empty_holder_primary">Je tu nějak prázdno…</string>
<string name="text_search_holder_secondary">Zkuste přeformulovat dotaz.</string>
<string name="text_history_holder_primary">To co čtete se zobrazí zde</string>
<string name="text_history_holder_secondary">Co si přečíst, najdete v sekci «Prozkoumat»</string>
<string name="text_history_holder_secondary">Najděte co číst v sekci « Prozkoumat»</string>
<string name="text_local_holder_primary">Nejdříve něco uložte</string>
<string name="text_local_holder_secondary">Uložte to z online zdrojů nebo importujte soubor.</string>
<string name="text_local_holder_secondary">Uložte něco z online zdrojů nebo importujte soubor.</string>
<string name="recent_manga">Nedávné</string>
<string name="manga_shelf">Polička</string>
<string name="pages_animation">Animace stránek</string>
@@ -167,7 +167,7 @@
<string name="removal_completed">Odstraňování dokončeno</string>
<string name="download_slowdown">Zpomalení stahování</string>
<string name="local_manga_processing">Zpracovávání uložených mang</string>
<string name="chapters_will_removed_background">Kapitoly budou na pozadí odstraněny</string>
<string name="chapters_will_removed_background">Kapitoly budou odstraněny na pozadí</string>
<string name="account_already_exists">Účet již existuje</string>
<string name="back">Zpět</string>
<string name="sync_title">Synchronizujte svá data</string>
@@ -207,10 +207,10 @@
<string name="appwidget_shelf_description">Manga z vašich oblíbených</string>
<string name="appwidget_recent_description">Vaše nedávno čtená manga</string>
<string name="report">Hlášení</string>
<string name="show_reading_indicators">Zobrazovat idikátory pokroku</string>
<string name="show_reading_indicators">Zobrazovat indikátory pokroku ve čtení</string>
<string name="data_deletion">Smazání dat</string>
<string name="show_reading_indicators_summary">Zobrazovat procento čtení v historii a oblíbených</string>
<string name="exclude_nsfw_from_history_summary">Manga označená jako NSFW nebude nikdy přidána do historie a nebude ukládán váš pokrok</string>
<string name="exclude_nsfw_from_history_summary">Manga označená jako NSFW nebude nikdy přidána do historie a nebude ukládán Váš pokrok</string>
<string name="clear_cookies_summary">Může pomoci v případě nekterých problémů. Všechny autorizace budou neplatné</string>
<string name="show_all">Zobrazit vše</string>
<string name="invalid_domain_message">Neplatná doména</string>
@@ -251,7 +251,7 @@
<string name="manga_error_description_pattern">Podrobnosti chyby:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Zkuste &lt;a href=%2$s&gt;otveřít mangu v prohlížeči&lt;/a&gt; abyste se ujistili že je dostupná na zdroji&lt;br&gt;2. Ujistěte se že používáte &lt;a href=kotatsu://about&gt;nejnovější verzi Kotatsu&lt;/a&gt;&lt;br&gt;3. Pokud je dostupná, pošlete hlášení o chybě vývojářům.</string>
<string name="history_shortcuts">Zobrazovat zkratky nedávných mang</string>
<string name="history_shortcuts_summary">Udělejte nedávné mangy dostupné dlouhým kliknutím na ikonu aplikace</string>
<string name="reader_control_ltr_summary">Klikněte do pravého rohu nebo stisknutí pravého tlačítka vždy změní na další stranu</string>
<string name="reader_control_ltr_summary">Kliknutím do pravého rohu nebo stisknutím pravého tlačítka vždy zobrazí následující stranu.</string>
<string name="reader_control_ltr">Ovládání ergonomické čtečky</string>
<string name="color_correction">Korekce barev</string>
<string name="brightness">Jas</string>
@@ -293,7 +293,7 @@
<string name="download_started">Stahování začalo</string>
<string name="got_it">Mám to</string>
<string name="sources_reorder_tip">Klikněte a přidržte na předmětu pro přeskupení</string>
<string name="user_agent">UserAgent header</string>
<string name="user_agent">Záhlaví UserAgent</string>
<string name="settings_apply_restart_required">Prosíme restartujte aplikaci pro aplikování těchto změn</string>
<string name="comics_archive_import_description">Můžete vybrat jeden nebo vícs .cbz nebo .zip souborů, každý soubor bude znám jako samostatná manga.</string>
<string name="show_on_shelf">Zobrazovat na poličce</string>
@@ -338,7 +338,7 @@
<string name="network">Síť</string>
<string name="data_and_privacy">Data a soukromí</string>
<string name="restore_summary">Obnovit nedávno vytvořenou zálohu</string>
<string name="reader_info_bar_summary">Zobrazovat aktuální čas a pokrok čtení na vršku displeje</string>
<string name="reader_info_bar_summary">Zobrazovat aktuální čas a pokrok ve čtení na vršku displeje</string>
<string name="show_pages_numbers_summary">Zobrazovat číslo strany ve spodním rohu</string>
<string name="download_option_all_chapters">Všechny kapitoly s překladem %s</string>
<string name="download_option_whole_manga">Celá manga</string>
@@ -425,4 +425,211 @@
<string name="clear_source_cookies_summary">Vyčistit cookies pouze pro specifikované domény. Ve většině případech bude neplatná autorizace</string>
<string name="download_option_manual_selection">Vyberte kapitoly manuálně</string>
<string name="description">Popis</string>
</resources>
<string name="state_upcoming">Již brzy</string>
<string name="no_manga_sources_found">Na Váš dotaz nebyly nalezeny žádné zdroje mang</string>
<string name="downloads_settings_info">Pokud máte problémy s blokováním ze strany serveru, můžete v nastavení zdrojů povolit zpomalení stahování pro jednotlivé zdroje mang</string>
<string name="content_rating">Klasifikace obsahu</string>
<string name="rating_safe">Bezpečný</string>
<string name="chapters_grid_view">Zobrazení v mřížce</string>
<string name="categories">Kategorie</string>
<string name="online_variant">Online varianta</string>
<string name="content_type_hentai">Hentai</string>
<string name="disable_nsfw">Vypnout NSFW</string>
<string name="email_password_enter_hint">Pro pokračování zadejte svůj e-mail a heslo</string>
<string name="this_month">Tento měsíc</string>
<string name="voice_search">Hlasové vyhledávání</string>
<string name="related_manga">Související manga</string>
<string name="color_light">Světlý</string>
<string name="color_dark">Tmavý</string>
<string name="color_white">Bílá</string>
<string name="background">Pozadí</string>
<string name="color_black">Černá</string>
<string name="data_not_restored">Data nebyla obnovena</string>
<string name="in_progress">Probíhá</string>
<string name="captcha_required_summary">%s vyžaduje vyřešení captcha aby fungovalo správně</string>
<string name="languages">Jazyky</string>
<string name="unknown">Neznámé</string>
<string name="too_many_requests_message">Příliš mnoho dotazů. Zkuste to později</string>
<string name="related_manga_summary">Zobrazí seznam souvisejících mang. V některých případech může být nepřesný nebo chybět</string>
<string name="advanced">Pokročilý</string>
<string name="manga_list">Seznam mang</string>
<string name="main_screen_sections">Oddíly hlavní obrazovky</string>
<string name="on_device">Na zařízení</string>
<string name="directories">Rejstříky</string>
<string name="items_limit_exceeded">Nelze přidávat žádné další položky</string>
<string name="to_top">Úplně nahoru</string>
<string name="moved_to_top">Přesunuto na začátek</string>
<string name="zoom_out">Oddálit</string>
<string name="zoom_in">Přiblížit</string>
<string name="reader_zoom_buttons_summary">Zobrazit či nezobrazit tlačítka ovládání přiblížení v pravém dolním rohu</string>
<string name="enhanced_colors_summary">Snižuje banding, ale může ovlivnit výkon</string>
<string name="state_abandoned">Upuštěný</string>
<string name="enhanced_colors">32-bitový režim barev</string>
<string name="suggest_new_sources_summary">Výzva k povolení nově přidaných zdrojů po aktualizaci aplikace</string>
<string name="suggest_new_sources">Navrhnout nové zdroje po aktualizaci aplikace</string>
<string name="periodic_backups">Pravidelné zálohy</string>
<string name="backup_frequency">Frekvence vytváření záloh</string>
<string name="frequency_every_day">Každý den</string>
<string name="frequency_every_2_days">Každé 2 dny</string>
<string name="frequency_once_per_week">Jednou týdně</string>
<string name="frequency_twice_per_month">Dvakrát měsíčně</string>
<string name="frequency_once_per_month">Jednou měsíčně</string>
<string name="backups_output_directory">Výstupní adresář záloh</string>
<string name="periodic_backups_enable">Povolit pravidelné zálohy</string>
<string name="last_successful_backup">Poslední úspěšná záloha: %s</string>
<string name="lock_screen_rotation">Uzamknout otáčení obrazovky</string>
<string name="content_type_other">Jiné</string>
<string name="sources_catalog">Katalog zdrojů</string>
<string name="source_summary_pattern">%1$s, %2$s</string>
<string name="source_enabled">Zdroj povolen</string>
<string name="catalog">Katalog</string>
<string name="manage_sources">Spravovat zdroje</string>
<string name="manual">Ručně</string>
<string name="available_d">Dostupný: %1$d</string>
<string name="disable_nsfw_summary">Zakázat NSFW zdroje a skrýt mangy pro dospělé ze seznamu, je-li to možné</string>
<string name="error_multiple_genres_not_supported">Filtrování podle více žánrů tento zdroj mang nepodporuje</string>
<string name="error_multiple_states_not_supported">Filtrování podle více stavů tento zdroj mang nepodporuje</string>
<string name="error_search_not_supported">Hledání není v tomto zdroji mang podporovaný</string>
<string name="skip">Přeskočit</string>
<string name="grayscale">Odstíny šedi</string>
<string name="data_not_restored_text">Ujistěte se, že jste vybrali ten správný zálohový soubor</string>
<string name="manage_categories">Spravovat kategorie</string>
<string name="show">Ukázat</string>
<string name="apply">Použít</string>
<string name="speed_value">x%.1f</string>
<string name="search_hint">Zadejte název mangy, žánr nebo název zdroje</string>
<string name="progress">Pokrok</string>
<string name="order_added">Přidáno</string>
<string name="error_corrupted_file">Vrácená data jsou neplatná nebo je soubor poškozen</string>
<string name="reader_zoom_buttons">Zobrazit tlačítka pro přiblížení</string>
<string name="keep_screen_on">Ponechat obrazovku zapnutou</string>
<string name="keep_screen_on_summary">Nevypínat obrazovku během čtení mangy</string>
<string name="list_options">Seznam možností</string>
<string name="by_relevance">Relevance</string>
<string name="state_paused">Pozastavený</string>
<string name="reader_optimize">Snížit spotřebu paměti (beta)</string>
<string name="reader_optimize_summary">Snížit kvalitu stránek mimo obrazovku pro snížení spotřeby paměti</string>
<string name="state">Stav</string>
<string name="globally">Globálně</string>
<string name="this_manga">Tato manga</string>
<string name="error_filter_locale_genre_not_supported">Filtrování podle žánrů nebo lokalizace tento zdroj nepodporuje</string>
<string name="error_filter_states_genre_not_supported">Filtrování podle žánrů nebo stavů tento zdroj nepodporuje</string>
<string name="welcome_text">Vyberte, které zdroje obsahu chcete povolit. To lze také později změnit v nastavení</string>
<string name="genres_search_hint">Začněte psát název žánru</string>
<string name="sync_auth">Přihlášení k synchronizačnímu účtu</string>
<string name="restore">Obnovit</string>
<string name="backup_date_">Datum obnovení: %s</string>
<string name="by_name_reverse">Název obráceně</string>
<string name="suggestions_wifi_only_summary">Neaktualizujte návrhy pomocí síťových připojení s účtovaným objemem dat</string>
<string name="tracker_wifi_only_summary">Nevyhledávejte nové kapitoly pomocí síťových připojení s účtovaným objemem dat</string>
<string name="color_correction_apply_text">Tato nastavení lze použít globálně nebo pouze pro aktuální manga. Pokud bude použito globálně, individuální nastavení nebudou přepsána.</string>
<string name="content_type_comics">Komiks</string>
<string name="no_manga_sources_catalog_text">V této sekci nejsou k dispozici žádné zdroje, případně již byly všechny přidány.
\nZůstaňte v kontaktu</string>
<string name="content_type_manga">Manga</string>
<string name="disable_battery_optimization_summary_downloads">Mohlo by to pomoci se zahájením stahování, pokud s tím máte nějaké problémy</string>
<string name="genres_exclude">Vyloučit žánry</string>
<string name="unsupported_backup_message">Vyberte prosím správný zálohový soubor Kotatsu</string>
<string name="last_used">Naposledy používané</string>
<string name="volume_">Díl %d</string>
<string name="remove_from_history">Odstranit z historie</string>
<string name="incognito_mode_hint">Váš pokrok ve čtení nebude uložen</string>
<string name="last_read">Poslední přečtený</string>
<string name="minutes_short">%d m</string>
<string name="hours_minutes_short">%1$d h %2$d m</string>
<string name="vertical">Svisle</string>
<string name="ask_for_dest_dir_every_time">Pokaždý se ptát na cílovou složku</string>
<string name="show_menu">Zobrazit menu</string>
<string name="mark_as_completed_prompt">Označit vybranou mangu jako kompletně přečtenou?
\n
\nPozor: Stávající pokrok ve čtení se ztratí.</string>
<string name="remaining_time_pattern">%1$s %2$s</string>
<string name="volume_unknown">Neznámý díl</string>
<string name="prev_chapter">Předchozí kapitola</string>
<string name="next_chapter">Následující kapitola</string>
<string name="prev_page">Předchozí stránka</string>
<string name="next_page">Následující stránka</string>
<string name="reader_actions">Akce čtečky</string>
<string name="reader_actions_summary">Konfigurace akcí pro oblasti obrazovky, na které lze klepnout</string>
<string name="switch_pages_volume_buttons">Povolit tlačítka ovládání hlasitosti</string>
<string name="switch_pages_volume_buttons_summary">Používání tlačítek ovládání hlasitosti k přetáčení stránek</string>
<string name="tap_action">Akce klepnutím</string>
<string name="long_tap_action">Akce dlouhým klepnutím</string>
<string name="none">Žádný</string>
<string name="use_two_pages_landscape">Použít rozvržení dvou stránek na šířku (beta)</string>
<string name="default_webtoon_zoom_out">Výchozí zvětšení pro webtoon</string>
<string name="reading_time_estimation">Zobrazit odhadovanou dobu čtení</string>
<string name="reading_time_estimation_summary">Odhadovaná doba čtení může být nepřesná</string>
<string name="pages_saving">Ukládání stránek</string>
<string name="default_page_save_dir">Výchozí složka k ukládání stránek</string>
<string name="single_cbz_file">Jediný soubor CBZ</string>
<string name="preferred_download_format">Upřednostňovaný formát stahování</string>
<string name="multiple_cbz_files">Vícero souborů CBZ</string>
<string name="clear_stats_confirm">Opravdu chcete vymazat veškeré statistiky čtení? Tato akci nelze vrátit zpět.</string>
<string name="pages_read_s">Přečtené stránky: %s</string>
<string name="migrate_confirmation">Manga \"%1$s\" z \"%2$s\" bude ve Vaší historii a oblíbených položkách nahrazena \"%3$s\" z \"%4$s\" (je-li k dispozici)</string>
<string name="alternatives">Jiné možnosti</string>
<string name="migrate">Přesunout</string>
<string name="manga_migration">Přesun mangy</string>
<string name="migration_completed">Přesun dokončen</string>
<string name="delete_read_chapters">Odstranit přečtené kapitoly</string>
<string name="delete_read_chapters_prompt">Toto trvale odstraní veškeré přečtené kapitoly z místního uložiště. Můžete je stáhnout znovu později, ale importované kapitoly budou ztraceny navždy</string>
<string name="reading_stats">Statistické údaje čtení</string>
<string name="other_manga">Jiné mangy</string>
<string name="less_than_minute">Méně než minutu</string>
<string name="clear_stats">Vymazat statistiky</string>
<string name="week">Týden</string>
<string name="month">Měsíc</string>
<string name="all_time">Po celou dobu</string>
<string name="day">Den</string>
<string name="three_months">Tři měsíce</string>
<string name="empty_stats_text">Pro zvolené období nejsou k dispozici žádné statistiky</string>
<string name="show_pages_thumbs_summary">Povolit záložku \"Stránky\" na obrazovce podrobností</string>
<string name="show_pages_thumbs">Zobrazit náhledy stránek</string>
<string name="fullscreen_mode">Režim celé obrazovky</string>
<string name="reader_fullscreen_summary">Skrýt systémový stavový a navigační řádek</string>
<string name="category_hidden_done">Tato kategorie byla skryta z hlavní obrazovky a je přístupná přes Menu → Spravovat kategorie</string>
<string name="automatic">Automatický</string>
<string name="show_updated">Zobrazit aktualizované</string>
<string name="split_by_translations_summary">Zobrazit kapitoly s různými překlady samostatně, nikoli v jednom seznamu</string>
<string name="order_oldest">Nejstarší</string>
<string name="long_ago_read">Přečteno před dlouhou dobou</string>
<string name="unread">Nepřečteno</string>
<string name="suggestions_unavailable_text">Funkce návrhů je vypnutá</string>
<string name="check_for_new_chapters_disabled">Zjišťování nových kapitol je vypnuté</string>
<string name="rating_suggestive">Sugestivní</string>
<string name="default_tab">Výchozí záložka</string>
<string name="mark_as_completed">Označit jako dokončený</string>
<string name="toggle_ui">Zobrazit/skrýt uživatelské rozhraní</string>
<string name="statistics">Statistiky</string>
<string name="no_chapters_deleted">Nebyly odstraněny žádné kapitoly</string>
<string name="chapters_deleted_pattern">Odstraněn %1$s, smazán %2$s</string>
<string name="delete_read_chapters_auto">Odstraňovat přečtené kapitoly automaticky</string>
<string name="runs_on_app_start">Spouští se při spuštění aplikace</string>
<string name="webtoon_gaps">Mezery v režimu webtoon</string>
<string name="webtoon_gaps_summary">Zobrazit svislé mezery mezi stránkami v režimu webtoon</string>
<string name="location">Umístění</string>
<string name="config_reset_confirm">Obnovit nastavení na výchozí hodnoty? Tuto akci nelze vrátit zpět.</string>
<string name="search_suggestions">Návrhy na vyhledávání</string>
<string name="recent_queries">Nedávné dotazy</string>
<string name="suggested_queries">Navrhované dotazy</string>
<string name="error_no_data_received">Ze serveru nebyla přijata žádná data</string>
<string name="rating_adult">Dospělý</string>
<string name="show_labels_in_navbar">Zobrazit štítky na navigačním panelu</string>
<string name="stats_cleared">Statistiky smazány</string>
<string name="delete_read_chapters_summary">Odstranit již přečtené kapitoly z místního uložiště k uvolnění paměti</string>
<string name="split_by_translations">Rozdělení podle překladů</string>
<string name="hours_short">%d h</string>
<string name="missing_storage_permission">Přístup k manze na externím úložišti není povolen</string>
<string name="authors">Autoři</string>
<string name="enable_source">Povolit zdroj</string>
<string name="unsupported_source">Tento zdroj mang není podporovaný</string>
<string name="blocked_by_server_message">Jste zablokováni serverem. Zkuste použít jiné síťové připojení (VPN, proxy atd.)</string>
<string name="less_frequently">Méně často</string>
<string name="more_frequently">Častěji</string>
<string name="frequency_of_check">Četnost kontrol</string>
<string name="new_chapters_pattern">%1$s: %2$d</string>
<string name="pin_navigation_ui">Upevnit uživatelské rozhraní pro navigaci</string>
<string name="fix">Upevnit</string>
<string name="pin_navigation_ui_summary">Neskrývat navigační panel a zobrazení vyhledávání při posouvání</string>
</resources>

View File

@@ -535,4 +535,4 @@
<string name="appwidget_recent_description">Τα πρόσφατα διαβασμένα manga σου</string>
<string name="clear_cookies_summary">Μπορεί να βοηθήσει σε περίπτωση κάποιων προβλημάτων. Όλες οι εξουσιοδοτήσεις θα ανακληθούν</string>
<string name="category_hidden_done">Αυτή η κατηγορία αποκρύφτηκε από την αρχική οθόνη και είναι προσβάσιμη μέσω του Μενού → Διαχείριση κατηγοριών</string>
</resources>
</resources>

View File

@@ -301,7 +301,7 @@
<string name="categories_delete_confirm">¿Estás seguro de que quieres eliminar las categorías favoritas seleccionadas\?
\nTodos los mangas en ella se perderán y esto no se puede deshacer.</string>
<string name="explore">Explorar</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="exit_confirmation_summary">Pulse dos veces «Atrás» para salir de la aplicación</string>
<string name="exit_confirmation">Confirmación de salida</string>
<string name="pages_cache">Caché de páginas</string>
@@ -560,22 +560,22 @@
<string name="default_webtoon_zoom_out">Alejar el zoom del webtoon predeterminado</string>
<string name="fullscreen_mode">Modo de pantalla completa</string>
<string name="reader_fullscreen_summary">Ocultar las barras de estado y navegación del sistema</string>
<string name="reading_time_estimation">Mostrar el iempo estimado de lectura</string>
<string name="reading_time_estimation">Mostrar el tiempo estimado de lectura</string>
<string name="reading_time_estimation_summary">El valor estimado puede ser inexacto</string>
<string name="check_for_new_chapters_disabled">La búsqueda de nuevos capítulos está desactivada</string>
<string name="suggestions_unavailable_text">Sugerencias desactivadas</string>
<string name="show_labels_in_navbar">Mostrar etiquetas en la barra de navegación</string>
<string name="ask_for_dest_dir_every_time">Preguntar siempre por el directorio de destino</string>
<string name="remove_from_history">Eliminar del historial</string>
<string name="pages_saving">Guardar páginas</string>
<string name="pages_saving">Guardando páginas</string>
<string name="default_page_save_dir">Directorio predeterminado para guardar páginas</string>
<string name="location">Ubicación</string>
<string name="automatic">Automático</string>
<string name="single_cbz_file">Un único archivo CBZ</string>
<string name="multiple_cbz_files">Varios archivos CBZ</string>
<string name="preferred_download_format">Formato preferido para la descarga</string>
<string name="reading_stats">Leyendo las estadísticas</string>
<string name="other_manga">Otro manga</string>
<string name="reading_stats">Estadística de lectura</string>
<string name="other_manga">Otros mangas</string>
<string name="less_than_minute">Menos de un minuto</string>
<string name="statistics">Estadísticas</string>
<string name="stats_cleared">Estadísticas borradas</string>
@@ -591,7 +591,7 @@
<string name="delete_read_chapters">Borrar capítulos leídos</string>
<string name="no_chapters_deleted">No se han eliminado capítulos</string>
<string name="chapters_deleted_pattern">Eliminado %1$s, borrado %2$s</string>
<string name="delete_read_chapters_summary">Borra los capítulos que ya has leído de la memoria local para liberar espacio</string>
<string name="delete_read_chapters_summary">Borrar los capítulos que ya has leído de la memoria local para liberar espacio</string>
<string name="delete_read_chapters_prompt">Esto borrará permanentemente todos los capítulos marcados como leídos de tu almacenamiento local. Puede volver a descargarlo más tarde, pero los capítulos importados se perderán para siempre</string>
<string name="alternatives">Alternativas</string>
<string name="migrate">Migrar</string>
@@ -632,4 +632,9 @@
<string name="pin_navigation_ui_summary">No ocultar la barra de navegación y la vista de búsqueda al desplazarse</string>
<string name="pin_navigation_ui">Pin de la interfaz de usuario</string>
<string name="blocked_by_server_message">Estás bloqueado por el servidor. Intente utilizar una conexión de red diferente (VPN, Proxy, etc.)</string>
<string name="disable">Desactivar</string>
<string name="sources_disabled">Fuentes deshabilitadas</string>
<string name="disable_connectivity_check">Desactivar el control de conectividad</string>
<string name="ignore_ssl_errors_summary">Puede desactivar la verificación de certificados SSL en caso de que tenga problemas relacionados con SSL al acceder a recursos de red. Esto puede afectar a su seguridad. Es necesario reiniciar la aplicación después de cambiar esta configuración.</string>
<string name="disable_connectivity_check_summary">Omitir la comprobación de la conectividad en caso de que tenga problemas con ella (por ejemplo, si pasa al modo sin conexión aunque la red esté conectada)</string>
</resources>

View File

@@ -422,4 +422,4 @@
<string name="downloads_resumed">Allalaadimised on jätkanud</string>
<string name="invert_colors">Värvide ümberpööramine</string>
<string name="proxy">Puhverserver</string>
</resources>
</resources>

View File

@@ -182,7 +182,7 @@
<string name="screenshots_block_nsfw">I-block sa NSFW</string>
<string name="suggestions_summary">Magmungkahi ng manga batay sa iyong mga kagustuhan</string>
<string name="suggestions_info">Ang lahat ng data ay lokal lamang na sinusuri sa device na ito at hindi kailanman ipinadala kahit saan.</string>
<string name="disabled">Hindi pinagana</string>
<string name="disabled">Di pinagana</string>
<string name="reset_filter">I-reset ang filter</string>
<string name="text_feed_holder">Ang mga bagong kabanata ng iyong binabasa ay ipinapakita dito</string>
<string name="rotate_screen">I-rotate ang screen</string>
@@ -250,7 +250,7 @@
<string name="other_cache">Iba pang cache</string>
<string name="storage_usage">Paggamit ng storage</string>
<string name="available">Magagamit na</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="removed_from_favourites">Inalis sa mga paborito</string>
<string name="options">Mga pagpipilian</string>
<string name="incognito_mode">Incognito mode</string>
@@ -322,12 +322,12 @@
\nAng lahat ng manga sa loob nito ay mawawala at hindi na ito mababawi.</string>
<string name="bookmark_added">Idinagdag ang bookmark</string>
<string name="detect_reader_mode_summary">Awtomatikong matukoy kung ang manga ay webtoon</string>
<string name="disable_battery_optimization">Huwag paganahin ang pag-optimize ng baterya</string>
<string name="disable_battery_optimization">Di paganahin ang pag-optimize ng baterya</string>
<string name="disable_battery_optimization_summary">Tumutulong sa mga pagsusuri sa mga update sa background</string>
<string name="status_planned">Nakaplano</string>
<string name="status_completed">Nakumpleto na</string>
<string name="status_on_hold">Naka-hold</string>
<string name="disable_all">Huwag paganahin ang lahat</string>
<string name="disable_all">Di paganahin lahat</string>
<string name="use_fingerprint">Gumamit ng fingerprint kung magagamit</string>
<string name="report">Ulat</string>
<string name="reset">I-reset</string>
@@ -338,7 +338,7 @@
<string name="reader_slider">Ipakita ang slider ng paglipat ng pahina</string>
<string name="manga_error_description_pattern">Mga detalye ng error:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. Subukang &lt;a href=%2$s&gt;magbukas ng manga sa isang web browser&lt;/a&gt; upang matiyak na available ito sa souce&lt;br&gt;2. Tiyaking ginagamit mo ang &lt;a href=kotatsu://about&gt;pinakabagong bersyon ng Kotatsu&lt;/a&gt;&lt;br&gt;3. Kung available ito, magpadala ng ulat ng error sa mga developer.</string>
<string name="enable_logging">Paganahin ang pag-log</string>
<string name="source_disabled">Hindi pinagana ang source</string>
<string name="source_disabled">Di pinagana ang source</string>
<string name="importing_manga">Pag-import ng manga</string>
<string name="import_completed_hint">Maaari mong tanggalin ang orihinal na file mula sa storage upang makatipid ng espasyo</string>
<string name="compact">Compact</string>
@@ -446,7 +446,7 @@
<string name="show">Ipakita</string>
<string name="unknown">Di-kilala</string>
<string name="in_progress">Isinasagawa</string>
<string name="disable_nsfw">I-disable ang NSFW</string>
<string name="disable_nsfw">Di paganahin ang NSFW</string>
<string name="error_corrupted_file">Nabalik ang di-wastong data o na-corrupt ang file</string>
<string name="related_manga_summary">Magpakita ng listahan ng mga kaugnay na manga. Sa ilang mga kaso, ito ay maaaring may mali o nawawala</string>
<string name="advanced">Advanced</string>
@@ -495,7 +495,7 @@
<string name="lock_screen_rotation">Rotation ng lock screen</string>
<string name="manual">Manu-mano</string>
<string name="source_enabled">Napaganang source</string>
<string name="disable_nsfw_summary">Huwag paganahin ang mga source na NSFW at itago ang manga na pang-adulto mula sa listahan kung maaari</string>
<string name="disable_nsfw_summary">Di paganahin ang mga source na NSFW at itago ang manga na pang-matanda mula sa listahan kung maaari</string>
<string name="no_manga_sources_catalog_text">Walang mga sources na maaring gamitin sa seksyong ito, o maaring na-add na lahat.
\nManatiling nakatutok para sa iba pang mga source</string>
<string name="available_d">Available: %1$d</string>
@@ -565,8 +565,8 @@
<string name="reader_actions_summary">Ayusin ang mga pagkilos para sa mga nata-tap na lugar ng screen</string>
<string name="switch_pages_volume_buttons">Paganahin ang mga volume button</string>
<string name="switch_pages_volume_buttons_summary">Gumamit ng mga volume button para sa paglipat ng mga pahina</string>
<string name="suggestions_unavailable_text">Naka-disable ang feature na Mga suhestiyon</string>
<string name="check_for_new_chapters_disabled">Naka-disable ang pagsuri para sa mga bagong kabanata</string>
<string name="suggestions_unavailable_text">Di pinagana ang feature na Mga suhestiyon</string>
<string name="check_for_new_chapters_disabled">Di pinagana ang pagsusuri para sa mga bagong kabanata</string>
<string name="reading_time_estimation">Ipakita ang tinantyang oras ng pagbabasa</string>
<string name="reading_time_estimation_summary">Maaaring hindi tumpak ang halaga ng pagtatantya ng oras</string>
<string name="location">Lokasyon</string>
@@ -611,7 +611,7 @@
<string name="show_pages_thumbs">Ipakita ang mga thumbnail ng pahina</string>
<string name="show_pages_thumbs_summary">Paganahin ang tab na \"Mga Pahina\" sa screen ng mga detalye</string>
<string name="error_no_data_received">Walang natanggap na data mula sa server</string>
<string name="unsupported_backup_message">Mangyaring pumili ng wastong backup file ng Kotatsu</string>
<string name="unsupported_backup_message">Pumili ng wastong backup file ng Kotatsu</string>
<string name="hours_short">%d o</string>
<string name="minutes_short">%d m</string>
<string name="hours_minutes_short">%1$d o %2$d m</string>
@@ -632,4 +632,9 @@
<string name="pin_navigation_ui">I-pin ang UI ng nabigasyon</string>
<string name="pin_navigation_ui_summary">Huwag itago ang navigation bar at ang view ng paghahanap sa pag-scroll</string>
<string name="blocked_by_server_message">Hinarangan ka ng server. Subukang gumamit ng ibang koneksyon sa network (VPN, Proxy, atbp.)</string>
<string name="disable">Di paganahin</string>
<string name="sources_disabled">Di pinagana ang mga source</string>
<string name="disable_connectivity_check">Di paganahin ang pagtingin sa koneksyon</string>
<string name="disable_connectivity_check_summary">Laktawan ang pagsuri sa koneksyon kung sakaling mayroon kang isyu rito (hal. pagpunta sa offline mode kahit na nakakonekta sa network)</string>
<string name="ignore_ssl_errors_summary">Maaaring di paganahin ang pag-verify ng mga SSL certificate kung sakaling makaharap ka ng mga isyu na nauugnay sa SSL kapag nag-a-access ng mga network resource. Ito ay makaapekto sa iyong seguridad. Kinakailangang mag-restart ang aplikasyon pagkatapos baguhin ang setting na ito.</string>
</resources>

View File

@@ -5,7 +5,7 @@
<string name="nothing_found">कुछ नहीं मिला</string>
<string name="history_is_empty">अभी तक कोई इतिहास नहीं है</string>
<string name="read">पढ़ें</string>
<string name="add_to_favourites">इसे पसंद करे</string>
<string name="add_to_favourites">इसे पसंदीदा बनाए</string>
<string name="add">जोड़ें</string>
<string name="save">सहेजें</string>
<string name="newest">नवीनतम</string>
@@ -14,7 +14,7 @@
<string name="close">बंद करे</string>
<string name="try_again">पुनः प्रयास करें</string>
<string name="you_have_not_favourites_yet">अभी तक कोई पसंदीदा नहीं है</string>
<string name="remove">हटाए</string>
<string name="remove">हटाए</string>
<string name="by_name">नाम</string>
<string name="popular">लोकप्रिय</string>
<string name="local_storage">स्थानीय स्टॉरेज</string>
@@ -27,45 +27,45 @@
<string name="chapter_d_of_d">अध्याय %1$d, %2$d में से</string>
<string name="computing_">गणना हो रही है…</string>
<string name="add_new_category">नई श्रेणी</string>
<string name="clear_history">इतिहास मिटाए</string>
<string name="share">शेयर</string>
<string name="clear_history">इतिहास साफ करें</string>
<string name="share">साझा करें</string>
<string name="create_shortcut">शॉर्टकट बनाएं…</string>
<string name="share_s">%s साझा करें</string>
<string name="search">खोजें</string>
<string name="search_manga">मंगा खोजें</string>
<string name="manga_downloading_">डाउनलोड हो रहा है…</string>
<string name="downloads">डाउनलोड्स</string>
<string name="downloads">डाउनलोड</string>
<string name="by_rating">रेटिंग</string>
<string name="clear">साफ करे</string>
<string name="clear">साफ करे</string>
<string name="page_saved">सहेजा गया</string>
<string name="share_image">छवि साझा करें</string>
<string name="delete">मिटाएं</string>
<string name="clear_pages_cache">ेज कैश साफ करें</string>
<string name="clear_pages_cache">ृष्ठ कैश साफ करें</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="standard">सामान्य</string>
<string name="webtoon">Webtoon</string>
<string name="standard">मान</string>
<string name="webtoon">वेबटून</string>
<string name="remote_sources">मांगा स्रोत</string>
<string name="download_complete">डाउनलोड हो गया</string>
<string name="processing_">प्रक्रिया चल रही है</string>
<string name="download_complete">डाउनलोड किया गया</string>
<string name="processing_">प्रसंस्करण</string>
<string name="history">इतिहास</string>
<string name="grid">ग्रिड</string>
<string name="loading_">लोड हो रहा है…</string>
<string name="text_file_not_supported">ZIP या CBZ मैं से एक फाइल चुनें।</string>
<string name="updated">पडेट हो गया</string>
<string name="_s_deleted_from_local_storage">\"%s\" को स्थानीय स्टोरेज से टा दिया गया</string>
<string name="save_page">ेज सहेजें</string>
<string name="text_file_not_supported">ZIP या CBZ मैं से एक फाइल चुनें।</string>
<string name="updated">द्यतित</string>
<string name="_s_deleted_from_local_storage">\"%s\" को स्थानीय स्टोरेज से मिटा दिया गया</string>
<string name="save_page">ृष्ठ सहेजें</string>
<string name="_import">आयात</string>
<string name="operation_not_supported">यह कार्य समर्थित नहीं है</string>
<string name="sort_order">क्रमबद्धता क्रम</string>
<string name="sort_order">छंटाई क्रम</string>
<string name="list">सूची</string>
<string name="filter">फिल्टर</string>
<string name="theme">थीम</string>
<string name="follow_system">सिस्टम की पालन करें</string>
<string name="follow_system">सिस्टम की पालन</string>
<string name="pages">पन्ने</string>
<string name="no_description">कोई विवरण नहीं है</string>
<string name="updates">पडेट</string>
<string name="manga_shelf">शेल्फ</string>
<string name="text_history_holder_secondary">\"एक्सप्लोर करें\" अनुभाग में जानें कि क्या पढ़ना है</string>
<string name="updates">द्यतन</string>
<string name="manga_shelf">दराज</string>
<string name="text_history_holder_secondary">«अन्वेषण करें» अनुभाग में जानें कि क्या पढ़ना है</string>
<string name="all_favourites">सभी पसंदीदा</string>
<string name="light_indicator">LED सूचक</string>
<string name="favourites_categories">पसंदीदा श्रेणियां</string>
@@ -73,7 +73,7 @@
<string name="switch_pages">पन्नो को बदले</string>
<string name="rotate_screen">स्क्रीन घुमायें</string>
<string name="vibration">कंपन</string>
<string name="remove_category">हटाए</string>
<string name="remove_category">हटाए</string>
<string name="read_mode">पढ़ने की विधि</string>
<string name="internal_storage">आंतरिक स्टोरेज</string>
<string name="read_later">बाद में पढ़े</string>
@@ -81,46 +81,46 @@
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%2$d में से %1$d</string>
<string name="text_feed_holder">आप जो पढ़ रहे हैं उसके नए अध्याय यहां दिखाए गए हैं</string>
<string name="favourites_category_empty">रिक्त श्रेणी</string>
<string name="manga_save_location">डाउनलोड फोल्डर</string>
<string name="updates_feed_cleared">साफ हो गया</string>
<string name="update">पडेट</string>
<string name="feed_will_update_soon">फीड अपडेट शीघ्र ही आरंभ होगा</string>
<string name="manga_save_location">डाउनलोड फोल्डर</string>
<string name="updates_feed_cleared">साफ किया गया</string>
<string name="update">द्यतन</string>
<string name="feed_will_update_soon">फीड अद्यतन शीघ्र ही आरंभ होगा</string>
<string name="app_update_available">इस ऐप का नया संस्करण उपलब्ध हैं</string>
<string name="new_version_s">नया संस्करण: %s</string>
<string name="text_delete_local_manga">डिवाइस से \"%s\" को स्थायी रूप से टाएं?</string>
<string name="text_delete_local_manga">डिवाइस से \"%s\" को स्थायी रूप से मिटाएं?</string>
<string name="text_history_holder_primary">आप जो पढ़ेंगे वह यहां प्रदर्शित किया जाएगा</string>
<string name="delete_manga">मंगा टाएं</string>
<string name="delete_manga">मंगा मिटाएं</string>
<string name="notification_sound">सूचना की ध्वनि</string>
<string name="search_history_cleared">साफ हो गया</string>
<string name="open_in_browser">वेब ब्राउज़र में खोलें</string>
<string name="notifications">सूचनाएं</string>
<string name="not_available">उपल्ब्ध नहीं हैं</string>
<string name="track_sources">पडेट देखें</string>
<string name="clear_search_history">खोजा हुवा इतिहास को साफ करे</string>
<string name="track_sources">द्यतन देखें</string>
<string name="clear_search_history">खोज इतिहास साफ करे</string>
<string name="download">डाउनलोड</string>
<string name="size_s">आकार: %s</string>
<string name="new_chapters">नये अध्याय</string>
<string name="clear_updates_feed">पडेटीड साफ करें</string>
<string name="clear_updates_feed">द्यतन फीड साफ करें</string>
<string name="notifications_settings">सुचना के सेटिंग</string>
<string name="domain">डोमेन</string>
<string name="reader_settings">रीडर के सेटिंग</string>
<string name="reader_settings">पाठक सेटिंग्स</string>
<string name="text_search_holder_secondary">क्वेरी को पुन: तैयार करने का प्रयास करें।</string>
<string name="error">त्रुटि</string>
<string name="grid_size">ग्रिड का आकार</string>
<string name="_continue">जारी रखें</string>
<string name="recent_manga">हालिया</string>
<string name="search_on_s">%s पर खोजें</string>
<string name="search_results">खोज के परिणाम</string>
<string name="pages_animation">ेज एनीमेशन</string>
<string name="search_results">खोज परिणाम</string>
<string name="pages_animation">ृष्ठ सजीवता</string>
<string name="other_storage">अन्य स्टोरेज</string>
<string name="external_storage">बाहरी स्टोरेज</string>
<string name="text_local_holder_secondary">किसी ऑनलाइन कैटलॉग से कुछ सहेजें या किसी फाइल से आयात करें।</string>
<string name="text_local_holder_secondary">किसी ऑनलाइन कैटलॉग से कुछ सहेजें या किसी फाइल से आयात करें।</string>
<string name="text_local_holder_primary">पहले कुछ सहेजें</string>
<string name="text_empty_holder_primary">यहा कुछ खाली सा है…</string>
<string name="done">हो गया</string>
<string name="dont_check">जाच मत कर</string>
<string name="text_empty_holder_primary">यहा कुछ खाली सा है…</string>
<string name="done">संपन्न</string>
<string name="dont_check">जाच मत करें</string>
<string name="enter_password">पासवर्ड दर्ज करें</string>
<string name="advanced">विकसि</string>
<string name="advanced">उन्न</string>
<string name="catalog">कैटलॉग</string>
<string name="manage_sources">स्रोत प्रबंधित करें</string>
<string name="screenshots_policy">स्क्रीनशॉट नीति</string>
@@ -129,35 +129,35 @@
<string name="suggestions_enable">सुझाव सक्षम करें</string>
<string name="suggestions_summary">अपनी प्राथमिकताओं के आधार पर मंगा का सुझाव दें</string>
<string name="suggestions_info">सभी डेटा का विश्लेषण केवल इस डिवाइस पर स्थानीय रूप से किया जाता है और कभी भी कहीं नहीं भेजा जाता है।</string>
<string name="reset_filter">िल्टर रीसेट करें</string>
<string name="reset_filter">फिल्टर रीसेट करें</string>
<string name="onboard_text">उन भाषाओं का चयन करें जिन्हें आप मंगा पढ़ना चाहते हैं। आप इसे बाद में सेटिंग में बदल सकते हैं।</string>
<string name="chapters_empty">इस मंगा में कोई अध्याय नहीं</string>
<string name="suggestions_excluded_genres">शैलियों को छोड़ें</string>
<string name="removal_completed">निष्कासन पूरा हुआ</string>
<string name="removal_completed">निष्कासन पूर्ण हो गय</string>
<string name="download_slowdown_summary">आपके IP पते को ब्लॉक होने से बचाने में मदद करता है</string>
<string name="local_manga_processing">सहेजे गए मंगा का प्रसंस्करण</string>
<string name="chapters_will_removed_background">अध्याय पृष्ठभूमि में हटा दिए जाएंगे</string>
<string name="comics_archive">कॉमिक्स संग्रह</string>
<string name="webtoon_zoom">वेबटून जूम</string>
<string name="repeat_password">पासवर्ड दोहराए</string>
<string name="webtoon_zoom">वेबटून जूम</string>
<string name="repeat_password">पासवर्ड दोहराए</string>
<string name="passwords_mismatch">बेमेल पासवर्ड</string>
<string name="app_version">संस्करण %s</string>
<string name="check_for_updates">पडेट के लिए जांचें</string>
<string name="no_update_available">कोई अपडेट उपलब्ध नहीं</string>
<string name="check_for_updates">द्यतनों के लिए जांचें</string>
<string name="no_update_available">कोई अद्यतन उपलब्ध नहीं</string>
<string name="scale_mode">स्केल मोड</string>
<string name="black_dark_theme">काली</string>
<string name="black_dark_theme_summary">AMOLED स्क्रीन पर कम पावर का उपयोग होता है</string>
<string name="black_dark_theme_summary">AMOLED स्क्रीन पर कम बिजली का उपयोग होता है</string>
<string name="create_backup">डेटा बैकअप बनाएं</string>
<string name="restore_backup">बैकअप से पुनर्स्थापित करें</string>
<string name="data_restored">पुनर्स्थापित किया गया</string>
<string name="data_restored_with_errors">डेटा पुनर्स्थापित कर दिया गया था, लेकिन त्रुटिया हैं</string>
<string name="just_now">अभी</string>
<string name="data_restored_with_errors">डेटा पुनर्स्थापित कर दिया गया था, लेकिन त्रुटिया हैं</string>
<string name="just_now">बस अभी</string>
<string name="yesterday">कल</string>
<string name="long_ago">बहुत पहले</string>
<string name="group">समूह</string>
<string name="today">आज</string>
<string name="tap_to_try_again">दोबारा प्रयास करने के लिए टैप करें</string>
<string name="reader_mode_hint">इस मंगा के लिए चुना गया कॉन्फ़िगरेशन याद रखा जाएगा</string>
<string name="reader_mode_hint">इस मंगा के लिए चुना गया विन्यास याद रखा जाएगा</string>
<string name="silent">खामोश</string>
<string name="captcha_required">CAPTCHA आवश्यक है</string>
<string name="captcha_solve">हल करें</string>
@@ -165,15 +165,15 @@
<string name="backup_saved">बैकअप सहेजा गया</string>
<string name="welcome">स्वागत है</string>
<string name="tracker_warning">कुछ डिवाइसों में अलग-अलग सिस्टम व्यवहार होता है, जो पृष्ठभूमि कार्यों को बाधित कर सकता है।</string>
<string name="read_more">और पढ़ें</string>
<string name="read_more">अधिक पढ़ें</string>
<string name="chapter_is_missing">अध्याय गायब है</string>
<string name="about_app_translation_summary">इस ऐप का अनुवाद करें</string>
<string name="auth_complete">अधिकृत</string>
<string name="auth_not_supported_by">%s पर लॉगन करना समर्थित नहीं है</string>
<string name="auth_not_supported_by">%s पर लॉगिन करना समर्थित नहीं है</string>
<string name="text_clear_cookies_prompt">आप सभी स्रोतों से लॉग आउट हो जायेंगे</string>
<string name="state_finished">समाप्त</string>
<string name="state_ongoing">चल रही है</string>
<string name="system_default">डिफ़ॉल्ट</string>
<string name="system_default">तयशुदा</string>
<string name="exclude_nsfw_from_history">इतिहास से NSFW मंगा को बाहर करें</string>
<string name="show_pages_numbers">क्रमांकित पन्ने</string>
<string name="screenshots_block_nsfw">NSFW पर रोक लगाएं</string>
@@ -185,11 +185,11 @@
<string name="only_using_wifi">केवल Wi-Fi पर</string>
<string name="always">हमेशा</string>
<string name="nsfw">18+</string>
<string name="various_languages">विभिन्न भाषाए</string>
<string name="various_languages">विभिन्न भाषाए</string>
<string name="search_chapters">अध्याय खोजें</string>
<string name="percent_string_pattern">%1$s%%</string>
<string name="appearance">दिखावट</string>
<string name="preload_pages">पन्ने प्रीलोड करें</string>
<string name="preload_pages">पन्ने पहले से लोड करें</string>
<string name="edit">संपादित करें</string>
<string name="download_slowdown">धीमी गति से डाउनलोड करें</string>
<string name="edit_category">श्रेणी संपादित करें</string>
@@ -197,20 +197,20 @@
<string name="empty_favourite_categories">कोई पसंदीदा श्रेणियां नहीं</string>
<string name="logout">लॉग आउट</string>
<string name="bookmark_add">बुकमार्क जोड़ें</string>
<string name="bookmark_remove">बुकमार्क हटाए</string>
<string name="bookmarks">बुकमार्क्स</string>
<string name="bookmark_remove">बुकमार्क हटाए</string>
<string name="bookmarks">बुकमार्क</string>
<string name="bookmark_removed">बुकमार्क हटा दिया गया</string>
<string name="bookmark_added">बुकमार्क जोड़ा गया</string>
<string name="undo">पूर्ववत</string>
<string name="undo">पूर्ववत करें</string>
<string name="removed_from_history">इतिहास से हटा दिया गया</string>
<string name="detect_reader_mode">ऑटोडिटेक्ट रीडर मोड</string>
<string name="detect_reader_mode">पाठक मोड का स्वत: पता लगाएं</string>
<string name="detect_reader_mode_summary">स्वचालित रूप से पता लगाएं कि मंगा वेबटून है या नहीं</string>
<string name="disable_battery_optimization">बैटरी अनुकूलन अक्षम करें</string>
<string name="send">भेजें</string>
<string name="disable_all">सब अक्षम करें</string>
<string name="use_fingerprint">यदि उपलब्ध हो तो फिंगरप्रिंट का उपयोग करें</string>
<string name="use_fingerprint">यदि उपलब्ध हो तो फिंगरप्रिंट का उपयोग करें</string>
<string name="appwidget_shelf_description">आपके पसंदीदा में से मंगा</string>
<string name="appwidget_recent_description">आपे हाल ही में पढ़ा मंगा</string>
<string name="appwidget_recent_description">आपे हालिया पढ़ें मंगा</string>
<string name="report">रिपोर्ट</string>
<string name="status_planned">योजना बनाई</string>
<string name="status_reading">पढ़ रहा हूँ</string>
@@ -218,44 +218,44 @@
<string name="status_completed">पूरा किया हुआ</string>
<string name="status_on_hold">होल्ड पर</string>
<string name="status_dropped">गिरा दिया गया</string>
<string name="show_reading_indicators">ढ़ने की प्रगति संकेतक दिखाए</string>
<string name="show_reading_indicators">ठन प्रगति संकेतक दिखाए</string>
<string name="data_deletion">डेटा विलोपन</string>
<string name="exclude_nsfw_from_history_summary">NSFW के रूप में चिह्नित मंगा को इतिहास में कभी नहीं जोड़ा जाएगा और आपकी प्रगति सहेजी नहीं जाएगी</string>
<string name="clear_cookies_summary">कुछ समस्या होने पर मदद मिल सकती है. सभी प्राधिकरण अमान्य कर दिए जाएंगे</string>
<string name="clear_cookies_summary">कुछ समस्या होने पर मदद मिल सकती है सभी प्राधिकरण अमान्य कर दिए जाएंगे</string>
<string name="invalid_domain_message">अमान्य डोमेन</string>
<string name="select_range">रेंज चुनें</string>
<string name="clear_all_history">सारा इतिहास साफ करें</string>
<string name="select_range">दायरा चुनें</string>
<string name="clear_all_history">सारा इतिहास साफ करें</string>
<string name="last_2_hours">पिछले 2 घंटे</string>
<string name="history_cleared">इतिहास साफ हो गया</string>
<string name="history_cleared">इतिहास साफ हो गया</string>
<string name="manage">प्रबंधित करें</string>
<string name="explore">अन्वेषण करें</string>
<string name="confirm_exit">बाहर निकलने के लिए फिर से वापस दबाए</string>
<string name="confirm_exit">बाहर निकलने के लिए फिर से वापस दबाए</string>
<string name="exit_confirmation">बाहर निकलने की पुष्टि</string>
<string name="saved_manga">सहेजा गया मंगा</string>
<string name="pages_cache">पन्नो का कैश</string>
<string name="other_cache">अन्य कैश</string>
<string name="pages_cache">पन्नो का कैश</string>
<string name="other_cache">अन्य कैश</string>
<string name="available">उपलब्ध</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="removed_from_favourites">पसंदीदा से हटाया गया</string>
<string name="options">विकल्प</string>
<string name="not_found_404">सामग्री नहीं मिली या हटाई गई</string>
<string name="incognito_mode">गुप्त मोड</string>
<string name="reader_info_pattern">Ch. %1$d/%2$d Pg. %3$d/%4$d</string>
<string name="reader_info_bar">रीडर में सूचना पट्टी दिखाएं</string>
<string name="folder_with_images">छवियों वाला फोल्डर</string>
<string name="reader_info_pattern">अध्. %1$d/%2$d पृ. %3$d/%4$d</string>
<string name="reader_info_bar">पाठक में सूचना पट्टी दिखाएं</string>
<string name="folder_with_images">छवियों वाला फोल्डर</string>
<string name="import_completed">आयात पूरा हुआ</string>
<string name="history_shortcuts_summary">एप्लिकेशन आइकन पर लंबे समय तक दबाकर हालिया मंगा को उपलब्ध कराएं</string>
<string name="reader_control_ltr">एर्गोनोमिक रीडर नियंत्रण</string>
<string name="reader_control_ltr">सुविधापूर्ण पाठक नियंत्रण</string>
<string name="color_correction">रंग सुधार</string>
<string name="brightness">चमक</string>
<string name="storage_usage">स्टोरेज उपयोग</string>
<string name="contrast">कंट्रास्ट</string>
<string name="reset">रीसेट</string>
<string name="text_unsaved_changes_prompt">सहेजे न गए परिवर्तन सहेजें या हटाएँ?</string>
<string name="contrast">वैषम्य</string>
<string name="reset">रीसेट करें</string>
<string name="text_unsaved_changes_prompt">सहेजे परिवर्तन सहेजें या त्यागें?</string>
<string name="error_no_space_left">डिवाइस पर जगह समाप्त</string>
<string name="reader_slider">ेज स्विचिंग स्लाइडर दिखाए</string>
<string name="reader_slider">ृष्ठ स्विचिंग स्लाइडर दिखाए</string>
<string name="network_unavailable">नेटवर्क उपलब्ध नहीं है</string>
<string name="server_error">सर्वर साइड त्रुटि (%1$d). कृपया बाद में पुन: प्रयास करें</string>
<string name="server_error">सर्वर साइड त्रुटि (%1$d) कृपया बाद में पुन: प्रयास करें</string>
<string name="clear_new_chapters_counters">नए अध्यायों के बारे में भी स्पष्ट जानकारी</string>
<string name="compact">सघन</string>
<string name="mark_as_current">वर्तमान के रूप में चिह्नित करें</string>
@@ -264,23 +264,23 @@
<string name="show_suspicious_content">संदिग्ध सामग्री दिखाएं</string>
<string name="theme_name_dynamic">गतिशील</string>
<string name="color_theme">रंग योजना</string>
<string name="show_in_grid_view">ग्रिड दृश्य में दिखाए</string>
<string name="show_in_grid_view">ग्रिड दृश्य में दिखाए</string>
<string name="theme_name_miku">Miku</string>
<string name="theme_name_rikka">Rikka</string>
<string name="theme_name_kanade">Kanade</string>
<string name="scrobbling_empty_hint">पढ़ने की प्रगति को ट्रैक करने के लिए, मंगा विवरण स्क्रीन पर मेनू → ट्रैक का चयन करें।</string>
<string name="services">सेवाएं</string>
<string name="settings_apply_restart_required">कृपया इन परिवर्तनों को लागू करने के लिए एप्लिकेशन को पुनः आरंभ करें</string>
<string name="comics_archive_import_description">आप एक या अधिक .cbz या .zip फाइलों का चयन कर सकते हैं, प्रत्येक फाइल को एक अलग मंगा के रूप में पहचाना जाएगा।</string>
<string name="user_agent">UserAgent हेडर</string>
<string name="comics_archive_import_description">आप एक या अधिक .cbz या .zip फाइलों का चयन कर सकते हैं, प्रत्येक फाइल को एक अलग मंगा के रूप में पहचाना जाएगा।</string>
<string name="user_agent">UserAgent शीर्षलेख</string>
<string name="speed">गति</string>
<string name="show_on_shelf">शेल्फ पर दिखाए</string>
<string name="show_on_shelf">दराज पर दिखाए</string>
<string name="sync_auth_hint">आप किसी मौजूदा खाते में साइन इन कर सकते हैं या एक नया खाता बना सकते हैं</string>
<string name="mirror_switching_summary">यदि मिरर उपलब्ध हैं तो त्रुटियों पर मंगा स्रोतों के लिए स्वचालित रूप से डोमेन स्विच करें</string>
<string name="find_similar">समान खोजें</string>
<string name="pause">विराम</string>
<string name="resume">फिर से शुरू करें</string>
<string name="paused">रुका हुआ</string>
<string name="resume">पुनः आरम्भ</string>
<string name="paused">विरामित</string>
<string name="cancel_all">सभी रद्द करें</string>
<string name="mirror_switching">स्वचालित रूप से मिरर चुनें</string>
<string name="downloads_wifi_only_summary">मोबाइल नेटवर्क पर स्विच करते समय डाउनलोड करना बंद कर दें</string>
@@ -297,24 +297,24 @@
<string name="downloads_removed">डाउनलोड हटा दिए गए हैं</string>
<string name="suggestions_enable_prompt">क्या आप वैयक्तिकृत मंगा सुझाव प्राप्त करना चाहते हैं?</string>
<string name="web_view_unavailable">WebView उपलब्ध नहीं है: जांचें कि WebView प्रदाता स्थापित है या नहीं</string>
<string name="clear_network_cache">नेटवर्क कैश साफ करें</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="password">पासवर्ड</string>
<string name="invert_colors">रंगों को लटा करें</string>
<string name="invert_colors">रंगों को लटें</string>
<string name="invalid_port_number">अमान्य पोर्ट नंबर</string>
<string name="network">नेटवर्क</string>
<string name="show_pages_numbers_summary">निचले कोने में पेज संख्याए दिखाए</string>
<string name="show_pages_numbers_summary">निचले कोने में पृष्ठ संख्याए दिखाए</string>
<string name="restore_summary">पहले बनाए गए बैकअप को पुनर्स्थापित करें</string>
<string name="webtoon_zoom_summary">वेबटून मोड में जूम इन जेस्चर की अनुमति दें</string>
<string name="webtoon_zoom_summary">वेबटून मोड में जूम इन जेस्चर की अनुमति दें</string>
<string name="reader_info_bar_summary">स्क्रीन के शीर्ष पर वर्तमान समय और पढ़ने की प्रगति दिखाएं</string>
<string name="volume_">वॉल्यूम %d</string>
<string name="volume_unknown">अज्ञात वॉल्यूम</string>
<string name="downloads_settings_info">यदि आपको सर्वर-साइड ब्लॉकिंग की समस्या हो रही है तो आप स्रोत सेटिंग्स में प्रत्येक मंगा स्रोत के लिए व्यक्तिगत रूप से डाउनलोड मंदी को सक्षम कर सकते हैं</string>
<string name="remove_from_history">इतिहास से हटा दे</string>
<string name="remove_from_history">इतिहास से हटा</string>
<string name="skip">छोड़ें</string>
<string name="incognito_mode_hint">आपकी पढ़ने की प्रगति सहेजी नहीं जाएगी</string>
<string name="content_rating">सामग्री मूल्यांकन</string>
@@ -328,45 +328,45 @@
<string name="download_started">डाउनलोड प्रारंभ हुआ</string>
<string name="manga_list">मंगा सूची</string>
<string name="disable_nsfw">NSFW अक्षम करें</string>
<string name="images_proxy_title">छविया अनुकूलन प्रॉक्सी</string>
<string name="images_proxy_title">छविया अनुकूलन प्रॉक्सी</string>
<string name="data_and_privacy">डेटा और गोपनीयता</string>
<string name="email_password_enter_hint">जारी रखने के लिए अपना ईमेल और पासवर्ड डालें</string>
<string name="clear_source_cookies_summary">केवल निर्दिष्ट डोमेन के लिए कुकीज़ साफ करें। अधिकांश मामलों में प्राधिकरण अमान्य हो जाएगा</string>
<string name="clear_source_cookies_summary">केवल निर्दिष्ट डोमेन के लिए कुकीज़ साफ करें। अधिकांश मामलों में प्राधिकरण अमान्य हो जाएगा</string>
<string name="download_option_next_unread_n_chapters">अगला अपठित %s</string>
<string name="no_access_to_file">आपके पास इस फाइल या डॉयरेक्टरी तक कोई पहुंच नहीं है</string>
<string name="no_access_to_file">आपके पास इस फाइल या निर्देशिका तक कोई पहुंच नहीं है</string>
<string name="voice_search">ध्वनि खोज</string>
<string name="related_manga">संबंधित मंगा</string>
<string name="description">विवरण</string>
<string name="this_month">इस महीने</string>
<string name="background">पृष्ठभूमि</string>
<string name="local_manga_directories">स्थानीय मंगा डॉयरेक्टरी</string>
<string name="data_not_restored_text">सुनिश्चित करें कि आपने सही बैकअप फाइल का चयन किया है</string>
<string name="local_manga_directories">स्थानीय मंगा निर्देशिकाएं</string>
<string name="data_not_restored_text">सुनिश्चित करें कि आपने सही बैकअप फाइल का चयन किया है</string>
<string name="data_not_restored">डेटा पुनर्स्थापित नहीं किया गया</string>
<string name="suggestions_wifi_only_summary">मीटर्ड नेटवर्क कनेक्शन का उपयोग करके सुझावों को अपडेट न करें</string>
<string name="tracker_wifi_only_summary">मीटर्ड नेटवर्क कनेक्शन का उपयोग करके नए अध्यायों की जाच न करें</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="show">दिखाए</string>
<string name="languages">भाषाएं</string>
<string name="unknown">अज्ञात</string>
<string name="in_progress">प्रगति पर है</string>
<string name="error_corrupted_file">अमान्य डेटा लौटाया गया है या फाइल दूषित है</string>
<string name="items_limit_exceeded">कोई और आइटम नहीं जोड़ा जा सकता</string>
<string name="error_corrupted_file">अमान्य डेटा लौटाया गया है या फाइल दूषित है</string>
<string name="items_limit_exceeded">कोई और वस्तुएं नहीं जोड़ा जा सकता</string>
<string name="on_device">डिवाइस पर</string>
<string name="main_screen_sections">मुख्य स्क्रीन अनुभाग</string>
<string name="directories">डॉयरेक्टरी</string>
<string name="directories">निर्देशिकाएं</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="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="enhanced_colors_summary">बैंडिंग को कम करता है, लेकिन प्रदर्शन को प्रभावित कर सकता है</string>
<string name="enhanced_colors">32-बिट रंग मोड</string>
<string name="suggest_new_sources">ऐप अपडेट के बाद नए स्रोत सुझाएं</string>
<string name="suggest_new_sources">ऐप अद्यतन के बाद नए स्रोत सुझाएं</string>
<string name="suggest_new_sources_summary">एप्लिकेशन को अपडेट करने के बाद नए जोड़े गए स्रोतों को सक्षम करने का संकेत दें</string>
<string name="online_variant">ऑनलाइन संस्करण</string>
<string name="periodic_backups">आवधिक बैकअप</string>
@@ -388,54 +388,54 @@
<string name="source_enabled">स्रोत सक्षम</string>
<string name="sources_catalog">स्रोत कैटलॉग</string>
<string name="no_manga_sources_catalog_text">इस अनुभाग में कोई स्रोत उपलब्ध नहीं है, या यह सब पहले ही जोड़ा जा चुका होगा।
\nबने रहें</string>
\nहमारे साथ बने रहें</string>
<string name="no_manga_sources_found">आपकी क्वेरी से कोई उपलब्ध मंगा स्रोत नहीं मिला</string>
<string name="manual">मैन्युअल</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">मेमोरी खपत कम करें (beta)</string>
<string name="disable_nsfw_summary">यदि संभव हो तो NSFW स्रोतों को अक्षम करें और वयस्क मंगा को सूची से छिपाए</string>
<string name="state_paused">विरामित</string>
<string name="reader_optimize">मेमोरी खपत कम करें (बीटा)</string>
<string name="state">अवस्था</string>
<string name="error_multiple_genres_not_supported">अनेक शैलियों द्वारा फिल्टर करना इस मंगा स्रोत द्वारा समर्थित नहीं है</string>
<string name="error_multiple_genres_not_supported">अनेक शैलियों द्वारा फिल्टर करना इस मंगा स्रोत द्वारा समर्थित नहीं है</string>
<string name="error_search_not_supported">खोज इस मंगा स्रोत द्वारा समर्थित नहीं है</string>
<string name="genres_search_hint">शैली का नाम लिखना प्रारंभ करें</string>
<string name="disable_battery_optimization_summary_downloads">यदि आपको इसमें कोई समस्या है तो डाउनलोड शुरू करने में मदद मिल सकती है</string>
<string name="restore">पुनर्स्थापित करें</string>
<string name="backup_date_">बैकअप दिनांक: %s</string>
<string name="state_upcoming">आगामी</string>
<string name="by_name_reverse">नाम उलटा</string>
<string name="mark_as_completed">पूर्ण के रूप में चिह्नित करें</string>
<string name="by_name_reverse">नाम उलट दिया गय</string>
<string name="mark_as_completed">पूर्ण चिह्नित करें</string>
<string name="mark_as_completed_prompt">चयनित मंगा को पूरी तरह से पढ़ा गया के रूप में चिह्नित करें?
\n
\nचेतावनी: वर्तमान पठन प्रगति नष्ट हो जाएगी।</string>
<string name="remaining_time_pattern">%1$s %2$s</string>
<string name="show_menu">मेन्यू दिखाए</string>
<string name="show_menu">मेनू दिखाए</string>
<string name="long_tap_action">लंबे टैप पर कार्रवाई</string>
<string name="tap_action">टैप पर कार्रवाई</string>
<string name="tap_action">टैप कार्रवाई</string>
<string name="none">कोई नहीं</string>
<string name="config_reset_confirm">सेटिंग्स को डिफ़ॉल्ट मानों पर रीसेट करें? इस एक्शन को वापस नहीं किया जा सकता।</string>
<string name="use_two_pages_landscape">लैंडस्केप ओरिएंटेशन पर दो पेज लेआउट का उपयोग करें (beta)</string>
<string name="config_reset_confirm">सेटिंग्स को तयशुदा मानों पर रीसेट करें? इस कार्रवाई को वापस नहीं किया जा सकता।</string>
<string name="use_two_pages_landscape">परिदृश्य उन्मुखीकरण पर दो पृष्ठ अभिन्यास का उपयोग करें (बीटा)</string>
<string name="got_it">समझ गया</string>
<string name="default_tab">डिफ़ॉल्ट टैब</string>
<string name="default_tab">तयशुदा टैब</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_all_unread">सभी अपठित अध्याय</string>
<string name="download_option_all_unread_b">सभी अपठित अध्याय (%s)</string>
<string name="download_option_manual_selection">अध्यायों का चयन मैन्युअल रूप से करें</string>
<string name="download_option_manual_selection">अध्यायों का चयन हस्तचालित रूप से करें</string>
<string name="color_light">हल्का</string>
<string name="color_dark">गहरा</string>
<string name="color_white">सफेद</string>
<string name="color_white">सफेद</string>
<string name="color_black">काला</string>
<string name="manage_categories">श्रेणी व्यवस्थित करें</string>
<string name="downloaded">डाउनलोड किया गया</string>
<string name="too_many_requests_message">बहुत सारे अनुरोध. बाद में पुन: प्रयास</string>
<string name="related_manga_summary">संबंधित मंगा की एक सूची दिखाएं. कुछ मामलों में यह गलत या गायब हो सकता है</string>
<string name="pick_custom_directory">कस्टम डायरेक्टरी चुनें</string>
<string name="default_webtoon_zoom_out">डिफ़ॉल्ट वेबटून जूम आउट</string>
<string name="too_many_requests_message">बहुत सारे अनुरोध बाद में पुन: प्रयास</string>
<string name="related_manga_summary">संबंधित मंगा की एक सूची दिखाएं कुछ मामलों में यह गलत या गायब हो सकता है</string>
<string name="pick_custom_directory">तदनुकूल डायरेक्टरी चुनें</string>
<string name="default_webtoon_zoom_out">तयशुदा वेबटून जूम आउट</string>
<string name="captcha_required_summary">%s को ठीक से काम करने के लिए कैप्चा को हल करने की आवश्यकता है</string>
<string name="fullscreen_mode">पूर्ण स्क्रीन मोड</string>
<string name="reader_fullscreen_summary">सिस्टम स्थिति और नेविगेशन बार छिपाए</string>
<string name="fullscreen_mode">पूर्णस्क्रीन मोड</string>
<string name="reader_fullscreen_summary">सिस्टम स्थिति और नेविगेशन बार छिपाए</string>
<string name="username">उपयोक्तानाम</string>
<string name="authorization_optional">प्राधिकरण (वैकल्पिक)</string>
<string name="category_hidden_done">यह श्रेणी मुख्य स्क्रीन से छिपी हुई थी और मेनू → श्रेणियों को प्रबंधित करें के माध्यम से पहुंच योग्य है</string>
@@ -444,192 +444,197 @@
<string name="apply">लागू करें</string>
<string name="ignore_ssl_errors">SSL त्रुटियों को नजरअंदाज करें</string>
<string name="downloads_wifi_only">केवल Wi-Fi के ज़रिए डाउनलोड करें</string>
<string name="show_notification_new_chapters_off">आपको सूचनाएं प्राप्त नहीं होंगी लेकिन नए अध्याय सूचियों में हाइलाइट किए जाएंगे</string>
<string name="show_notification_new_chapters_off">आपको सूचनाएं प्राप्त नहीं होंगी लेकिन नए अध्याय सूचियों में चिन्हांकित किए जाएंगे</string>
<string name="notifications_enable">सूचनाएं सक्षम करें</string>
<string name="name">नाम</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="reorder">पुन: व्यवस्थित करें</string>
<string name="reorder">पुन क्रमित करें</string>
<string name="empty">खाली</string>
<string name="import_will_start_soon">आयात जल्द शुरू होगा</string>
<string name="feed">ीड</string>
<string name="history_shortcuts">हाल के मंगा शॉर्टकट दिखाए</string>
<string name="discard">खारिज</string>
<string name="sources_reorder_tip">किसी आइटम को पुन: व्यवस्थित करने के लिए उस पर टैप करके रखें</string>
<string name="feed">फीड</string>
<string name="history_shortcuts">हाल के मंगा शॉर्टकट दिखाए</string>
<string name="discard">त्यागें</string>
<string name="sources_reorder_tip">किसी वस्तु को पुन: व्यवस्थित करने के लिए उस पर टैप करके रखें</string>
<string name="wrong_password">गलत पासवर्ड</string>
<string name="protect_application">ऐप को सुरक्षित रखें</string>
<string name="protect_application_summary">Kotatsu शुरू करते समय पासवर्ड मांगें</string>
<string name="right_to_left">दाएं-से-बाएं</string>
<string name="create_category">नई श्रेणी</string>
<string name="zoom_mode_fit_center">केंद्र फिट</string>
<string name="zoom_mode_fit_center">केंद्र के अनुरूप</string>
<string name="zoom_mode_fit_height">ऊंचाई के अनुरूप</string>
<string name="zoom_mode_fit_width">चौड़ाई के अनुरूप</string>
<string name="zoom_mode_keep_start">प्रारंभ में रखें</string>
<string name="clear_cookies">कीज साफ करें</string>
<string name="clear_feed">ीड साफ करें</string>
<string name="text_clear_updates_feed_prompt">सभी अपडेट इतिहास स्थायी रूप से साफ करें?</string>
<string name="check_for_new_chapters">नए अध्यायों की जाच करें</string>
<string name="clear_cookies">कीज साफ करें</string>
<string name="clear_feed">फीड साफ करें</string>
<string name="text_clear_updates_feed_prompt">सभी अद्यतन इतिहास स्थायी रूप से साफ करें?</string>
<string name="check_for_new_chapters">नए अध्यायों की जाच करें</string>
<string name="reverse">उलटा</string>
<string name="sign_in">साइन इन</string>
<string name="canceled">रद्द किया गया</string>
<string name="account_already_exists">खाता पहले से मौजूद है</string>
<string name="back">पीछे</string>
<string name="sync">िंक्रनाइज़ेश</string>
<string name="sync_title">अपना डेटा सिंक करें</string>
<string name="sync">मन्वय</string>
<string name="sync_title">अपना डेटा समन्वयित करें</string>
<string name="email_enter_hint">जारी रखने के लिए अपना ईमेल दर्ज करें</string>
<string name="hide">छुपाएं</string>
<string name="new_sources_text">नए मंगा स्रोत उपलब्ध हैं</string>
<string name="check_new_chapters_title">नए अध्यायों की जाच करें और इसके बारे में सूचित करें</string>
<string name="check_new_chapters_title">नए अध्यायों की जाच करें और इसके बारे में सूचित करें</string>
<string name="remove_completed">पूर्ण हटा दें</string>
<string name="toggle_ui">UI दिखाए/छिपाए</string>
<string name="toggle_ui">UI दिखाए/छिपाए</string>
<string name="next_chapter">अगला अध्याय</string>
<string name="reader_actions">पाठक क्रियाएँ</string>
<string name="reader_actions">पाठक कार्रवाई</string>
<string name="switch_pages_volume_buttons">वॉल्यूम बटन सक्षम करें</string>
<string name="next_page">अगला पेज</string>
<string name="reading_time_estimation">पढ़ने का अनुमानित समय दिखाए</string>
<string name="reading_time_estimation_summary">समय अनुमान मान गलत हो सकता है</string>
<string name="location">जगह</string>
<string name="next_page">अगला पृष्ठ</string>
<string name="reading_time_estimation">पढ़ने का अनुमानित समय दिखाए</string>
<string name="reading_time_estimation_summary">समय अनुमान मान गलत हो सकता है</string>
<string name="location">स्थान</string>
<string name="queued">कतारबद्ध</string>
<string name="about_app_translation">अनुवाद</string>
<string name="enabled">सक्रिय</string>
<string name="enabled">सक्षम</string>
<string name="auth_required">इस सामग्री को देखने के लिए साइन इन करें</string>
<string name="default_s">डिफ़ॉल्ट: %s</string>
<string name="default_s">तयशुदा: %s</string>
<string name="next">अगला</string>
<string name="genres">शैलियां</string>
<string name="logged_in_as">%s के रूप में लॉगन किया गया</string>
<string name="protect_application_subtitle">ऐप शुरू करने के लिए एक पासवर्ड दर्ज करें</string>
<string name="logged_in_as">%s के रूप में लॉगिन किया गया</string>
<string name="protect_application_subtitle">ऐप शुरू करने के लिए पासवर्ड दर्ज करें</string>
<string name="suggestions_updating">सुझाव अपडेट हो रहे हैं</string>
<string name="confirm">पुष्टि करें</string>
<string name="suggestions_excluded_genres_summary">वे शैलिया निर्दिष्ट करें जिन्हें आप सुझावों में नहीं देखना चाहते</string>
<string name="password_length_hint">पासवर्ड 4 अक्षर या अधिक का होना चाहिए</string>
<string name="text_delete_local_manga_batch">डिवाइस से चयनित आइटम स्थायी रूप से टाएं?</string>
<string name="suggestions_excluded_genres_summary">वे शैलिया निर्दिष्ट करें जिन्हें आप सुझावों में नहीं देखना चाहते</string>
<string name="password_length_hint">पासवर्ड 4 वर्णों या अधिक का होना चाहिए</string>
<string name="text_delete_local_manga_batch">डिवाइस से चयनित वस्तुएं स्थायी रूप से मिटाएं?</string>
<string name="text_clear_search_history_prompt">हाल की सभी खोज क्वेरी को स्थायी रूप से हटा दें?</string>
<string name="about">बारे में</string>
<string name="about">ऐप के बारे में</string>
<string name="backup_restore">बैकअप और पुनर्स्थापना</string>
<string name="preparing_">तैयार कर रहे हैं…</string>
<string name="file_not_found">फाइल नहीं मिली</string>
<string name="data_restored_success">सारा डेटा पुनर्स्थापित कर दिया गया</string>
<string name="backup_information">आप अपने इतिहास और पसंदीदा का बैकअप बना सकते हैं और उसे पुनर्स्थापित कर सकते हैं</string>
<string name="show_notification_new_chapters_on">आप जो मंगा पढ़ रहे हैं उसके अपडेट के बारे में आपको सूचनाएं प्राप्त होंगी</string>
<string name="show_notification_new_chapters_on">आप जो मंगा पढ़ रहे हैं उसके अद्यतन के बारे में आपको सूचनाएं प्राप्त होंगी</string>
<string name="dns_over_https">HTTPS पर DNS</string>
<string name="default_mode">डिफ़ॉल्ट मोड</string>
<string name="disable_battery_optimization_summary">बैकग्राउंड अपडेट जांच में मदद करता है</string>
<string name="default_mode">तयशुदा मोड</string>
<string name="disable_battery_optimization_summary">बैकग्राउंड अद्यतन जांच में मदद करता है</string>
<string name="crash_text">कुछ गलत हो गया। कृपया इसे ठीक करने में हमारी सहायता के लिए डेवलपर्स को एक बग रिपोर्ट सबमिट करें।</string>
<string name="show_reading_indicators_summary">इतिहास और पसंदीदा में पढ़ा गया प्रतिशत दिखाए</string>
<string name="show_reading_indicators_summary">इतिहास और पसंदीदा में पढ़ा गया प्रतिशत दिखाए</string>
<string name="show_all">सब दिखाएं</string>
<string name="downloads_cancelled">डाउनलोड रद्द कर दिए गए हैं</string>
<string name="no_bookmarks_yet">अभी तक कोई बुकमार्क नहीं</string>
<string name="no_bookmarks_summary">आप मंगा पढ़ते समय बुकमार्क बना सकते हैं</string>
<string name="sync_settings">िंक्रोनाइज़ेशन सेटिंग्स</string>
<string name="exit_confirmation_summary">ऐप से बाहर निकलने के लिए बैक को दो बार दबाए</string>
<string name="sync_settings">मन्वयन सेटिंग्स</string>
<string name="exit_confirmation_summary">ऐप से बाहर निकलने के लिए \"पीछे\" को दो बार दबाए</string>
<string name="server_address">सर्वर पता</string>
<string name="sync_host_description">आप स्व-होस्टेड सिंक्रनाइज़ेशन सर्वर या डिफ़ॉल्ट सर्वर का उपयोग कर सकते हैं। यदि आप निश्चित नहीं हैं कि आप क्या कर रहे हैं तो इसे न बदलें।</string>
<string name="sync_host_description">आप स्वयं-होस्ट किये गये समन्वयन सर्वर या तयशुदा सर्वर का उपयोग कर सकते हैं। यदि आप निश्चित नहीं हैं कि आप क्या कर रहे हैं तो इसे न बदलें।</string>
<string name="no_chapters">कोई अध्याय नहीं</string>
<string name="automatic_scroll">स्वचालित स्क्रॉल</string>
<string name="importing_manga">मंगा आयात किया जा रहा है</string>
<string name="import_completed_hint">स्थान बचाने के लिए आप मूल फाइल को स्टोरेज से हटा सकते हैं</string>
<string name="import_completed_hint">स्थान बचाने के लिए आप मूल फाइल को स्टोरेज से हटा सकते हैं</string>
<string name="network_unavailable_hint">मंगा को ऑनलाइन पढ़ने के लिए Wi-Fi या मोबाइल नेटवर्क चालू करें</string>
<string name="source_disabled">स्रोत अक्षम किया गया</string>
<string name="prefetch_content">सामग्री प्रीलोड हो रही है</string>
<string name="share_logs">लॉग साझा करें</string>
<string name="enable_logging_summary">डिबग उद्देश्यों के लिए कुछ क्रियाए रिकॉर्ड करें। यदि आप निश्चित नहीं हैं कि आप क्या कर रहे हैं तो इसे चालू न करें</string>
<string name="enable_logging_summary">डिबग उद्देश्यों के लिए कुछ क्रियाए रिकॉर्ड करें। यदि आप निश्चित नहीं हैं कि आप क्या कर रहे हैं तो इसे चालू न करें</string>
<string name="theme_name_asuka">Asuka</string>
<string name="theme_name_mion">Mion</string>
<string name="theme_name_sakura">Sakura</string>
<string name="theme_name_mamimi">Mamimi</string>
<string name="allow_unstable_updates">अस्थिर अपडेट की अनुमति दें</string>
<string name="allow_unstable_updates">अस्थिर अद्यतन की अनुमति दें</string>
<string name="nothing_here">यहां कुछ नहीं है</string>
<string name="allow_unstable_updates_summary">अस्थिर बिल्ड के बारे में सूचनाएं प्राप्त करें</string>
<string name="categories_delete_confirm">क्या आप वाकई चयनित पसंदीदा श्रेणियां टाना चाहते हैं?
<string name="categories_delete_confirm">क्या आप वाकई चयनित पसंदीदा श्रेणियां मिटाना चाहते हैं?
\nइसमें मौजूद सारा मंगा नष्ट हो जाएगा और इसे पूर्ववत नहीं किया जा सकता।</string>
<string name="manga_error_description_pattern">त्रुटि विवरण:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. यह सुनिश्चित करने के लिए कि यह अपने स्रोत पर उपलब्ध है &lt;a href=%2$s&gt;मंगा को वेब ब्राउज़र में खोलने का प्रयास करें&lt;/a&gt;&lt;br&gt;2। सुनिश्चित करें कि आप &lt;a href=kotatsu://about&gt;Kotatsu के नवीनतम संस्करण&lt;/a&gt;&lt;br&gt;3 का उपयोग कर रहे हैं। यदि यह उपलब्ध है, तो डेवलपर्स को एक त्रुटि रिपोर्ट भेजें।</string>
<string name="folder_with_images_import_description">आप अभिलेखों या छवियों वाली एक डॉयरेक्टरी का चयन कर सकते हैं। प्रत्येक संग्रह (या उपडॉयरेक्टरी) को एक अध्याय के रूप में पहचाना जाएगा।</string>
<string name="images_procy_description">यदि संभव हो तो ट्रैफिक उपयोग को कम करने और छवि लोडिंग को तेज़ करने के लिए wsrv.nl सेवा का उपयोग करें</string>
<string name="manga_error_description_pattern">त्रुटि विवरण:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1 यह सुनिश्चित करने के लिए कि यह अपने स्रोत पर उपलब्ध है &lt;a href=%2$s&gt;मंगा को वेब ब्राउज़र में खोलने का प्रयास करें&lt;/a&gt;&lt;br&gt;2। सुनिश्चित करें कि आप &lt;a href=kotatsu://about&gt;Kotatsu के नवीनतम संस्करण&lt;/a&gt;&lt;br&gt;3 का उपयोग कर रहे हैं। यदि यह उपलब्ध है, तो डेवलपर्स को एक त्रुटि रिपोर्ट भेजें।</string>
<string name="folder_with_images_import_description">आप अभिलेखों या छवियों वाली एक निर्देशिका का चयन कर सकते हैं। प्रत्येक संग्रह (या उपनिर्देशिका) को एक अध्याय के रूप में पहचाना जाएगा।</string>
<string name="images_procy_description">यदि संभव हो तो ट्रैफिक उपयोग को कम करने और छवि लोडिंग को तेज़ करने के लिए wsrv.nl सेवा का उपयोग करें</string>
<string name="state_abandoned">गिरा दिया गया</string>
<string name="list_options">विकल्पों की सूची बनाएं</string>
<string name="by_relevance">प्रासंगिकता</string>
<string name="categories">श्रेणिया</string>
<string name="reader_optimize_summary">कम मेमोरी का उपयोग करने के लिए ऑफस्क्रीन पन्नो की गुणवत्ता कम करें</string>
<string name="error_multiple_states_not_supported">एकाधिक राज्यों द्वारा फिल्टर करना इस मंगा स्रोत द्वारा समर्थित नहीं है</string>
<string name="color_correction_apply_text">ये सेटिंग्स विश्व स्तर पर या केवल वर्तमान मंगा पर लागू की जा सकती हैं। यदि विश्व स्तर पर लागू किया जाता है, तो व्यक्तिगत सेटिंग्स को ओवरराइड नहीं किया जाएगा।</string>
<string name="categories">श्रेणिया</string>
<string name="reader_optimize_summary">कम मेमोरी का उपयोग करने के लिए छुपे पन्नो की गुणवत्ता कम करें</string>
<string name="error_multiple_states_not_supported">एकाधिक राज्यों द्वारा फिल्टर करना इस मंगा स्रोत द्वारा समर्थित नहीं है</string>
<string name="color_correction_apply_text">ये सेटिंग्स विश्व स्तर पर या केवल वर्तमान मंगा पर लागू की जा सकती हैं। यदि विश्व स्तर पर लागू किया जाता है, तो व्यक्तिगत सेटिंग्स को अध्यारोहण नहीं किया जाएगा।</string>
<string name="this_manga">यह मंगा</string>
<string name="error_filter_locale_genre_not_supported">शैलियों और स्थान दोनों के आधार पर फिल्टर करना इस स्रोत द्वारा समर्थित नहीं है</string>
<string name="welcome_text">कृपया चुनें कि आप कौन से सामग्री स्रोत सक्षम करना चाहते हैं। इसे बाद में सेटिंग्स में भी कॉन्फ़िगर किया जा सकता है</string>
<string name="sync_auth">खाता सिंक करने के लिए लॉगिन करें</string>
<string name="error_filter_states_genre_not_supported">शैलियों और राज्यों दोनों द्वारा फिल्टर करना इस स्रोत द्वारा समर्थित नहीं है</string>
<string name="error_filter_locale_genre_not_supported">शैलियों और स्थान दोनों के आधार पर फिल्टर करना इस स्रोत द्वारा समर्थित नहीं है</string>
<string name="welcome_text">कृपया चुनें कि आप कौन से सामग्री स्रोत सक्षम करना चाहते हैं। इसे बाद में सेटिंग्स में भी विन्यस्त किया जा सकता है</string>
<string name="sync_auth">खाता समन्वयन करने के लिए लॉगिन करें</string>
<string name="error_filter_states_genre_not_supported">शैलियों और राज्यों दोनों द्वारा फिल्टर करना इस स्रोत द्वारा समर्थित नहीं है</string>
<string name="prev_chapter">पिछला अध्याय</string>
<string name="default_page_save_dir">डिफॉल्ट पेज सेव डायरेक्टरी</string>
<string name="reader_actions_summary">टैप करने योग्य स्क्रीन क्षेत्रों के लिए क्रियाएँ कॉन्फ़िगर करें</string>
<string name="reader_control_ltr_summary">दाए किनारे पर टैप करने या दाए कुंजी दबाने से हमेशा अगले पेज पर स्विच हो जाता है।</string>
<string name="prev_page">पिछला पेज</string>
<string name="default_page_save_dir">तयशुदा पृष्ठ सहेजने की डायरेक्टरी</string>
<string name="reader_actions_summary">टैप करने योग्य स्क्रीन क्षेत्रों के लिए कार्रवाइयां विन्यस्त करें</string>
<string name="reader_control_ltr_summary">दाए किनारे पर टैप करने या दाए कुंजी दबाने से हमेशा अगले पृष्ठ पर स्विच हो जाता है।</string>
<string name="prev_page">पिछला पृष्ठ</string>
<string name="switch_pages_volume_buttons_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="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="preferred_download_format">वरीय डाउनलोड प्रारूप</string>
<string name="multiple_cbz_files">अनेक CBZ फाइलें</string>
<string name="automatic">ऑटोमैटिक</string>
<string name="single_cbz_file">एकल CBZ फाइल</string>
<string name="reading_stats">पठन आकड़े</string>
<string name="stats_cleared">आंकड़े साफ हो गए</string>
<string name="multiple_cbz_files">अनेक CBZ फाइलें</string>
<string name="automatic">स्वचालित</string>
<string name="single_cbz_file">एकल CBZ फाइल</string>
<string name="reading_stats">पठन आकड़े</string>
<string name="stats_cleared">आंकड़े साफ हो गए</string>
<string name="week">हफ्ता</string>
<string name="all_time">पूरा समय</string>
<string name="other_manga">अन्य मंगा</string>
<string name="less_than_minute">एक मिनट से कम</string>
<string name="statistics">आंकड़े</string>
<string name="clear_stats_confirm">क्या आप सचमुच सभी पठन आकड़े साफ करना चाहते हैं? इस क्रिया को पूर्ववत नहीं किया जा सकता है।</string>
<string name="clear_stats_confirm">क्या आप सचमुच सभी पठन आकड़े साफ करना चाहते हैं? इस क्रिया को पूर्ववत नहीं किया जा सकता है।</string>
<string name="three_months">तीन महीने</string>
<string name="clear_stats">कड़े साफ करें</string>
<string name="clear_stats">कड़े साफ करें</string>
<string name="month">महीना</string>
<string name="day">दिन</string>
<string name="empty_stats_text">चयनित अवधि के लिए कोई आकड़े नहीं हैं</string>
<string name="pages_read_s">ेज पढ़ें गए: %s</string>
<string name="manga_migration">मंगा माइग्रेश</string>
<string name="migration_completed">माइग्रेशन पूरा हुआ</string>
<string name="migrate">माइग्रेश</string>
<string name="empty_stats_text">चयनित अवधि के लिए कोई आकड़े नहीं हैं</string>
<string name="pages_read_s">ठित पृष्ठ: %s</string>
<string name="manga_migration">मंगा प्रवास</string>
<string name="migration_completed">प्रवासन पूरा हुआ</string>
<string name="migrate">प्रवास</string>
<string name="alternatives">वैकल्पिक</string>
<string name="migrate_confirmation">आपके इतिहास और पसंदीदा में \"%2$s\" से मंगा \"%1$s\" को \"%4$s\" से \"%3$s\" से बदल दिया जाएगा (यदि मौजूद है)</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="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="chapters_grid_view">ग्रिड दृश्य</string>
<string name="split_by_translations">अनुवाद द्वारा विभाजित</string>
<string name="split_by_translations_summary">विभिन्न अनुवादों वाले अध्यायों को एक सूची के बजाय अलग-अलग दिखाए</string>
<string name="split_by_translations_summary">विभिन्न अनुवादों वाले अध्यायों को एक सूची के बजाय अलग-अलग दिखाए</string>
<string name="order_oldest">सबसे पुराना</string>
<string name="long_ago_read">बहुत पहले पढ़ा था</string>
<string name="unread">अपठित</string>
<string name="unsupported_source">यह मंगा स्रोत समर्थित नहीं है</string>
<string name="enable_source">स्रोत सक्षम करें</string>
<string name="show_pages_thumbs_summary">विवरण स्क्रीन पर \"पेज\" टैब सक्षम करें</string>
<string name="show_pages_thumbs">ेज थंबनेल दिखाए</string>
<string name="show_pages_thumbs_summary">विवरण स्क्रीन पर \"पृष्ठ\" टैब सक्षम करें</string>
<string name="show_pages_thumbs">ृष्ठ थंबनेल दिखाए</string>
<string name="error_no_data_received">सर्वर से कोई डेटा प्राप्त नहीं हुआ</string>
<string name="unsupported_backup_message">कृपया उचित कोटात्सु बैकअप फाइल चुनें</string>
<string name="unsupported_backup_message">कृपया उचित Kotatsu बैकअप फाइल चुनें</string>
<string name="hours_short">%d घं</string>
<string name="minutes_short">%d मि</string>
<string name="hours_minutes_short">%1$d घं %2$d मि</string>
<string name="fix">ठीक करें</string>
<string name="missing_storage_permission">बाहरी स्टोरेज पर मंगा तक पहुंचने की कोई अनुमति नहीं है</string>
<string name="last_used">अंतिम प्रयोग</string>
<string name="show_updated">अद्यत दिखाए</string>
<string name="show_updated">अद्यतित दिखाए</string>
<string name="webtoon_gaps">वेबटून मोड में अंतराल</string>
<string name="webtoon_gaps_summary">वेबटून मोड में पृष्ठों के बीच लंबवत अंतराल दिखाएं</string>
<string name="less_frequently">कम बार</string>
<string name="more_frequently">अधिक बार</string>
<string name="frequency_of_check">जांच की आवृत्ति</string>
<string name="new_chapters_pattern">%1$s: %2$d</string>
<string name="pin_navigation_ui">पिन नेविगेशन UI</string>
<string name="pin_navigation_ui">नेविगेशन UI पिन करें</string>
<string name="pin_navigation_ui_summary">स्क्रॉल पर नेविगेशन बार और खोज दृश्य को न छिपाएं</string>
<string name="search_suggestions">सुझाव खोजें</string>
<string name="recent_queries">हाल के प्रश्न</string>
<string name="suggested_queries">सुझाए गए प्रश्न</string>
<string name="authors">लेखक</string>
<string name="blocked_by_server_message">आपको सर्वर द्वारा ब्लॉक कर दिया गया है. किसी भिन्न नेटवर्क कनेक्शन (VPN, प्रॉक्सी, आदि) का उपयोग करने का प्रयास करें</string>
</resources>
<string name="authors">रचयिता</string>
<string name="blocked_by_server_message">आपको सर्वर द्वारा अवरुद्ध कर दिया गया है किसी भिन्न नेटवर्क कनेक्शन (VPN, प्रॉक्सी, आदि) का उपयोग करने का प्रयास करें</string>
<string name="disable">अक्षम करें</string>
<string name="sources_disabled">स्रोत अक्षम</string>
<string name="disable_connectivity_check">कनेक्टिविटी जांच अक्षम करें</string>
<string name="ignore_ssl_errors_summary">यदि नेटवर्क संसाधनों तक पहुँचने के दौरान आपको SSL से संबंधित समस्याओं का सामना करना पड़ता है तो आप SSL प्रमाणपत्र सत्यापन को अक्षम कर सकते हैं। इससे आपकी सुरक्षा प्रभावित हो सकती है। इस सेटिंग को बदलने के बाद एप्लिकेशन को पुनरारंभ करना आवश्यक है।</string>
<string name="disable_connectivity_check_summary">यदि आपको कनेक्टिविटी से जुड़ी कोई समस्या है तो कनेक्टिविटी जांच को छोड़ दें (उदाहरण के लिए नेटवर्क कनेक्ट होने के बावजूद ऑफ़लाइन मोड में जाना)</string>
</resources>

View File

@@ -614,4 +614,4 @@
<string name="runs_on_app_start">Futtatás amikor az alkalmazás elindul</string>
<string name="enable_source">Forrás engedélyezése</string>
<string name="unsupported_source">Ez a manga forrás nem támogatott</string>
</resources>
</resources>

View File

@@ -307,7 +307,7 @@
<string name="other_cache">Altra cache</string>
<string name="storage_usage">Utilizzo dello spazio di archiviazione</string>
<string name="available">Disponibile</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="removed_from_favourites">Rimosso dai preferiti</string>
<string name="options">Opzioni</string>
<string name="no_chapters">Nessun capitolo</string>
@@ -318,7 +318,7 @@
<string name="importing_manga">Importazione di manga</string>
<string name="import_will_start_soon">L\'importazione inizierà presto</string>
<string name="feed">Flusso</string>
<string name="reader_control_ltr_summary">Toccando il bordo destro o premendo il tasto destro si passa sempre alla pagina successiva</string>
<string name="reader_control_ltr_summary">Toccando il bordo destro o premendo il tasto destro si passa sempre alla pagina successiva.</string>
<string name="contrast">Contrasto</string>
<string name="reset">Ripristina</string>
<string name="reader_slider">Mostra il cursore di cambio pagina</string>
@@ -538,4 +538,59 @@
<string name="volume_">Volume %d</string>
<string name="category_hidden_done">Questa categoria è stata nascosta dalla schermata principale ed è accessibile tramite Menù → Gestisci categorie</string>
<string name="remaining_time_pattern">%1$s %2$s</string>
</resources>
<string name="last_used">Ultimo utilizzo</string>
<string name="incognito_mode_hint">I tuoi progressi di lettura non saranno salvati</string>
<string name="vertical">Verticale</string>
<string name="last_read">Ultima lettura</string>
<string name="reading_time_estimation_summary">Il tempo di lettura stimato potrebbe essere inaccurato</string>
<string name="reading_time_estimation">Mostra il tempo di lettura stimato</string>
<string name="email_password_enter_hint">Inserisci la tua email e password per continuare</string>
<string name="show_menu">Mostra menu</string>
<string name="switch_pages_volume_buttons_summary">Usa i pulsanti del volume per cambiare pagine</string>
<string name="tap_action">Azioni tap</string>
<string name="long_tap_action">Azioni tap prolungato</string>
<string name="none">Nessuno</string>
<string name="fullscreen_mode">Modalità schermo interno</string>
<string name="chapters_grid_view">Vista griglia</string>
<string name="prev_chapter">Capitolo precedente</string>
<string name="reader_actions_summary">Configura le azioni per le aree di schermo cliccabili</string>
<string name="suggestions_unavailable_text">La funzione di suggerimento è disabilitata</string>
<string name="show_labels_in_navbar">Mostra le etichette nella barra di navigazione</string>
<string name="delete_read_chapters_summary">Rimuovi i capitoli già letti dalla memoria locale per liberare spazio</string>
<string name="delete_read_chapters_prompt">Questo eliminerà in modo permanente tutti i capitoli segnati come già letti dalla memoria locale. Puoi scaricarli di nuovo, ma i capitoli importati potrebbero essere persi per sempre</string>
<string name="order_oldest">Meno recenti</string>
<string name="long_ago_read">Letto molto tempo fa</string>
<string name="unsupported_source">Questa fonte manga non è supportata</string>
<string name="hours_short">%d ore</string>
<string name="minutes_short">%d m</string>
<string name="show_updated">Mostra aggiornato</string>
<string name="toggle_ui">Mostra/nascondi UI</string>
<string name="next_chapter">Prossimo capitolo</string>
<string name="prev_page">Pagina precedente</string>
<string name="next_page">Prossima pagina</string>
<string name="reader_actions">Azioni del lettore</string>
<string name="switch_pages_volume_buttons">Abilita pulsanti del volume</string>
<string name="reader_fullscreen_summary">Nascondi le barre di stato e di notifica</string>
<string name="check_for_new_chapters_disabled">Il controllo di nuovi capitoli è disabilitato</string>
<string name="delete_read_chapters_auto">Elimina i capitoli già letti automaticamente</string>
<string name="runs_on_app_start">Esegui quando l\'applicazione viene avviata</string>
<string name="split_by_translations">Dividi per traduzioni</string>
<string name="split_by_translations_summary">Mostra separatamente i capitoli con diverse traduzioni, invece che in un\'unica lista</string>
<string name="unread">Non letto</string>
<string name="show_pages_thumbs">Mostra le anteprime delle pagine</string>
<string name="show_pages_thumbs_summary">Abilita la tab \"Pagine\" nella schermata di dettaglio</string>
<string name="unsupported_backup_message">Si prega di selezione un file backup di Kotatsu corretto</string>
<string name="hours_minutes_short">%1$d h %2$d m</string>
<string name="fix">Aggiustamenti</string>
<string name="missing_storage_permission">Non sono presenti i permessi per accedere al manga nella memoria esterna</string>
<string name="webtoon_gaps">Gap in modalità webtoon</string>
<string name="webtoon_gaps_summary">Mostra gap verticali tra le pagine in modalità webotoon</string>
<string name="config_reset_confirm">Ripristinare le impostazioni ai valori predefiniti? Questo procedimento è irreversibile.</string>
<string name="use_two_pages_landscape">Usa il layout a due pagine con l\'orientamento orizzontale (beta)</string>
<string name="error_no_data_received">Non è stato ricevuto nessun dato dal server</string>
<string name="enable_source">Attiva fonte</string>
<string name="less_frequently">Meno frequente</string>
<string name="more_frequently">Più frequente</string>
<string name="frequency_of_check">Frequenza di controllo</string>
<string name="new_chapters_pattern">%1$s: %2$d</string>
</resources>

View File

@@ -462,4 +462,4 @@
<string name="reader_zoom_buttons_summary">右下にズームコントロールボタンを表示するかどうか</string>
<string name="reader_zoom_buttons">ズームボタンを表示</string>
<string name="zoom_out">ズームアウト</string>
</resources>
</resources>

View File

@@ -514,4 +514,4 @@
<string name="this_manga">Бұл маңга</string>
<string name="error_filter_locale_genre_not_supported">Бұл дереккөзде жанр бойынша және локал файлдар бойынша сүзуге болмайды</string>
<string name="error_multiple_genres_not_supported">Бұл дереккөзде бірнеше жанр бойынша сүзуге болмайды</string>
</resources>
</resources>

View File

@@ -337,4 +337,4 @@
<string name="reset">초기화</string>
<string name="text_unsaved_changes_prompt">저장되지 않은 변경 사항을 저장하거나 삭제하시겠습니까\?</string>
<string name="manga_error_description_pattern">오류 세부정보:&lt;br&gt;&lt;tt&gt;%1$s&lt;/tt&gt;&lt;br&gt;&lt;br&gt;1. &lt;a href=%2$s&gt;웹 브라우저에서 만화를 열어&lt;/a&gt; 소스에서 사용할 수 있는지 확인하세요&lt;br&gt;2. &lt;a href=kotatsu://about&gt;최신 버전의 Kotatsu&lt;/a&gt;&lt;br&gt;를 사용하고 있는지 확인하세요.3. 사용 가능한 경우 개발자에게 오류 보고서를 보냅니다.</string>
</resources>
</resources>

View File

@@ -303,4 +303,4 @@
<string name="error_corrupted_file">Data tidak sah dikembalikan atau fail rosak</string>
<string name="on_device">Pada peranti</string>
<string name="directories">Panduan</string>
</resources>
</resources>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="new_chapters">
<item quantity="other">%1$d စာမျက်နှာအသစ်</item>
</plurals>
<plurals name="chapters">
<item quantity="other">%1$d စာမျက်နှာ</item>
</plurals>
<plurals name="minutes_ago">
<item quantity="other">%1$d မိနစ်အကြာက</item>
</plurals>
<plurals name="hours_ago">
<item quantity="other">%1$d နာရီအကြာက</item>
</plurals>
<plurals name="items">
<item quantity="other">%1$d အချက်အလက်</item>
</plurals>
<plurals name="days_ago">
<item quantity="other">%1$d ရက်အကြာက</item>
</plurals>
<plurals name="months_ago">
<item quantity="other">%1$d လအကြာက</item>
</plurals>
<plurals name="hours">
<item quantity="other">%1$d နာရီ</item>
</plurals>
<plurals name="minutes">
<item quantity="other">%1$d မိနစ်</item>
</plurals>
</resources>

View File

@@ -387,4 +387,4 @@
<string name="downloads_paused">Stansa hentingane</string>
<string name="sync_auth_hint">Du kan logge inn på ein konto du alt har, eller lage ein ny ein</string>
<string name="invalid_value_message">Ugild verdi</string>
</resources>
</resources>

View File

@@ -236,7 +236,7 @@
<string name="invalid_domain_message">Nieważna domena</string>
<string name="reorder">Zmień kolejność</string>
<string name="exit_confirmation">Potwierdzenie wyjścia</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="reader_info_pattern">Rozdz. %1$d/%2$d Str. %3$d/%4$d</string>
<string name="network_unavailable_hint">Włącz Wi-Fi lub sieć komórkową, aby czytać mangę online</string>
<string name="_import">Importuj</string>
@@ -632,4 +632,9 @@
<string name="pin_navigation_ui_summary">Nie ukrywaj paska nawigacji i paska wyszukiwania podczas przewijania</string>
<string name="search_suggestions">Sugestie wyszukiwania</string>
<string name="blocked_by_server_message">Jesteś zablokowany przez serwer. Spróbuj użyć innego połączenia sieciowego (VPN, proxy itp.)</string>
<string name="sources_disabled">Źródła wyłączone</string>
<string name="disable">Wyłączone</string>
<string name="disable_connectivity_check">Wyłącz sprawdzanie łączności</string>
<string name="ignore_ssl_errors_summary">Możesz wyłączyć weryfikację certyfikatów SSL w przypadku wystąpienia problemów związanych z SSL podczas uzyskiwania dostępu do zasobów sieciowych. Może to mieć wpływ na Twoje bezpieczeństwo. Po zmianie tego ustawienia wymagane jest ponowne uruchomienie aplikacji.</string>
<string name="disable_connectivity_check_summary">Pomiń sprawdzanie łączności w przypadku problemów z nią (np. przejście do trybu offline, mimo że sieć jest podłączona)</string>
</resources>

View File

@@ -308,7 +308,7 @@
<string name="reader_info_pattern">Cap. %1$d/%2$d Pág. %3$d/%4$d</string>
<string name="reader_info_bar">Mostrar barra de informações no leitor</string>
<string name="folder_with_images">Pasta com imagens</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="comics_archive">Arquivo de quadrinhos</string>
<string name="importing_manga">Importando mangá(s)</string>
<string name="import_completed">Importação completa</string>
@@ -619,4 +619,22 @@
<string name="delete_read_chapters_prompt">Isso irá apagar permanentemente todos os capítulos marcados como lidos do armazenamento local. Você pode baixá-los novamente mais tarde, mas os capítulos importados podem ser perdidos para sempre</string>
<string name="last_used">Usado pela última vez</string>
<string name="show_updated">Mostrar atualização</string>
</resources>
<string name="disable_connectivity_check">Desativar a verificação de conectividade</string>
<string name="disable_connectivity_check_summary">Ignore a verificação de conectividade caso tenha problemas com ela (por exemplo, entrar no modo off-line mesmo que a rede esteja conectada)</string>
<string name="webtoon_gaps">Lacunas no modo webtoon</string>
<string name="webtoon_gaps_summary">Mostrar espaços verticais entre as páginas no modo webtoon</string>
<string name="authors">Autores</string>
<string name="ignore_ssl_errors_summary">Você pode desativar a verificação de certificados SSL caso tenha problemas relacionados a SSL ao acessar recursos de rede. Isso pode afetar sua segurança. É necessário reiniciar o aplicativo após alterar essa configuração.</string>
<string name="search_suggestions">Sugestões de Pesquisa</string>
<string name="recent_queries">Consultas recentes</string>
<string name="suggested_queries">Consultas sugeridas</string>
<string name="disable">Desativar</string>
<string name="sources_disabled">Fontes desativadas</string>
<string name="blocked_by_server_message">Você está bloqueado pelo servidor. Tente usar uma conexão de internet diferente (VPN, Proxy, etc.)</string>
<string name="less_frequently">Com menos frequência</string>
<string name="more_frequently">Com mais frequência</string>
<string name="frequency_of_check">Frequência de verificação</string>
<string name="new_chapters_pattern">%1$s: %2$d</string>
<string name="pin_navigation_ui">Fixar interface de navegação</string>
<string name="pin_navigation_ui_summary">Não esconder barra de navegação e visualização de pesquisa ao rolar</string>
</resources>

View File

@@ -614,4 +614,20 @@
<string name="enable_source">Ativar fonte</string>
<string name="unsupported_source">Esta fonte de mangá não é suportada</string>
<string name="delete_read_chapters_prompt">Isso irá apagar permanentemente todos os capítulos marcados como lidos do armazenamento local. Você pode baixá-los novamente mais tarde, mas os capítulos importados podem ser perdidos para sempre</string>
<string name="last_used">Usado por último</string>
<string name="show_updated">Mostrar atualização</string>
<string name="webtoon_gaps">Lacunas no modo webtoon</string>
<string name="authors">Autores</string>
<string name="disable">Desativar</string>
<string name="sources_disabled">Fontes desativadas</string>
<string name="webtoon_gaps_summary">Mostrar espaços verticais entre páginas no modo webtoon</string>
<string name="recent_queries">Consultas recentes</string>
<string name="search_suggestions">Sugestões de pesquisa</string>
<string name="suggested_queries">Consultas sugeridas</string>
<string name="blocked_by_server_message">Você está bloqueado pelo servidor. Tente usar uma conexão de rede diferente (VPN, Proxy, etc.)</string>
<string name="less_frequently">Menos frequência</string>
<string name="more_frequently">Mais frequência</string>
<string name="frequency_of_check">Frequência de verificação</string>
<string name="pin_navigation_ui">Fixar interface de navegação</string>
<string name="pin_navigation_ui_summary">Ao rolar, não esconda a barra de navegação e a visualização de busca</string>
</resources>

View File

@@ -47,7 +47,7 @@
<string name="clear_thumbs_cache">Избриши кеш сличица</string>
<string name="clear_search_history">Избриши историју претраге</string>
<string name="app_update_available">Доступна је нова верзија апликације</string>
<string name="open_in_browser">Отвори у веб прегледачу</string>
<string name="open_in_browser">Отвори у прегледачу</string>
<string name="notification_sound">Звук обавештења</string>
<string name="vibration">Вибрација</string>
<string name="text_empty_holder_primary">Овде је некако празно…</string>
@@ -472,7 +472,7 @@
<string name="invert_colors">Обрни боје</string>
<string name="color_theme">Палета боја</string>
<string name="brightness">Осветљеност</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="preparing_">Припрема се…</string>
<string name="exit_confirmation_summary">Притисни Назад двапут да би изашао из апликације</string>
<string name="bookmarks_removed">Обележивачи су уклоњени</string>
@@ -619,7 +619,7 @@
<string name="fix">Поправи</string>
<string name="show_updated">Прикажи ажурирано</string>
<string name="webtoon_gaps_summary">Прикажи усправне празнине између страница у Вебтун режиму</string>
<string name="last_used">Последњи пут коришћен</string>
<string name="last_used">Последња кориштена</string>
<string name="webtoon_gaps">Празнине у режиму Вебтун-а</string>
<string name="search_suggestions">Предлози за претрагу</string>
<string name="recent_queries">Недавни упити</string>
@@ -632,4 +632,6 @@
<string name="pin_navigation_ui">Закачи навигациону траку</string>
<string name="pin_navigation_ui_summary">Не сакривај навигациону траку и приказ претраге при листању</string>
<string name="blocked_by_server_message">Блокирао вас је послуживач. Покушај да користиш другу мрежну везу (ВПН, прокси, итд.)</string>
<string name="disable">Онемогући</string>
<string name="sources_disabled">Извори су онемогућени</string>
</resources>

View File

@@ -300,7 +300,7 @@
<string name="other_cache">Diğer önbellekler</string>
<string name="storage_usage">Depolama kullanımı</string>
<string name="available">Mevcut</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="options">Seçenekler</string>
<string name="incognito_mode">Gizli mod</string>
<string name="no_chapters">Bölüm yok</string>
@@ -632,4 +632,9 @@
<string name="suggested_queries">Önerilen sorgular</string>
<string name="authors">Yazarlar</string>
<string name="blocked_by_server_message">Sunucu tarafından engellendiniz. Farklı bir ağ bağlantısı kullanmayı deneyin (VPN, vekil sunucu, vb.)</string>
<string name="disable">Devre dışı bırak</string>
<string name="sources_disabled">Kaynaklar devre dışı bırakıldı</string>
<string name="disable_connectivity_check">Bağlantı denetimini devre dışı bırak</string>
<string name="ignore_ssl_errors_summary">Ağ kaynaklarına erişirken SSL ile ilgili bir sorunla karşılaşmanız durumunda SSL sertifikaları doğrulamasını devre dışı bırakabilirsiniz. Bu durum güvenliğinizi etkileyebilir. Bu ayarı değiştirdikten sonra uygulamanın yeniden başlatılması gerekir.</string>
<string name="disable_connectivity_check_summary">Sorun yaşamanız durumunda bağlantı denetimini atlayın (örneğin, ağ bağlı olmasına rağmen çevrim dışı moda geçiş)</string>
</resources>

View File

@@ -312,7 +312,7 @@
<string name="import_completed_hint">可删除原文件节省存储空间</string>
<string name="import_will_start_soon">即将开始导入</string>
<string name="feed">更新</string>
<string name="memory_usage_pattern">%s - %s</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="not_found_404">没有章节或已被删除</string>
<string name="reader_control_ltr_summary">点击屏幕右侧边缘或按下右键总是翻到下一页</string>
<string name="reader_control_ltr">简易操作</string>
@@ -629,7 +629,12 @@
<string name="less_frequently">低频</string>
<string name="authors">作者</string>
<string name="search_suggestions">搜索建议</string>
<string name="suggested_queries">搜索建议</string>
<string name="suggested_queries">建议搜索</string>
<string name="recent_queries">最近搜索</string>
<string name="blocked_by_server_message">你的访问已被服务器拦截请尝试使用不同的网络连接访问如VPN代理等</string>
<string name="disable">关闭</string>
<string name="sources_disabled">图源已关闭</string>
<string name="disable_connectivity_check">关闭连接连通性检查</string>
<string name="disable_connectivity_check_summary">若连通性检查存在问题可打开此选项(例:即使连接了网络但依旧提示网络断开)</string>
<string name="ignore_ssl_errors_summary">若在连接到在线图源时SSL证书出现问题可关闭SSL证书认证关闭后对安全性有所影响需要重启应用来更改设置。</string>
</resources>

View File

@@ -35,6 +35,12 @@
<item>Google</item>
<item>CloudFlare</item>
<item>AdGuard</item>
<item>0ms</item>
</string-array>
<string-array name="image_proxies" translatable="false">
<item>@string/none</item>
<item>wsrv.nl</item>
<item>0ms.dev</item>
</string-array>
<string-array name="reader_modes" translatable="false">
<item>@string/standard</item>

View File

@@ -40,8 +40,14 @@
<item>2</item>
<item>0</item>
</string-array>
<string-array name="values_image_proxies" translatable="false">
<item>-1</item>
<item>0</item>
<item>1</item>
</string-array>
<string-array name="sync_host_list" translatable="false">
<item>@string/sync_host_default</item>
<item>moe.shirizu.org</item>
<item>86.57.183.214:8081</item>
</string-array>
<string-array name="values_proxy_types" translatable="false">

View File

@@ -214,7 +214,7 @@
<string name="various_languages">Various languages</string>
<string name="search_chapters">Find chapter</string>
<string name="chapters_empty">No chapters in this manga</string>
<string name="percent_string_pattern">%1$s%%</string>
<string name="percent_string_pattern" translatable="false">%1$s%%</string>
<string name="appearance">Appearance</string>
<string name="suggestions_updating">Suggestions updating</string>
<string name="suggestions_excluded_genres">Exclude genres</string>
@@ -301,7 +301,7 @@
<string name="other_cache">Other cache</string>
<string name="storage_usage">Storage usage</string>
<string name="available">Available</string>
<string name="memory_usage_pattern">%1$s - %2$s</string>
<string name="memory_usage_pattern" translatable="false">%1$s - %2$s</string>
<string name="removed_from_favourites">Removed from favourites</string>
<string name="options">Options</string>
<string name="not_found_404">Content not found or removed</string>
@@ -498,7 +498,7 @@
<string name="content_type_hentai">Hentai</string>
<string name="content_type_comics">Comics</string>
<string name="content_type_other">Other</string>
<string name="source_summary_pattern">%1$s, %2$s</string>
<string name="source_summary_pattern" translatable="false">%1$s, %2$s</string>
<string name="sources_catalog">Sources catalog</string>
<string name="source_enabled">Source enabled</string>
<string name="no_manga_sources_catalog_text">There are no sources available in this section, or all of it might have been already added.\nStay tuned</string>
@@ -541,7 +541,7 @@
<string name="mark_as_completed">Mark as completed</string>
<string name="mark_as_completed_prompt">Mark selected manga as completely read?\n\nWarning: current reading progress will be lost.</string>
<string name="category_hidden_done">This category was hidden from the main screen and is accessible through Menu → Manage categories</string>
<string name="remaining_time_pattern">%1$s %2$s</string>
<string name="remaining_time_pattern" translatable="false">%1$s %2$s</string>
<string name="volume_">Volume %d</string>
<string name="volume_unknown">Unknown volume</string>
<string name="incognito_mode_hint">Your reading progress will not be saved</string>
@@ -633,7 +633,7 @@
<string name="less_frequently">Less frequently</string>
<string name="more_frequently">More frequently</string>
<string name="frequency_of_check">Frequency of check</string>
<string name="new_chapters_pattern">%1$s: %2$d</string>
<string name="new_chapters_pattern" translatable="false">%1$s: %2$d</string>
<string name="pin_navigation_ui">Pin navigation UI</string>
<string name="pin_navigation_ui_summary">Do not hide navigation bar and search view on scroll</string>
<string name="search_suggestions">Search suggestions</string>
@@ -643,4 +643,11 @@
<string name="blocked_by_server_message">You are blocked by the server. Try to use a different network connection (VPN, Proxy, etc.)</string>
<string name="disable">Disable</string>
<string name="sources_disabled">Sources disabled</string>
<string name="disable_connectivity_check">Disable connectivity check</string>
<string name="ignore_ssl_errors_summary">You can disable SSL certificates verification in case you face an SSL-related issues when accessing network resources. This may affect your security. Application restarting is required after changing this setting.</string>
<string name="disable_connectivity_check_summary">Skip the connectivity check in case you have issues with it (e.g. going offline mode even though the network is connected)</string>
<string name="disable_nsfw_notifications">Disable NSFW notifications</string>
<string name="disable_nsfw_notifications_summary">Do not show notifications about NSFW manga updates</string>
<string name="tracker_debug_info">Checking for new chapters log</string>
<string name="tracker_debug_info_summary">Debug information about background checks for new chapters</string>
</resources>

View File

@@ -28,6 +28,12 @@
android:key="logs_share"
android:title="@string/share_logs" />
<Preference
android:key="tracker_debug"
android:persistent="false"
android:summary="@string/tracker_debug_info_summary"
android:title="@string/tracker_debug_info" />
<Preference
android:key="about_app_translation"
android:summary="@string/about_app_translation_summary"

View File

@@ -33,11 +33,13 @@
android:title="@string/dns_over_https"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="images_proxy"
android:summary="@string/images_procy_description"
android:title="@string/images_proxy_title" />
<ListPreference
android:defaultValue="-1"
android:entries="@array/image_proxies"
android:entryValues="@array/values_image_proxies"
android:key="images_proxy_2"
android:title="@string/images_proxy_title"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
@@ -47,6 +49,12 @@
<SwitchPreferenceCompat
android:key="ssl_bypass"
android:summary="@string/ignore_ssl_errors_summary"
android:title="@string/ignore_ssl_errors" />
<SwitchPreferenceCompat
android:key="no_offline"
android:summary="@string/disable_connectivity_check_summary"
android:title="@string/disable_connectivity_check" />
</PreferenceScreen>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<SwitchPreferenceCompat
android:defaultValue="true"
@@ -44,6 +45,13 @@
android:key="notifications_settings"
android:title="@string/notifications_settings" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="tracker_enabled"
android:key="tracker_no_nsfw"
android:summary="@string/disable_nsfw_notifications_summary"
android:title="@string/disable_nsfw_notifications" />
<Preference
android:dependency="tracker_enabled"
android:key="ignore_dose"
@@ -51,7 +59,8 @@
android:summary="@string/disable_battery_optimization_summary"
android:title="@string/disable_battery_optimization"
app:allowDividerAbove="true"
app:isPreferenceVisible="false" />
app:isPreferenceVisible="false"
tools:isPrefrenceVisible="true" />
<org.koitharu.kotatsu.settings.utils.LinksPreference
android:icon="@drawable/ic_info_outline"

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<remote-actions version="1.2">
<action
id="next_filter"
label="@string/prev_page"
priority="1"
trigger_key="L">
<preference name="gesture" value="swipe_left"/>
</action>
<action
id="prev_filter"
label="@string/next_page"
priority="2"
trigger_key="R">
<preference name="gesture" value="swipe_right"/>
</action>
</remote-actions>

View File

@@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.4.0'
classpath 'com.android.tools.build:gradle:8.4.1'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51.1'
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.24-1.0.20'