Compare commits

...

335 Commits

Author SHA1 Message Date
Jobobby04 7ec14cd9f0 Fix preview 2024-08-11 20:42:40 -04:00
AntsyLich c23c9491fc Handle Android SDK 35 API collision
(cherry picked from commit fdb96179c6373eb0a8e7d6daea671a315d5ce5f0)
2024-08-11 19:41:15 -04:00
Jobobby04 29f3766c87 Update version code 2024-08-11 19:37:48 -04:00
Jobobby04 07c89890bc Fix SY migrations 2024-08-11 19:37:05 -04:00
Weblate (bot) 64a54f55b3 Translations update from Hosted Weblate (#1228)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Ahmed seif al-nasr <ahmdsyfalnsr2@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Howard Wu <HowardWu20@outlook.com>
Co-authored-by: TawfikSharaf <tawfikahmed132.wa@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
2024-08-11 18:39:57 -04:00
Tim Schneeberger f7202e67cc feat(migration): add option to only show entries with new chapters (#1238) 2024-08-11 18:30:19 -04:00
renovate[bot] 155b03c176 fix(deps): update dependency com.elvishew:xlog to v1.11.1 (#1239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-11 18:29:49 -04:00
renovate[bot] 6b0482576b chore(deps): update gradle/actions action to v4 (#1243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-11 18:29:42 -04:00
AntsyLich c137bafd68 Fix UI freeze after migration
Fixes #938

(cherry picked from commit 3f1d28c3833e6b868152149ed02b3fb8c54eccef)
2024-08-11 18:09:02 -04:00
AntsyLich 49bdffdc28 Add a button to select all scanlators
Resolves #943
Closes #1109

(cherry picked from commit 84b2164787a795f3fd757c325cbfb6ef660ac3a3)
2024-08-11 18:08:43 -04:00
Catting f1b32d531a Add Copy Tracker URL on icon long press (#1101)
* Add Copy Tracker URL on icon long press

Signed-off-by: Catt0s <5874051+mm12@users.noreply.github.com>

* Add 'Copy To Clipboard' to tracker item menu

Signed-off-by: Catt0s <5874051+mm12@users.noreply.github.com>

* Add 'Copy link' to locales.

Signed-off-by: Catt0s <5874051+mm12@users.noreply.github.com>

* Implement code review suggestions
>
> Co-authored-by: AntsyLich  <59261191+AntsyLich@users.noreply.github.com>

Signed-off-by: Catt0s <5874051+mm12@users.noreply.github.com>

* Update app/src/main/java/eu/kanade/presentation/track/components/TrackLogoIcon.kt

---------

Signed-off-by: Catt0s <5874051+mm12@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 200d39e023af79b02276554a1bef1d7d53e3b903)
2024-08-11 18:02:09 -04:00
Weblate (bot) a5ec6c5cdd Translations update from Hosted Weblate (#939)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ml/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/am/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/da/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/he/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ka/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/km/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/mr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sa/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sah/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sdh/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sq/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/te/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uz/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Ahmed seif al-nasr <ahmdsyfalnsr2@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: Animeboynz <40583749+Animeboynz@users.noreply.github.com>
Co-authored-by: David Katrinka <davidkatrinka1995@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Iker Lerones <ikerlero@hotmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Matyáš Caras <matyas@caras.wtf>
Co-authored-by: Norsze <norbert.szabo7+github@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: gekka <1778962971@qq.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: vodkapmp <vodkapmp@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Артём Голуб <artemtirax2001@gmail.com>
(cherry picked from commit b1b15a93eec15a82e2e83650abf97c1b9f0c501c)
2024-08-11 18:02:02 -04:00
MajorTanya 9c56cdb1c1 Fix MAL search results not showing start dates (#1098)
The previous approach would always throw an Exception because
`SimpleDateFormat.format()` expects the input to be of type `Date` or
`Number`, not `String`.

(cherry picked from commit 97c81fadb426d71ac99c9443ab0e89f4089046ef)
2024-08-11 18:01:51 -04:00
MajorTanya 543de065a6 Change Kitsu to kitsu.app domain (#1106)
cf. https://github.com/hummingbird-me/kitsu-server/commit/244fdccca9754d8579c049e738832843001b33b1

(cherry picked from commit 9240eceedc5e2b065dd680819c4180c1ae09512b)

# Conflicts:
#	README.md
2024-08-11 18:01:43 -04:00
Jobobby04 33296e1faf Translations readme 2024-08-11 18:00:59 -04:00
Catting d1a90c0bb7 Contributing: ktLintFormat -> detekt (#1102)
* Contributing: ktLintFormat -> detekt

update Contributing info to use detekt instead of ktLintFormat

* Update CONTRIBUTING.md

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 14ae57d78b31f0bb3b58d19c1d8cfcebcc8e2253)
2024-08-11 18:00:34 -04:00
renovate[bot] 9fa61d33be fix(deps): update dependency com.android.tools.build:gradle to v8.5.2 (#1099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 4828c54245dd6532c0e7a2b6c8cf5d8a703d3376)
2024-08-11 18:00:26 -04:00
renovate[bot] 0e9dcc7855 fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha10 (#1092)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e8b7c3e24bb677d289554b972ef2496a976c79aa)
2024-08-11 18:00:18 -04:00
renovate[bot] 6738c6072d fix(deps): update dependency androidx.work:work-runtime to v2.9.1 (#1091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit af77083660000e7378587dbc8d44e44bd8b196ec)
2024-08-11 18:00:11 -04:00
renovate[bot] 29033c539c fix(deps): update dependency androidx.annotation:annotation to v1.8.2 (#1090)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 36b9caeea8baf15f0d0ed37abc12638d44194c09)
2024-08-11 18:00:04 -04:00
renovate[bot] eaa3413c37 fix(deps): update paging.version to v3.3.2 (#1093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 8e40146f96704c3dc98bbb4f9f89d470ffa32f69)
2024-08-11 17:59:57 -04:00
AntsyLich 73d9d1d46d ExpandableMangaDescription: Adjust size transform anim spec
Co-authored-by: ivan <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 1c16fc79c2ac4c4be30308fed84ffb371dab5902)
2024-08-11 17:59:47 -04:00
Roshan Varughese 94f9aaf351 Add Backup and Restore of Extension Repos (#1057)
* Backup/Restore Extension Repos

* Refactor

* Moving to Under App Settings

* Sort by URL, Check existing by SHA and Error Logging

Untested. Currently in a lecture and can't test if the changes really work.

* Changes to logic

* Don't ask me what's happening here

* Renaming Variables

* Fixing restoreAmount & changes to logic

Co-Authored-By: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 31263084eca3ba98624d258a317d53094bba2256)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt
2024-08-11 17:59:35 -04:00
AntsyLich e21149cb37 Rename backup restore error log file
(cherry picked from commit 2858ef835fec8d7278b1d0cad1b5664104d1e4b0)
2024-08-11 17:39:41 -04:00
renovate[bot] 11aad16f59 chore(deps): update kotlin monorepo to v2.0.10 (#1085)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit edb8201f74e516c296b62e04a13802e1bd9e0b6b)
2024-08-11 17:39:30 -04:00
FooIbar 33a3918e86 Don't crash on ill-formed URLs (#1084)
(cherry picked from commit 854474f85ffc41eccdc2b3a6cf105fa2805ebc3c)
2024-08-11 17:39:21 -04:00
Tran M. Cuong d655b8ecdf fix: drawScrollbar crash on list with 0 item but only sticky header (#1083)
(cherry picked from commit 04db46fe75c2406fe9750e97da65774a6b268f27)
2024-08-11 17:39:12 -04:00
FooIbar 70a8bef7a5 Match extra layout space with scroll distance (#1076)
And increase recycler item view cache size.

(cherry picked from commit a3dfd2efe6ace7a2a4d79bd09fb1a729989f1094)
2024-08-11 17:38:58 -04:00
Vetle Ledaal 999a8613cf Improve error message if restoring from JSON file (#1056)
* Improve error message if restoring from JSON file

* Replace Exception with IOException

* Use more generic error message if protobuf fails

* fix lint

(cherry picked from commit de8ef6dad7c89afb7041ccb489d68539a4849cb5)
2024-08-11 17:38:48 -04:00
AntsyLich 5721a02bca Bump default user agent string
(cherry picked from commit 8160b47ff5fbbd9b32caeb462b5be881fabd3449)
2024-08-11 17:38:39 -04:00
AntsyLich e303b88b90 Cleanup backup/restore related code
(cherry picked from commit c201b341a716b90d378dcda4bd9b8ac4a343d4fc)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
2024-08-11 17:38:13 -04:00
AntsyLich a62dd5821a Fix library is backed up when disabled and make categories backup/restore independent
(cherry picked from commit 56fb4f62a152e87a71892aa68c78cac51a2c8596)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupOptions.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/RestoreOptions.kt
2024-08-11 17:34:04 -04:00
Roshan Varughese a0786d9b09 Adds Option to Copy Panel to Clipboard (#1003)
* Add Copy to Clipboard

* Removing Unused Import

* Reusing onShare function

* Commit Suggestion

* Early Return on null

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 0af90999c8eed4b6c56a94418e5558833f273aa9)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2024-08-11 17:25:33 -04:00
renovate[bot] 04580ce357 fix(deps): update paging.version to v3.3.1 (#1046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 41e2dc7ae80250d9166fc637c1170667afdb0a9e)
2024-08-11 15:39:47 -04:00
Roshan Varughese b759f2f02a Format Category String on Subtitle Display (#1030)
* Fixes #1029

* Max Line Length Fix

* Update SettingsLibraryScreen.kt

No idea how this works.

Co-authored-by: Foolbar <118464521+Foolbar@users.noreply.github.com>

---------

Co-authored-by: Foolbar <118464521+Foolbar@users.noreply.github.com>
(cherry picked from commit 88efde8796b0e1cc8fba6cd987bdc487bd97f248)
2024-08-11 15:39:38 -04:00
renovate[bot] 8ae8068ecd fix(deps): update lifecycle.version to v2.8.4 (#1045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit b7849d714698900a25188bdbfd77bf24936f2dd7)
2024-08-11 15:39:31 -04:00
renovate[bot] eecd9367d4 fix(deps): update dependency androidx.annotation:annotation to v1.8.1 (#1043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 602b58f364b95b83a3148be34cd4c90d95d7d405)
2024-08-11 15:39:24 -04:00
renovate[bot] 55dee69838 fix(deps): update dependency androidx.activity:activity-compose to v1.9.1 (#1042)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e48dbdbf2356c0e6e148313dc6610e865cd8e995)
2024-08-11 15:39:16 -04:00
FooIbar a730ca5444 Remove obsolete workaround (#1021)
(cherry picked from commit 51b68cd25ff4bf556de88cb31525c55dd7eb7027)
2024-08-11 15:39:06 -04:00
renovate[bot] cebd8fe0a8 fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha09 (#1039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit ca784cbe3267e94e652e4c54f91b7107cc53c307)
2024-08-11 15:38:54 -04:00
renovate[bot] 55a979c5f7 fix(deps): update dependency io.mockk:mockk to v1.13.12 (#1016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 4f61b2e4e89bc257cf5e629823904805907bf75c)
2024-08-11 15:38:37 -04:00
renovate[bot] 728f3fc349 chore(deps): update dependency gradle to v8.9 (#1007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f63e95091013320b27bfc3c7c975c4bdd4a983c5)
2024-08-11 15:38:17 -04:00
renovate[bot] a9a3ed1d16 fix(deps): update dependency org.jsoup:jsoup to v1.18.1 (#999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f3f2bd41c3974878bcf0e3a62d99ee89bf92fb41)
2024-08-11 15:38:08 -04:00
renovate[bot] 36f13a7c6a fix(deps): update dependency com.android.tools.build:gradle to v8.5.1 (#1010)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 7a2ca4bf4d41764705637d61c6d86249f8815b7b)
2024-08-11 15:37:37 -04:00
renovate[bot] 37a2ccc678 Bump coil version and some cleanup
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e65634cb427eafe9e3bd192f9e8bf71f2243ce6c)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
2024-08-11 15:37:16 -04:00
FooIbar bb39088dd7 Fix some issues when reading/saving images (#993)
* Fix unsupported mime type error when saving images

Avoid using platform mime type map to get extensions as it may not have
all mime types we support.

* Fix jxl images downloading/reading

(cherry picked from commit daa47e049327c4d8b1fe4724ed1b84897d81fcf2)

# Conflicts:
#	core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
2024-08-11 15:34:57 -04:00
AntsyLich c5546e1095 Fix login prompts despite being logged in to trackers in Manga screen
(cherry picked from commit cbcd8bd6682023f728568f2b44da26124618aed7)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
2024-08-11 15:32:28 -04:00
AntsyLich 2d12c670db Observe tracker login state instead of fetching once (#987)
* Observe tracker login state instead of fetching once

* Review changes

(cherry picked from commit 2092c81bad59fd745a8514af320e534ecf40a5da)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
2024-08-11 15:15:44 -04:00
AntsyLich 3db4bccebc Make global search "Has result" sticky
Closes #133

(cherry picked from commit 5a61ca5535fe0d9e8e7bcb9e665ba2f9cb0cf649)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
2024-08-11 14:49:11 -04:00
Roshan Varughese 2f23ad6bfd Smart Update Dialog Tweak (#977)
* Smart Update Dialog Fix

* Build Fail Change 1

* Commit Suggested Change

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Build Fail Change 2

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit ddba71df37359e6abbbcc96b18685435961710dc)
2024-08-11 14:48:33 -04:00
CrepeTF de1898a2c9 Correct tako variable colours (#976)
(cherry picked from commit 75b5d966018aa917f57adf37370088a51e4914b2)
2024-08-11 14:48:18 -04:00
renovate[bot] eb135ec22d fix(deps): update lifecycle.version to v2.8.3 (#972)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 77db8873f6753cc9db8f67b39d53685563380cc6)
2024-08-11 14:48:10 -04:00
WerctFourth bf6c646dc7 Update image-decoder revision (#971)
(cherry picked from commit bff6183cf3ef400d8ddcdccf7180e4139816cc09)
2024-08-11 14:48:01 -04:00
renovate[bot] 9ce16d5e1c fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07 (#960)
* fix(deps): update dependency io.coil-kt.coil3:coil-bom to v3.0.0-alpha07

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit c0f9de88e70ef1db97c521993462ae27550b5790)
2024-08-11 14:47:41 -04:00
renovate[bot] 619ff726c8 fix(deps): update aboutlib.version to v11.2.2 (#965)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 80cdebcdf467ed00e530651aeed2b36cc63b8356)
2024-08-11 14:47:22 -04:00
renovate[bot] 730ceaaf49 fix(deps): update dependency org.junit.jupiter:junit-jupiter to v5.10.3 (#962)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 9e2f97eeb8a0f1d1b353dc3e77fb64d69b568674)
2024-08-11 14:47:13 -04:00
CrepeTF 07b701cb3c Theme fixes (#963)
* Fix theme issue with download progress indicator

* Fix theme issue with download progress indicator + better contrast

(cherry picked from commit e132cc405f23e18dd8d73626730821eae9051149)
2024-08-11 14:47:02 -04:00
Caio Oliveira b64c6b78ea buildSrc: Fix strange warning in ci build (#952)
* buildSrc: Fix strange warning

´Project accessors enabled, but root project name not explicitly set for 'buildSrc'. Checking out the project in different folders will impact the generated code and implicitly the buildscript classpath, breaking caching.´

* Update settings.gradle.kts

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 2674b849746f20c051dab3fd6edfad1594e41b42)
2024-08-11 14:46:50 -04:00
renovate[bot] 521bce5c08 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.1 (#958)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f34702d4fcc10f24953b21e883fb454778bbae77)
2024-08-11 14:46:36 -04:00
renovate[bot] a719ed8c9e fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.1 (#959)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 7823966ddf2289fee743feaa58b906ab9179a4ed)
2024-08-11 14:46:22 -04:00
renovate[bot] f6fc2d7e2f fix(deps): update dependency net.zetetic:sqlcipher-android to v4.6.0 (#1221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-27 09:55:53 -04:00
renovate[bot] 48d43c4f07 chore(deps): update actions/upload-artifact action to v4 (#1222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-27 09:55:42 -04:00
renovate[bot] f4fa86b2dc chore(deps): update softprops/action-gh-release action to v2 (#1223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-27 09:55:29 -04:00
renovate[bot] 37db0dc1f6 fix(deps): update dependency com.google.oauth-client:google-oauth-client to v1.36.0 (#1220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-26 20:55:33 -04:00
renovate[bot] 1ada03b07a chore(deps): update actions/setup-java action to v4 (#1217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-26 20:54:25 -04:00
renovate[bot] f4c1e7c2d5 chore(deps): update damianreeves/write-file-action action to v1.3 (#1216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-26 20:54:11 -04:00
Jobobby04 6c5282c598 Another 2024-06-26 20:39:26 -04:00
Jobobby04 7899474a36 Fix build errors 2024-06-26 20:38:12 -04:00
Jobobby04 225b419bba Update wrapper validation 2024-06-26 20:23:59 -04:00
Jobobby04 fa64103a1c Actual baseline 2024-06-26 20:20:12 -04:00
Jobobby04 57e0e99f06 Preview branch makes preview 2024-06-26 20:10:45 -04:00
Jobobby04 efde7afa8e Update baseline 2024-06-26 20:09:09 -04:00
Jobobby04 f929a4bc26 Move some libs to sylibs 2024-06-26 20:03:24 -04:00
renovate[bot] d35141c1cc chore: Configure Renovate (#1215)
* Add renovate.json

* Only update specific files

* Add a glob

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jobobby04 <jobobby04@users.noreply.github.com>
2024-06-26 19:57:10 -04:00
renovate[bot] 6988966019 fix(deps): update serialization.version to v1.7.1 (#951)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit d8fe7d32ca6bacbb74e9e80173625a993621e4b2)
2024-06-26 19:32:52 -04:00
Maddie Witman f6d8ebbb0a Added configuration options to e-ink page flashes (#625)
* Recommit for e-ink pref changes

* Fixed state holder for flash interval

* Detekt

* Refactor suggested by Antsy

* inverted currentDisplayRefresh check for early exit

(cherry picked from commit 2f86f25d5b24c2054a604802dc65b8bc3a99c7c0)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
#	app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt
2024-06-26 19:32:42 -04:00
renovate[bot] ae45df9fcf fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0 (#948)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 36e40c099772d2cb53d4ec87b2b00f97fe455c98)
2024-06-26 19:31:12 -04:00
renovate[bot] f332344681 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0 (#947)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 40754659a9e146626add56c494a6dc9873691f14)
2024-06-26 19:31:03 -04:00
AntsyLich c6abb340ca Cleanup in CommonMangaItem.kt
Closes #19

Co-authored-by: Roshan Varughese <40583749+Animeboynz@users.noreply.github.com>
(cherry picked from commit e17f70f7226ea031fc1f962c9dfea3e404ba53ad)
2024-06-26 19:30:50 -04:00
Tran M. Cuong 99dbb16a7a Fix Migrator test and also add the test to build script (#896)
* Fix MigratorTest after update to Kotlin 2.0.0

* add main module's test to build script

(cherry picked from commit e57638a49c759d36d25b92f26633df5bdfb0d2b3)

# Conflicts:
#	.github/workflows/build_pull_request.yml
#	.github/workflows/build_push.yml
2024-06-26 19:30:39 -04:00
FooIbar f62e8933d7 Fix unexpected skips in strong skipping mode (#940)
(cherry picked from commit 0ce1cf22cdbb7d82df3db1a901253b4973ab027f)

# Conflicts:
#	source-api/build.gradle.kts
2024-06-26 19:30:00 -04:00
renovate[bot] ee3c2fd79c fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v2.0.1 (#945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f6ec53cdde32a17bc394e55b697a3b59bfd76e58)
2024-06-26 19:28:53 -04:00
renovate[bot] 6b08b873a8 fix(deps): update dependency com.google.firebase:firebase-analytics to v22.0.2 (#936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit b37357f9097730edb1d72f1297461e580286856c)
2024-06-26 19:28:43 -04:00
renovate[bot] a3f2f49ab8 fix(deps): update moko to v0.24.1 (#933)
* fix(deps): update moko to v0.24.1

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit f58a05e91828a69c01d49d629e5bfa9ec7ae3ffc)
2024-06-26 19:28:27 -04:00
Weblate (bot) 524f5cc6ab Translations update from Hosted Weblate (#904)
* Translated using Weblate (Malayalam)

Currently translated at 16.9% (136 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/

* Translated using Weblate (Swedish)

Currently translated at 99.1% (797 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/

* Translated using Weblate (Arabic)

Currently translated at 99.5% (800 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sv/

---------

Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: Norsze <norbert.szabo7+github@gmail.com>
Co-authored-by: Duh051 <duhduh272@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
(cherry picked from commit cf02119da55c431d0fb4c42ecfec3681d466ae43)
2024-06-26 19:16:39 -04:00
FooIbar a35e084b9e Fix R8 version configuration not working (#916)
This reverts commit f3226fb278cab87422255e04e647c50095b61529.

(cherry picked from commit 4182ae89a036525c5575961a68371df249ce384f)

# Conflicts:
#	build.gradle.kts
2024-06-26 19:16:27 -04:00
FooIbar 78f7fba67b Update R8 to fix NoSuchMethodError crash (#914)
(cherry picked from commit f3226fb278cab87422255e04e647c50095b61529)

# Conflicts:
#	build.gradle.kts
2024-06-26 19:15:56 -04:00
renovate[bot] 69d1db3018 fix(deps): update dependency com.android.tools.build:gradle to v8.5.0 (#901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 2e78bceb30908aca8e585f91942849a6e4e7cb15)
2024-06-26 19:15:21 -04:00
Weblate (bot) d1b317e5c8 Translations update from Hosted Weblate (#878)
* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Malayalam)

Currently translated at 15.5% (125 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/

* Translated using Weblate (Malayalam)

Currently translated at 15.5% (125 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/

* Translated using Weblate (Malayalam)

Currently translated at 94.4% (17 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ml/

---------

Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: Animeboynz <roshanvarughese@hotmail.com>
(cherry picked from commit aa1714b2acf0e5b16558ea703220f60d4ecd23e9)
2024-06-26 19:15:06 -04:00
AntsyLich fff40e031f Fix issue with creating and restoring backup
Fixes #881

(cherry picked from commit f696f209c6b3efb3148e1d587af9e42c71d8dc6f)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt
2024-06-26 19:14:54 -04:00
renovate[bot] 5be2ec51ba fix(deps): update dependency androidx.glance:glance-appwidget to v1.1.0 (#890)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit af57e124f2113f78028771f1579a356884d7ead7)
2024-06-26 19:13:53 -04:00
renovate[bot] 1c2a7af13e fix(deps): update lifecycle.version to v2.8.2 (#889)
fix(deps): update dependency androidx.lifecycle:lifecycle-runtime-ktx to v2.8.2

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 8e8ee69bbacb2260d0ae52808c02684e567119b9)
2024-06-26 19:13:44 -04:00
renovate[bot] 182158acb0 fix(deps): update dependency com.android.tools.build:gradle to v8.4.2 (#883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e9d69a83febccf8840dad03597e3ac2a6aa3f972)
2024-06-26 19:13:34 -04:00
AntsyLich 21f92bfb3a Fix chapter number parsing when number is after unwanted tag
Fixes #554

Co-authored-by: Naputt1 <94742489+Naputt1@users.noreply.github.com>
(cherry picked from commit 6a80305d6c572da6c08c0c69f5c25ff26ecf7383)
2024-06-26 19:13:24 -04:00
AntsyLich a5522ef732 Check category order before restoring from backup
Closes #632

Co-authored-by: Cologler <10906962+Cologler@users.noreply.github.com>
(cherry picked from commit 119bcbf8ed2415664922ea77fadf0da1165d1732)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/CategoriesRestorer.kt
2024-06-26 19:13:14 -04:00
Weblate (bot) 239793f7fd Translations update from Hosted Weblate (#611)
* Translated using Weblate (Malayalam)

Currently translated at 12.9% (104 of 803 strings)

Translated using Weblate (Malayalam)

Currently translated at 94.4% (17 of 18 strings)

Translated using Weblate (Malayalam)

Currently translated at 11.8% (95 of 803 strings)

Added translation using Weblate (Malayalam)

Added translation using Weblate (Malayalam)

Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ml/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

* Translated using Weblate (Italian)

Currently translated at 99.6% (800 of 803 strings)

Co-authored-by: Federico Pierantoni <federico.pieranton@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translation: Mihon/Mihon

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/
Translation: Mihon/Mihon

* Translated using Weblate (Javanese)

Currently translated at 38.7% (311 of 803 strings)

Translated using Weblate (Japanese)

Currently translated at 100.0% (803 of 803 strings)

Translated using Weblate (Indonesian)

Currently translated at 98.7% (793 of 803 strings)

Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/
Translation: Mihon/Mihon

* Translated using Weblate (Greek)

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translation: Mihon/Mihon

* Translated using Weblate (Serbian)

Currently translated at 99.2% (797 of 803 strings)

Co-authored-by: Rikishaaa <jebote90@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/
Translation: Mihon/Mihon

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: Blackiezin <mcperenan134@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translation: Mihon/Mihon

* Translated using Weblate (French)

Currently translated at 100.0% (18 of 18 strings)

Translated using Weblate (French)

Currently translated at 99.0% (795 of 803 strings)

Co-authored-by: LaQuiche426 <loic.dossantos42630@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

* Translated using Weblate (Portuguese)

Currently translated at 99.8% (802 of 803 strings)

Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/
Translation: Mihon/Mihon

* Translated using Weblate (Vietnamese)

Currently translated at 100.0% (18 of 18 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.8% (778 of 803 strings)

Co-authored-by: Karuto <nguyenthaison609@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

* Translated using Weblate (Croatian)

Currently translated at 99.5% (799 of 803 strings)

Co-authored-by: Milo Ivir <mail@milotype.de>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translation: Mihon/Mihon

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: Eji-san <ejierubani@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translation: Mihon/Mihon

* Translated using Weblate (Galician)

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: kevans <albapazpi@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/
Translation: Mihon/Mihon

* Translated using Weblate (Ukrainian)

Currently translated at 99.8% (802 of 803 strings)

Co-authored-by: Kodekiro Kodekihara <lolbitoklol@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/
Translation: Mihon/Mihon

* Translated using Weblate (Malay)

Currently translated at 98.6% (792 of 803 strings)

Co-authored-by: Farith <mail2@farithadnan.net>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translation: Mihon/Mihon

* Translated using Weblate (Nepali)

Currently translated at 100.0% (18 of 18 strings)

Translated using Weblate (Nepali)

Currently translated at 100.0% (803 of 803 strings)

Co-authored-by: FateXBlood <fatexblood@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

* Translated using Weblate (Vietnamese)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/

* Translated using Weblate (Romanian)

Currently translated at 99.6% (800 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/

* Translated using Weblate (Romanian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/

* Translated using Weblate (Italian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/

* Translated using Weblate (Polish)

Currently translated at 99.5% (799 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (German)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Russian)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (French)

Currently translated at 99.5% (800 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/

* Translated using Weblate (Filipino)

Currently translated at 99.8% (803 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (804 of 804 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Catalan)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ca/

---------

Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: Federico Pierantoni <federico.pieranton@gmail.com>
Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Rikishaaa <jebote90@gmail.com>
Co-authored-by: Blackiezin <mcperenan134@gmail.com>
Co-authored-by: LaQuiche426 <loic.dossantos42630@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: Karuto <nguyenthaison609@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: Kodekiro Kodekihara <lolbitoklol@gmail.com>
Co-authored-by: Farith <mail2@farithadnan.net>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Chrono Lux <amber_c001@protonmail.com>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: Tim Schneeberger <thebone.main@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Naga <yz2000.pro@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
(cherry picked from commit 87fe64468ca08466af5b9fcc7f9e17e9a23021e6)

# Conflicts:
#	i18n/src/commonMain/resources/MR/gl/strings.xml
#	i18n/src/commonMain/resources/MR/ro/strings.xml
2024-06-26 19:12:15 -04:00
renovate[bot] 4e9cfe4602 fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v2 (#873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit bdce3c39f1475dc77dad300a0bf3702e85d32916)
2024-06-26 19:11:01 -04:00
AntsyLich f548c85e7a MangaChapterListItem: Don't use alpha modifier
Possibly fixes #822

Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 15d999229fcce865001d5fa77d0163e6e80e38db)
2024-06-26 19:10:39 -04:00
renovate[bot] 576349c446 fix(deps): update okhttp monorepo to v5.0.0-alpha.14 (#688)
* fix(deps): update okhttp monorepo to v5.0.0-alpha.14

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 1edd55c981aa72faf49c06173f33bf0c2f99fe60)
2024-06-26 19:10:28 -04:00
renovate[bot] 9b00e0458b fix(deps): update serialization.version to v1.7.0 (#870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 71b558cb34c4e2da435877f391e57b6d49c4ef4f)
2024-06-26 19:06:15 -04:00
renovate[bot] 6a1ff99441 chore(deps): update kotlin and compose compiler to v2 (major) (#819)
* chore(deps): update kotlin and compose compiler to v2

* Update .gitignore

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 46003ec25139319079abc9fde89b3afd344a1a11)

# Conflicts:
#	.github/renovate.json5
#	gradle/compose.versions.toml
#	source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
2024-06-26 19:06:10 -04:00
renovate[bot] 0121fe9397 fix(deps): update dependency io.kotest:kotest-assertions-core to v5.9.1 (#869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 1f7574bd4fc0471b7f974cffdf166c2551b2749b)
2024-06-26 17:41:20 -04:00
Cuong M. Tran 5c47c7a409 Fix MigratorTest after update to io.mockk v1.13.11 (#814)
* Fix MigratorTest after update to io.mockk v1.13.11

Causing error: io.mockk.MockKException: was not can only be called on a mocked object

* remove import

(cherry picked from commit da62c7a21a81f513988fa64df6253376f85228ef)
2024-06-26 17:40:33 -04:00
renovate[bot] 8bb4f33f2e fix(deps): update dependency io.github.fornewid:material-motion-compose-core to v1.2.1 (#858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 0870cffba121c0cc7db9c79f83f770a43d9c32e7)
2024-06-26 17:40:17 -04:00
Sven 5f5fd51668 fix: storage permission request for non-conforming devices (#726)
* fix: storage permission request for non-conforming devices

* fix: catch more specific exception

* chore: add toast message to indicate missing persistent permissions

* chore: correct newly introduced translaction string

* Change error toast message

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 8632ba85ee1ed080d7baa70050d460807c8edfcf)
2024-06-26 17:40:03 -04:00
Jobobby04 c7bbad93b2 Fix some MDLang issues 2024-06-26 17:38:53 -04:00
Jobobby04 1a4a2506f4 Codestral(ChatGPT) cleanup of some double pages code 2024-06-26 17:38:51 -04:00
gelionexists 7b7a594ddb Update LewdMangaChecker.kt (#1204)
- Added the `mature` tag
- Added `doujins` (doujins.com) and `luscious` (luscious.net) as filter keywords
2024-06-26 17:31:41 -04:00
KaiserBh c2eece0fff chore: improve google drive sync. (#1200)
improve google drive sync, removes the lock, change to protobuf, and potentially fix deviceId not being unique, since it wasn't appState...
2024-06-26 17:31:12 -04:00
ɴᴇᴋᴏ d29a4ff381 Update TW strings.xml (#1202)
Add Google sync strings
2024-06-26 17:30:06 -04:00
Tim Schneeberger 31f967235f Add Namicomi support for external chapters on MD (#1188) 2024-06-01 18:26:05 -04:00
Cuong M. Tran 7d6e746257 Fix build warning: remove non-default string resources (#1180) 2024-06-01 18:25:53 -04:00
Timo d306139047 Update README.md (#1174)
Changed DO and DON'T for bug repports to a link with the issue template.
2024-06-01 18:25:37 -04:00
Jobobby04 7e6811692e Fix WorkManager cancellation issues 2024-06-01 17:51:03 -04:00
renovate[bot] 8df0446020 chore(deps): update dependency gradle to v8.8 (#856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 116579d38c08c203fe2c4996419e277c7bf9c165)
2024-06-01 17:40:26 -04:00
renovate[bot] 8ccf8fc74d fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-rc01 (#855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 098f925519502a99aef9eb33c9b88b1280351a73)
2024-06-01 17:40:18 -04:00
Jobobby04 4deaa41c53 Cleanup some build warnings 2024-06-01 14:52:54 -04:00
renovate[bot] c843789f66 fix(deps): update aboutlib.version to v11.2.1 (#846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 9f5db70572cee99243c850d2da55fe0a10d52809)
2024-06-01 14:02:25 -04:00
renovate[bot] 78da81fa42 fix(deps): update dependency com.google.firebase:firebase-analytics to v22.0.1 (#848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 1f286f1a357a6c0f8cf2a7274b11b175315de4ea)
2024-06-01 14:02:17 -04:00
renovate[bot] 4fc96f263d fix(deps): update dependency com.google.gms:google-services to v4.4.2 (#849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 7ab7f5ac37e8c102ca259144f136616b59172e6d)
2024-06-01 14:02:02 -04:00
renovate[bot] 6a12b54ecb fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-rc01 (#851)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e567250b17c231abee151541fea1f97b269b516a)
2024-06-01 14:01:52 -04:00
renovate[bot] 8ffcd5efec fix(deps): update dependency androidx.appcompat:appcompat to v1.7.0 (#845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 095da924b9a1d724e6a228cb4abcc4dfe948db7d)
2024-06-01 14:01:44 -04:00
renovate[bot] 26121efeb4 fix(deps): update lifecycle.version to v2.8.1 (#844)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit b9da98b527e3d9ac56e263f7b595768239fa718e)
2024-06-01 14:01:36 -04:00
renovate[bot] 1ecf3a567b fix(deps): update paging.version to v3.3.0 (#810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit af8696cb9060e652f41220ea4a6074568df8bc3d)
2024-06-01 14:01:28 -04:00
renovate[bot] 20fff5798d fix(deps): update dependency org.apache.commons:commons-compress to v1.26.2 (#826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit de5a64aa73c8a2229fa87e9baba9981197474f51)
2024-06-01 14:01:20 -04:00
renovate[bot] 0ba580ba30 fix(deps): update aboutlib.version to v11.2.0 (#823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 9b944092c75046b68ac38e2d9d3a5e49033d7588)
2024-06-01 14:01:10 -04:00
renovate[bot] aaf28ee4f1 fix(deps): update dependency com.android.tools.build:gradle to v8.4.1 (#818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 0cb1794a445c8c7f4e9c163bd850f1681cdbef6a)
2024-06-01 14:01:03 -04:00
renovate[bot] 8558c110a9 chore(deps): update kotlin and compose compiler (#800)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 2f243fae11e48799437b1672ec1a8571552caacb)

# Conflicts:
#	gradle/compose.versions.toml
2024-06-01 14:00:54 -04:00
renovate[bot] 3865583c28 fix(deps): update lifecycle.version to v2.8.0 (#809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit d2e5c78074829ca7c7bace3e5bfee25ea430e44c)
2024-06-01 14:00:15 -04:00
renovate[bot] b2cc61f6fd fix(deps): update dependency androidx.annotation:annotation to v1.8.0 (#808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 5912d6b08f3624a385f820582e49f1dde1b55864)
2024-06-01 14:00:05 -04:00
renovate[bot] 47dd58de2a fix(deps): update dependency io.mockk:mockk to v1.13.11 (#803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 99b550ae0d9eaf530d644f7755162d9705c302f4)

# Conflicts:
#	gradle/libs.versions.toml
2024-06-01 13:59:55 -04:00
renovate[bot] 67d42c9c2b fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-beta01 (#801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 8d187f786594356dd47899dad660d602b52099ef)
2024-06-01 13:59:25 -04:00
renovate[bot] b97f322d6f fix(deps): update dependency com.google.android.material:material to v1.12.0 (#754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 653d5d3e252ff2508b100b0f291fdcfaf2a98097)
2024-06-01 13:59:17 -04:00
renovate[bot] d8cc4f8b45 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-beta01 (#797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit ce497003e38d838e846b6e4ce88aafe4610d522b)
2024-06-01 13:57:49 -04:00
CrepeTF f78752fbdf Update themes to follow new compose update changes (#766)
* Update Green Apple theme

* Add some Green Apple theme comments

* Update Lavender theme

* Update Midnight Dusk theme

* Update Nord theme

* Update Strawberry Daiquiri theme

* Update Tako theme

* Update Teal & Turquoise theme

* Update Lavender secondaryContainer and onSecondaryContainer colour

* Update M.Dusk secondaryContainer and onSecondaryContainer colour

* Update Tako secondaryContainer and onSecondaryContainer colour

* Comments

* Update Tidal Wave theme

* Update Yin Yang theme

* Update Yotsuba theme

* Fix navbar tinted background on pure black

* Add surfaceContainer levels to Lavender theme

* Resolve detekt issues

* Add surfaceContainer levels to Midnight Dusk theme

* Add surfaceContainer levels to Nord theme

* Add surfaceContainer levels to Tako theme

* Add surfaceContainer levels to Teal & Turquoise theme

* Add surfaceContainer levels to Tidal Wave theme

* Add surfaceContainer levels to Yin Yang theme

* Add surfaceContainer levels to Yotsuba theme

* Add dark theme surfaceContainer levels to Yotsuba theme

* surfaceContainer tweaks to Yotsuba theme

* surfaceContainer tweaks to Strawberry Daiquiri theme

* surfaceContainer tweaks to Nord theme

* surfaceContainer tweaks to Lavender theme

* Update Tachiyomi theme

* Update Pure Black theme

* Resolve detekt issues

* Oopsie

(cherry picked from commit 16392adcbba4027cbed0a44e2fc62df330af6385)
2024-06-01 13:57:37 -04:00
AwkwardPeak7 d968d58cd6 update r8 rules for MultipartBody.Builder in extensions (#783)
(cherry picked from commit f603db3f3fac72954997bfa0c2cd77a0b92cd7f0)
2024-06-01 13:57:27 -04:00
AntsyLich 54d5f9baaf Remove dependency on compose material 2 components
(cherry picked from commit fb9423028eb017c110cb805f2d0601e5b02e50f9)
2024-06-01 12:57:07 -04:00
FooIbar c1bf53e28a Fix tap control area shifting after zooming out (#767)
(cherry picked from commit 8e9396a9cfe5f5e87e4e5f2093421a3fa24d43db)
2024-06-01 12:56:57 -04:00
AntsyLich 517fd3a8f4 Use new SurfaceContainer color roles
Non-dynamic themes need to be updated

Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 1df87eabf2b301cf6fc60cfa5f9391756984b790)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt
2024-06-01 12:56:48 -04:00
AntsyLich 089d1aba57 Fix search bar style
(cherry picked from commit ca7391bbf3d49428f6321aa6baa03b0d2b5abff9)
2024-06-01 12:55:18 -04:00
Jobobby04 46bf139f01 Possibly fix extension obsolete bug 2024-05-05 13:17:29 -04:00
Jobobby04 c3fb5c0bec Revert "Bump compose version"
This reverts commit 5550ddad4e.
2024-05-05 12:58:03 -04:00
Reagan 000a4ffc3f Change keyboard type in extension repo dialog (#764)
(cherry picked from commit 550f1197e818c35c7c05fd6184e69c7d29559e9f)
2024-05-05 12:57:45 -04:00
Jobobby04 7b0b879d65 Downgrade crashlytics plugin 2024-05-05 00:07:41 -04:00
Dexroneum 8a622f6c7d [RU] Translations (#1161)
* [RU] Translations

* [RU] Deleted unused strings
2024-05-04 23:39:57 -04:00
Jobobby04 253060a3bc Minor cleanup 2024-05-04 23:15:17 -04:00
Jobobby04 b6b33e8c00 Get new page url on image fetch failure for EHentai 2024-05-04 23:13:52 -04:00
Jobobby04 2e4f811090 Add getImageUrl override to EHentai 2024-05-04 23:13:26 -04:00
Jobobby04 215a1908f7 Include lewd filter in filter highlight 2024-05-04 23:13:06 -04:00
Jobobby04 082acf000c Fix Local Manga details edit 2024-05-04 23:12:45 -04:00
Jobobby04 2d1240b274 Update dependencies and cleanup 2024-05-04 18:41:03 -04:00
Jobobby04 1e98709cc3 Revert "Fix badge count getting cut off on tab title"
This reverts commit f9148c0c5e.
2024-05-04 18:12:54 -04:00
AntsyLich 5550ddad4e Bump compose version
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit e473c7f09fc009161145aca94bd70027f042b0bf)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt
2024-05-04 17:07:27 -04:00
AntsyLich f9148c0c5e Fix badge count getting cut off on tab title
Fixes #335

(cherry picked from commit 263e467cdeb948b8f3679e2ea0282a291cf2f131)
2024-05-04 16:57:20 -04:00
Radon Rosborough 089e6268e7 Massively improve findFile performance (#728)
* Massively improve findFile performance

* Update libs.versions.toml

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 7ec2108812fbe0483111dbe996e29e5a621b583a)
2024-05-04 16:56:45 -04:00
AntsyLich 712cd1493f Address firebase ktx module deprecation
(cherry picked from commit 28dca3b7b818ad095008e7cd49ec07a82b0ebcad)

# Conflicts:
#	gradle/libs.versions.toml
2024-05-04 16:52:36 -04:00
AntsyLich bbc8adc3e8 Trust extension by repo (#570)
(cherry picked from commit 70cd688ac245a70a3146e2ac7374f24b0c3453ab)
2024-05-04 16:51:47 -04:00
AntsyLich 077b673c0a Fix some extension related issue and cleanups
- Extension being marked as not installed instead of untrusted after updating with private installer
- Extension update counter not updating due to extension being marked as untrusted
- Minimize `Key "extension-XXX-YYY" was already used` crash

(cherry picked from commit 21145144cdf550aa775047603e06e261951ebc42)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt
2024-05-04 16:51:26 -04:00
renovate[bot] 49eacf5178 fix(deps): update leakcanary to v2.14 (#715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit fa6dba6cc76f0b08cbc9bf222b0e087f4fb16d76)

# Conflicts:
#	gradle/libs.versions.toml
2024-05-04 16:01:19 -04:00
renovate[bot] 98d1dddf4a fix(deps): update dependency com.android.tools.build:gradle to v8.4.0 (#753)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 8a51d56c594e1f7ae4ebc01fc6a639292dde78bd)
2024-05-04 16:00:27 -04:00
renovate[bot] 37a616f3db fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-alpha04 (#749)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit a2f7d47a0a65bf88ac609b2227d440a7a2f841bf)
2024-05-04 16:00:14 -04:00
renovate[bot] ad18696a1a fix(deps): update dependency androidx.core:core-ktx to v1.13.1 (#748)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit b720f34267e4111466cdabf3a298d006231e4b55)
2024-05-04 15:59:53 -04:00
renovate[bot] 34bb012a1c fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-alpha04 (#751)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit c6a1412f18cb16f89c4ddeadb3448c141a49072e)
2024-05-04 15:59:43 -04:00
renovate[bot] 08c4989aa3 fix(deps): update aboutlib.version to v11.1.4 (#744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 6290cf222df922240575e2199459ab7b707d6ae2)

# Conflicts:
#	gradle/libs.versions.toml
2024-05-04 15:59:38 -04:00
FooIbar 14dae420f5 Log app crash exceptions in dumped crash logs (#742)
(cherry picked from commit a3d438e2f5b427eb8b4c391ab9fe10c5a83baf29)
2024-05-04 15:59:04 -04:00
w 65ed3c5ae6 Update subsampling-scale-image-view (#687)
Update libs.versions.toml

(cherry picked from commit 80461d883f7d6ca2203ae7455223ff49e8ef96ab)
2024-05-04 15:58:49 -04:00
FooIbar 5ae3508665 Use Coil pipeline instead of SSIV for image decode (#692)
(cherry picked from commit c3e7bb12f4cccf42dd3ea169111c771876e640fe)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
2024-05-04 15:58:41 -04:00
MajorTanya e32eb0e009 Add MyAnimeList issue autoclose (#703)
[skip ci] Add MyAnimeList issue autoclose

This rule is intended to automatically close issues that report
problems with linking MAL that would be solved with the standard
solution of updating & changing the default UA.

The RegEx might be too general, but there isn't any neat pattern in
the previously filed issues.

(cherry picked from commit 9a3ffe2ea6cbf7ef2c2966c304a54b715a5fa682)
2024-05-04 15:51:26 -04:00
renovate[bot] e0812ab5c8 fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.12 (#685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 213effa169e28e144f3e323290d865b02d0bf94b)
2024-05-04 15:51:13 -04:00
renovate[bot] df9f79c120 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.4 (#684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 25570147a1ca8ac374a75d7f29cf105bd686954b)
2024-05-04 15:51:04 -04:00
renovate[bot] 990eb33b98 fix(deps): update dependency androidx.activity:activity-compose to v1.9.0 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 2ad462b4d882c4a03359092515aa6b8d3cb4fd5d)
2024-05-04 15:50:50 -04:00
renovate[bot] e1bab1172a fix(deps): update dependency androidx.core:core-ktx to v1.13.0 (#690)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 7fd8f653529b8e1488dd57c051000abf2a80ed12)
2024-05-04 15:50:41 -04:00
FooIbar aeeff72bed Use Okio instead of java.io for image processing (#691)
(cherry picked from commit b152e3881bffd9050a8a0ed4030823886e3fe04f)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
#	core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
2024-05-04 15:50:20 -04:00
FooIbar 5895e78b39 Use m3 ripple and clean up interactionSource usage (#675)
Also remove a leftover of scoped storage adaptation.

(cherry picked from commit f27ca3b1b2f92258c213bca6b27d8eff4c7363ad)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt
2024-05-04 15:05:16 -04:00
FooIbar b24719a3e9 Update compose bom and fix renovate config for it (#674)
(cherry picked from commit 843daa5304d0b1a93ba69f8cc69791e446a58596)

# Conflicts:
#	.github/renovate.json5
2024-05-04 15:04:40 -04:00
renovate[bot] d551619d9d fix(deps): update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2 (#656)
Update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f080a4937e61d3dde5473876c34db8f16844e30c)

# Conflicts:
#	gradle/libs.versions.toml
2024-05-04 15:04:12 -04:00
renovate[bot] 06ad6c2e16 Update aboutlib.version to v11.1.3 (#654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 4c43a0ef66e9c8a321fb745b860319aaa074c57f)

# Conflicts:
#	gradle/libs.versions.toml
2024-05-04 15:03:39 -04:00
renovate[bot] df7e470e08 Update dependency com.android.tools.build:gradle to v8.3.2 (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit ea0fe2414e1e30b6e82ddf65144035283b31a5c4)
2024-05-04 15:02:59 -04:00
Cologler 03f32ebffd fix: check order before restore from backup (#1156) 2024-04-14 21:24:38 -04:00
KaiserBh ed20d25452 feat: syncing etag and overall improvement. (#1155)
* chore: don't log the access token from google.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: don't log the access token from google.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: forgot to add sy stuff.

The customInfo and readEntries wasn't taken into account, so when it was disabled it will always sync it because it's true by default in BackupOptions.kt. Should be fixed and now it doesn't reset the check mark.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* fix: same device sync.

When same device is initiating the sync just update the remote that.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* refactor: throw early.

When there is network failure or any sort during downloading just throw exception and stop syncing.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* refactor(gdrive): stream the json.

People with over 3k library can't sync because we are hitting OOM ```java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Failed to allocate a 370950192 byte allocation with 25165824 free bytes and 281MB until OOM, target footprint 333990992, growth limit 603979776```. This should fix that for them but only gdrive.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* feat: a demo for sync with new api

* refactor: perform early null checks

* feat: restore even if push failed

* feat: switch to protobuf

* chore: show error notification when sync fails.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* fix: update order by merge

* fix: call pushSyncData twice

---------

Signed-off-by: KaiserBh <kaiserbh@proton.me>
Co-authored-by: Cologler <skyoflw@gmail.com>
2024-04-14 19:50:48 -04:00
Shamicen 596a8d002f Safer password handling (#1146)
* no longer convert passwords to string

* also clear backing array of outputStream

* use fill and small refactor
2024-04-14 19:48:32 -04:00
Dexroneum 206d824ed2 [RU] Translations (#1129)
* [RU] Translations

* [RU] Translated the remaining lines
2024-04-14 19:48:03 -04:00
Howard Wu 97ed4e55ad Update Simplified Chinese Translation (#1126) 2024-04-14 19:47:39 -04:00
Jobobby04 739f7bc848 Move sync strings to SY files 2024-04-13 15:50:23 -04:00
AntsyLich e866e60b19 Revert "Update Scaffold fork (#10143)" + Cleanup
Causes delay of one frame before actual contentPadding is measured

This reverts commit ea15bc782a2cd603c78de7567a59e973dd50fd7f.

Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 56e66e041d22ebd680654df4aefa81578c0f5f11)
2024-04-13 12:46:19 -04:00
AntsyLich f135daeca5 MangaCoverFetcher: Small cleanups
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 13656959ae0606736f6ca9eb62699dc23e467c2f)
2024-04-13 12:46:05 -04:00
AntsyLich d80c19eb03 Remove unused imports
(cherry picked from commit 20e4cb26d6c264b13edb357ecfa25a6db21f19b8)
2024-04-13 12:42:52 -04:00
AntsyLich 2d47147172 Rework buildSrc and remove usage of subprojects
(cherry picked from commit e448e40406e8d9916120a278e42829a6f1b25a7a)

# Conflicts:
#	app/build.gradle.kts
#	buildSrc/src/main/kotlin/AndroidConfig.kt
#	i18n/build.gradle.kts
#	source-api/build.gradle.kts
2024-04-13 12:42:20 -04:00
AntsyLich de3570107e Fix build time zone in about screen
And slight cleanup

(cherry picked from commit aed53d3bdc85ce0e899fbb90b9f9cad0f1b86480)
2024-04-13 12:15:20 -04:00
renovate[bot] 5480495619 fix(deps): update sqldelight to v2.0.2 (#544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit d77f2f429d2603a5c2b805f2dc7255af41474cf8)
2024-04-13 11:59:22 -04:00
AntsyLich 694ef5f285 Disable mpp and agp compability warning
(cherry picked from commit c3fd2df6f55b70e49088f84209668c4530a9a9c6)
2024-04-13 11:59:14 -04:00
Weblate (bot) 472c97c580 Translations update from Hosted Weblate (#609)
* Translated using Weblate (Greek)

Currently translated at 99.8% (793 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/

* Translated using Weblate (German)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/

* Translated using Weblate (Persian)

Currently translated at 84.7% (673 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/

* Translated using Weblate (German)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Greek)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/

* Translated using Weblate (Galician)

Currently translated at 95.9% (762 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/

* Translated using Weblate (Javanese)

Currently translated at 38.8% (7 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/jv/

* Translated using Weblate (Galician)

Currently translated at 96.5% (767 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/gl/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Russian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Russian)

Currently translated at 99.7% (796 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (German)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Japanese)

Currently translated at 99.4% (794 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Czech)

Currently translated at 99.8% (797 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/

* Translated using Weblate (Italian)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Czech)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/

* Translated using Weblate (Italian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Russian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (German)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Amharic)

Currently translated at 34.3% (276 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/am/

* Translated using Weblate (Arabic)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/

* Translated using Weblate (Belarusian)

Currently translated at 42.0% (338 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/

* Translated using Weblate (Bulgarian)

Currently translated at 79.8% (641 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/

* Translated using Weblate (Bengali)

Currently translated at 79.2% (636 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/

* Translated using Weblate (Catalan)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/

* Translated using Weblate (Cebuano)

Currently translated at 55.0% (442 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/

* Translated using Weblate (Czech)

Currently translated at 99.2% (797 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/

* Translated using Weblate (Chuvash)

Currently translated at 74.5% (599 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/

* Translated using Weblate (Danish)

Currently translated at 39.9% (321 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/da/

* Translated using Weblate (German)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Greek)

Currently translated at 98.6% (792 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/

* Translated using Weblate (Esperanto)

Currently translated at 64.2% (516 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Basque)

Currently translated at 74.4% (598 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eu/

* Translated using Weblate (Persian)

Currently translated at 83.5% (671 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/

* Translated using Weblate (Finnish)

Currently translated at 84.0% (675 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (French)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/

* Translated using Weblate (Galician)

Currently translated at 95.2% (765 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/

* Translated using Weblate (Hebrew)

Currently translated at 89.7% (721 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/he/

* Translated using Weblate (Hindi)

Currently translated at 82.6% (664 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/

* Translated using Weblate (Croatian)

Currently translated at 98.7% (793 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Indonesian)

Currently translated at 98.6% (792 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/

* Translated using Weblate (Italian)

Currently translated at 99.3% (798 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Javanese)

Currently translated at 38.3% (308 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/

* Translated using Weblate (Georgian)

Currently translated at 52.5% (422 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ka/

* Translated using Weblate (Kazakh)

Currently translated at 86.1% (692 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kk/

* Translated using Weblate (Khmer (Central))

Currently translated at 26.7% (215 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/km/

* Translated using Weblate (Kannada)

Currently translated at 62.2% (500 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kn/

* Translated using Weblate (Korean)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/

* Translated using Weblate (Lithuanian)

Currently translated at 84.9% (682 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lt/

* Translated using Weblate (Latvian)

Currently translated at 93.3% (750 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/

* Translated using Weblate (Marathi)

Currently translated at 26.6% (214 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/mr/

* Translated using Weblate (Malay)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nb_NO/

* Translated using Weblate (Nepali)

Currently translated at 99.3% (798 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Dutch)

Currently translated at 92.9% (746 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/

* Translated using Weblate (Norwegian Nynorsk)

Currently translated at 33.6% (270 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nn/

* Translated using Weblate (Polish)

Currently translated at 98.6% (792 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.6% (792 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/

* Translated using Weblate (Portuguese)

Currently translated at 88.6% (712 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/

* Translated using Weblate (Romanian)

Currently translated at 97.8% (786 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/

* Translated using Weblate (Russian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Sanskrit)

Currently translated at 71.3% (573 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sa/

* Translated using Weblate (Yakut)

Currently translated at 51.3% (412 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sah/

* Translated using Weblate (Sardinian)

Currently translated at 93.3% (750 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/

* Translated using Weblate (Kurdish (Southern))

Currently translated at 29.8% (240 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sdh/

* Translated using Weblate (Slovak)

Currently translated at 78.7% (632 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/

* Translated using Weblate (Albanian)

Currently translated at 86.6% (696 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sq/

* Translated using Weblate (Serbian)

Currently translated at 98.6% (792 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/

* Translated using Weblate (Swedish)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/

* Translated using Weblate (Telugu)

Currently translated at 24.5% (197 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/te/

* Translated using Weblate (Thai)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/

* Translated using Weblate (Turkish)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/

* Translated using Weblate (Ukrainian)

Currently translated at 98.5% (791 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/

* Translated using Weblate (Uzbek)

Currently translated at 44.4% (357 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uz/

* Translated using Weblate (Vietnamese)

Currently translated at 96.3% (774 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/

---------

Co-authored-by: Syrodil Eventalious <giannis.yalanskyi@gmail.com>
Co-authored-by: NukeSource <dede48076@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Arash <ara.khoram95@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Mr. Fakezay <fakezaydev@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: gekka <1778962971@qq.com>
Co-authored-by: akir45 <akkn0708@gmail.com>
Co-authored-by: Matyáš Caras <matyas@caras.cafe>
Co-authored-by: Federico Pierantoni <federico.pieranton@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
(cherry picked from commit 34bf5c6f87d74df2dcc6d0f23f5a73425d2fd6ef)
2024-04-13 11:59:05 -04:00
Weblate (bot) 8b098b38f8 Translations update from Hosted Weblate (#508)
* Translated using Weblate (Greek)

Currently translated at 99.8% (793 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/

* Translated using Weblate (German)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/

* Translated using Weblate (Persian)

Currently translated at 84.7% (673 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/

* Translated using Weblate (German)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Greek)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/

* Translated using Weblate (Galician)

Currently translated at 95.9% (762 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/

* Translated using Weblate (Javanese)

Currently translated at 38.8% (7 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/jv/

* Translated using Weblate (Galician)

Currently translated at 96.5% (767 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/

* Translated using Weblate (Galician)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/gl/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Russian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (795 of 795 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Russian)

Currently translated at 99.7% (796 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (German)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Japanese)

Currently translated at 99.4% (794 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Czech)

Currently translated at 99.8% (797 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/

* Translated using Weblate (Italian)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (798 of 798 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Czech)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/

* Translated using Weblate (Italian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Russian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (German)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Japanese)

Currently translated at 99.7% (801 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (803 of 803 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

---------

Co-authored-by: Syrodil Eventalious <giannis.yalanskyi@gmail.com>
Co-authored-by: NukeSource <dede48076@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Arash <ara.khoram95@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Mr. Fakezay <fakezaydev@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: gekka <1778962971@qq.com>
Co-authored-by: akir45 <akkn0708@gmail.com>
Co-authored-by: Matyáš Caras <matyas@caras.cafe>
Co-authored-by: Federico Pierantoni <federico.pieranton@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
(cherry picked from commit 6abaa47f5beacdc36a40cec98e3d7f02ac77f320)

# Conflicts:
#	i18n/src/commonMain/resources/MR/gl/strings.xml
#	i18n/src/commonMain/resources/MR/hu/strings.xml
#	i18n/src/commonMain/resources/MR/tr/plurals.xml
#	i18n/src/commonMain/resources/MR/zh-rTW/strings.xml
2024-04-13 11:58:56 -04:00
Maddie Witman 5e0585d724 Moves upcoming requirement from existence to current day or later. (#606)
* Moves upcoming requirement from existence to current day or later.

* Suppress millis conversion warning

(cherry picked from commit c9fddf9e388cff5e4071a89719825dee466deaf4)
2024-04-13 11:56:15 -04:00
MajorTanya 3e438a9e87 Add ProGuard rule to keep mihon namespace classes (#605)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 555d2f834fb64df9a56fdf4f54d528c15fefa4cb)
2024-04-13 11:56:06 -04:00
Andreas 8046c1a540 Fix Migrator not doing work (#604)
(cherry picked from commit 6b3423a12b620dd2aae635ac4e859d00a4f62ceb)
2024-04-13 11:55:47 -04:00
renovate[bot] 1f3f6cd4df fix(deps): update detekt to v1.23.6 (#595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 86fbd20665613cacb8d3c733aed9731792a07392)
2024-04-13 11:55:31 -04:00
renovate[bot] a62a5ed650 fix(deps): update aboutlib.version to v11.1.1 (#592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit f1660beafc8303ba8d7ebfd160029e869a077f69)

# Conflicts:
#	gradle/libs.versions.toml
2024-04-13 11:55:24 -04:00
Maddie Witman a320903bc0 New Feature: Introduce Upcoming page to Mihon (#420)
* Work in progress upcoming feature

* Checkpointing WIP upcoming feature

* Functional Upcoming Screen

* Rename UpdateCalendar to UpdateUpcoming

* Converted Strings to resources

* Cleanup

* Fixed detekt issues

* Removed Link icon per @AntsyLich's suggestion.

* Detekt

* Fixed Calendar display on wide form factor devices

* Added Key to upcoming lazycolumn

* Updated tablet mode UI to support two column view

* Updated header creation logic

* Updated header creation logic... again

* Moved stray string to resources

* Fixed PR Comments and query refactor

* Tweaks to query, refactored to flow, comments on calendar

* Switched to Date Formatter

* Cleaned up date formatter

* More Refactor work

* Updated Calendar to support localized week formats

* Fixed year format

* Refactored Header animation

* Moved upcoming FAQ

* Completed YearMonth Migration

* Replaced currentYearMonth with delegate

* Even more cleanup

* cleaned up alignment modifiers

* Click Handler and other refactors

* Removed Wrapped Content Height/Size/extra clips

* Huge Refactor for CalendarDay

* Another cleanup attempt

* Migrated to new mihon.feature.* module pattern

* changed access modifier

* A Bunch of changes from the next round of reviews

* Cleanups

* Cleanup 2

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 72222ad86d6fb328d20eead86c6357833d08c061)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/DomainModule.kt
#	gradle/libs.versions.toml
2024-03-28 17:35:51 -04:00
Andreas a6c4f01c74 Migrator improvements (#588)
(cherry picked from commit 0265c16eb239518d52b7e9fb4200b5b003418d5d)

# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
2024-03-28 17:26:09 -04:00
Jobobby04 a657c65261 Reduce build warnings 2024-03-28 17:18:38 -04:00
Jobobby04 5455daf96b Fix build 2024-03-28 17:13:03 -04:00
Jobobby04 d40bc2b41b Update Dependencies 2024-03-28 16:58:20 -04:00
Jobobby04 527ca85c39 Update WebView to support lower minSDK 2024-03-28 16:57:12 -04:00
Maddie Witman 189714eaf1 Migrated from Accompanist Webview to KevinZou WebView (#569)
* Migrated from Accompanist Webview to KevinZou WebView to preempt deprecation

* Removed old webview from version library

(cherry picked from commit ba9cfd867c028551c0b0740922c5130b14455c9f)
2024-03-28 16:54:28 -04:00
Andreas 90d5104bdc Rewrite Migrations (#577)
* Rewrite Migrations

* Fix Detekt errors

* Do migrations synchronous

* Filter and sort migrations

* Review changes

* Review changes 2

* Fix Detekt errors

(cherry picked from commit 666d6aa117756f0a9a57b31f91b7acb0ee5d7409)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
2024-03-28 16:53:08 -04:00
Jobobby04 ceff887a10 Fix build 2024-03-27 17:23:51 -04:00
Jobobby04 2197bd0451 Revert "Migrated from Accompanist Webview to KevinZou WebView (#569)"
This reverts commit 268b483182.
2024-03-27 16:40:40 -04:00
AntsyLich 861a810961 Fix mishap in e020ae5ed558e80742ef0ad8bfa0f69af0959d5a
(cherry picked from commit 6965e59a643c67a2bf81b3c69ec70268e5da5797)
2024-03-27 16:28:52 -04:00
AntsyLich 81984c25df Fix more TypeReference issues and cleanup
(cherry picked from commit e020ae5ed558e80742ef0ad8bfa0f69af0959d5a)
2024-03-27 16:28:43 -04:00
MajorTanya b21d685a37 Fix extension repo crash with TypeReference issue (#574)
Fix by @AntsyLich.

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 05071b420572a8fa93a55ab02c743c7da4fd3b3a)
2024-03-27 16:28:35 -04:00
MajorTanya fb4d9209f8 Fix repo name used for URL instead of baseUrl (#572)
* Fix repo name used for URL instead of baseUrl

This applies to both the item being shown in the screen as well as the
"copy to clipboard" button. Before, copying a repo url would return
"The Repo Name/index.json.min". This PR fixes that.

* Correct Misunderstanding

Passing the whole ExtensionRepo data class through now, using the name
for display purposes and the baseUrl for copying the URL.

(cherry picked from commit da20d00481f112802aade5d63fc1eca15c5496ba)
2024-03-27 16:28:19 -04:00
MajorTanya 9ee0034c9a Refactor the ExtensionRepoService to use DTOs (#573)
* Refactor the ExtensionRepoService to use DTOs

Slightly refactored the `ExtensionRepoService` so it uses a DTO with
`parseAs` to avoid parsing the JSON response by hand.

The default Json instance Injekt provides here has
`ignoreUnknownKeys` enabled, so the `ExtensionRepoMetaDto` only
specifies the meta key of the response content.

The extension function `toExtensionRepo` allows for mapping the new
DTO to the `domain` `ExtensionRepo` data class.

* Implement feedback

- Removed SerialName of the ExtensionRepoMetaDto property and renamed
it `meta`, same as the incoming attribute.
- Added a more general catch clause that also logs the occurring
Exception

Detekt likes to complain about TooGenericExceptionCaught, hence the
Suppress annotation on the function.

(cherry picked from commit 8c437ceecf3c5d8d944a70439d3549e21d751736)
2024-03-27 16:28:09 -04:00
Maddie Witman 268b483182 Migrated from Accompanist Webview to KevinZou WebView (#569)
* Migrated from Accompanist Webview to KevinZou WebView to preempt deprecation

* Removed old webview from version library

(cherry picked from commit ba9cfd867c028551c0b0740922c5130b14455c9f)
2024-03-27 16:27:47 -04:00
Maddie Witman 2af6e7be32 Grab extension repo detail from repo.json and include in DB (#506)
* WIP Extension Repo DB Support

* Wired in to extension screen, browse settings screen

* Detekt changes

* Ui tweaks and open in browser

* Migrate ExtensionRepos on Update

* Migration Cleanup

* Slight cleanup / error handling

* Update ExtensionRepo from Repo.json during extension search.
Added Manual refresh in extension repos page.

* Split repo fetching into separate API module, major refactor work

* Removed development strings

* Moved migration to #3

* Fixed rebase

* Detekt changes

* Added Replace Repository Dialog

* Cleanup, removed platform specific code, PR comments

* Removed extra function, reverted small change

* Detekt cleanup

* Apply suggestions from code review

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Fixed error introduced in cleanup

* Tweak for multiline when

* Moved getCount() to flow

* changed getCount to non-suspend, used property delegation

* Apply suggestions from code review

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Fixed formatting with updated comment string

* Big wave of PR comments, renaming/other tweaks

* onOpenWebsite changes

* onOpenWebsite changes

* trying to make single line

* Renamed ExtensionRepoApi.kt to ExtensionRepoService.kt

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 4b4e46851083c29ca412c114b1b96136fcc21442)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
#	app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
#	data/src/main/sqldelight/tachiyomi/migrations/3.sqm
2024-03-27 16:27:24 -04:00
renovate[bot] 3ecf86ae35 fix(deps): update aboutlib.version to v11 (major) (#473)
* fix(deps): update aboutlib.version to v11

* Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit e75488f5d98fb600408d065d1c5060520a8921a4)

# Conflicts:
#	gradle/libs.versions.toml
2024-03-27 16:23:15 -04:00
renovate[bot] 41bb0e08ba chore(deps): update dependency gradle to v8.7 (#567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 3838dbcf0805a9688ad6b0a03dc44f64de855e12)
2024-03-27 16:22:43 -04:00
renovate[bot] 0bb1eb2da1 chore(deps): update kotlin (#499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit b3ca097e5a0a2feb582952f88a369f5a614c593f)
2024-03-27 16:22:34 -04:00
AntsyLich 919df9a7cf Add reference to compose compiler in compose.versions.toml so renovate can catch it
(cherry picked from commit 70c2443e82161378a3f653bac110767370b62c46)
2024-03-27 16:22:16 -04:00
Maddie Witman 2ea488bff5 Rework Duplicate Dialog and Allow Migration (#492)
* (Mostly) Working Manga screen migration via duplicate dialog

* Fully working migrate from Browse Search

* Small tweaks for Antsy

* Update app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt

* Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit c0a888807b78891b28c6f6b9f16b719e24b03de1)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
2024-03-27 16:21:57 -04:00
FooIbar ec30ccccc2 Fix webtoon last visible item position calculation (#562)
Covers the case when image height > screen height.

(cherry picked from commit 34930920a50be25ca05024200bf871512962e3d0)
2024-03-27 15:33:33 -04:00
renovate[bot] 780bdcbe55 fix(deps): update dependency com.google.firebase:firebase-analytics-ktx to v21.6.1 (#561)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 6682b5dd39ea46ecc57e040a558420d512893ed8)

# Conflicts:
#	gradle/libs.versions.toml
2024-03-27 15:33:26 -04:00
FooIbar a90bc4c7fa Fix recycled item's height being 0 in webtoon mode (#563)
Which will prevent the new image from being decoded until it's visible.

(cherry picked from commit ef6cad58fe0eeb7bfec7e8df33ada87946fa85d3)
2024-03-27 15:32:25 -04:00
AntsyLich 5e421c6f0e Address detekt issues
(cherry picked from commit 7e9340aa7f1021eabb4ae01eb0f4cbdfb6cc0589)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
2024-03-27 15:32:17 -04:00
w 742fdc19ca Update image-decoder, color management (#523)
* Update image-decoder, color management

* move display profile pref

* remove true color pref

* Move Display Profile settings to a new section

* Partially revert "remove true color pref"

This partially reverts commit e1a75816950e100936699279e1618adb2fa341aa.

* Tweak label

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 3f2c8e9ef6db540c77b818ffdf771674b3e46c8b)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	gradle/libs.versions.toml
2024-03-27 15:31:28 -04:00
renovate[bot] 74505565ef fix(deps): update dependency org.apache.commons:commons-compress to v1.26.1 (#502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit a29870c01e3b79952b0882441e266009bf1119f2)
2024-03-27 15:28:21 -04:00
renovate[bot] f041ed5b2a fix(deps): update dependency com.android.tools.build:gradle to v8.3.1 (#543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 583aa430ba9e8e7454076afc049f812bd3df21df)
2024-03-27 15:28:04 -04:00
MajorTanya 8762b20ab6 Switch to seconds for DATE_MODIFIED of saved pages (#552)
While most Android skins are seemingly able to handle the millisecond
format, the documentation technically specifies seconds. This seems to
be causing issues on Samsung devices using the Samsung Gallery app,
which renders the millisecond timestamps as if they were second ones,
causing the dates to be set at some point in the year 56189.

This change should fix that issue on Samsung devices and have no real
impact on the rest.

(cherry picked from commit 0ea0138a73466af3d371a48e344753844e9bc3d8)
2024-03-27 15:27:54 -04:00
AntsyLich 5a71889679 Fix regression from coil3 migration
Fixes #495

Co-authored-by: jobobby04 <17078382+jobobby04@users.noreply.github.com>
(cherry picked from commit 59bedb33ff59ad5db1df2e93567a2266fb63eacc)

# Conflicts:
#	core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
2024-03-27 15:27:43 -04:00
renovate[bot] a4983eb004 fix(deps): update dependency io.kotest:kotest-assertions-core to v5.8.1 (#528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit ebee2751109daf7be86d93736806374d6255be7c)
2024-03-27 15:27:07 -04:00
renovate[bot] 818bc7f75a fix(deps): update dependency com.squareup.okio:okio to v3.9.0 (#529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 015d9b3bd057fe218d5bab77fa0736be5488eb4d)
2024-03-27 15:26:52 -04:00
AntsyLich 7de6fa8c23 Disable SerialVersionUIDInSerializableClass detekt rule
(cherry picked from commit bcdf17fe27dfb140e120ef2223aceb79668b8c16)
2024-03-27 15:26:08 -04:00
Jobobby04 edca9039e5 Fix sync stalled 2024-03-25 18:32:27 -04:00
Jobobby04 fb1649125c Actually fix animated images 2024-03-18 09:43:14 -04:00
Jobobby04 0767526f18 Revert "Re-Add Animated Image Decoders to Coil"
This reverts commit 5d1b1408eb.
2024-03-18 09:42:22 -04:00
Jobobby04 5d1b1408eb Re-Add Animated Image Decoders to Coil 2024-03-17 23:17:27 -04:00
Jobobby04 2f54f00bf7 Revert "Minor fix for history url"
This reverts commit 28edaca869.
2024-03-17 20:08:03 -04:00
ɴᴇᴋᴏ 87feb58055 Add files via upload (#1120) 2024-03-17 19:57:33 -04:00
Jobobby04 28edaca869 Minor fix for history url 2024-03-17 19:56:06 -04:00
Jobobby04 d14f012bbb Update firebase 2024-03-17 19:53:23 -04:00
Jobobby04 adc6bbf54f Minor doc fix 2024-03-17 19:53:12 -04:00
Jobobby04 2b064baca1 Update baseline 2024-03-17 19:52:59 -04:00
Jobobby04 983a80ba42 History url is not globally unique 2024-03-17 19:52:38 -04:00
Fermín Cirella 911e959fcf Add option to reset custom manga info (#1118)
* Add option to reset custom manga info

* Remove extra line

* Update app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2024-03-16 23:59:19 -04:00
KaiserBh a425cae73b fix: The trigger for library update wasn't working properly. (#1119)
Missed them, so it was always updating library without actually syncing even when the trigger was on.

Signed-off-by: KaiserBh <kaiserbh@proton.me>
2024-03-16 23:56:00 -04:00
Jobobby04 d12a9d329b [skip ci] Add instructions for supporting Cloud Sync Google Drive Impl 2024-03-16 15:59:00 -04:00
Jobobby04 9018757496 Oops 2024-03-16 13:47:55 -04:00
ɴᴇᴋᴏ b0d91fa83f Update zh-rTW (#1117)
* Update plurals.xml

* Update strings.xml

* Update plurals.xml

* Update strings.xml

* Update plurals.xml

* Update plurals.xml

* Update strings.xml

* Update plurals.xml

* plurals.xml

* plurals.xml

* Delete i18n/src/commonMain/resources/MR/zh-rTW/strings.xml

* Add files via upload
2024-03-16 13:46:27 -04:00
Jobobby04 1caa929aa0 Add preview prefix 2024-03-16 13:45:47 -04:00
Jobobby04 04e5be12e1 Write client_secrets.json on build 2024-03-16 13:37:41 -04:00
Jobobby04 1136644a57 Remove Client Secrets 2024-03-16 13:32:58 -04:00
Jobobby04 d70258b956 Cleanup sync code 2024-03-16 13:14:40 -04:00
Jobobby04 54cb379a50 Update Detekt baseline 2024-03-16 12:36:51 -04:00
Jobobby04 0e959c4594 Move strings to SY strings 2024-03-16 12:23:30 -04:00
Shamicen 6719f22eff implement mihonapp/mihon#326 (#1104)
* implement mihonapp/mihon#326

Archives are now being read from channels

Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* disable parallelisms for loading into memory

* switched to mutex

* detekt changes

* more detekt baseline changes

---------

Co-authored-by: FooIbar <118464521+FooIbar@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2024-03-16 11:59:00 -04:00
Jobobby04 45711cd394 Update Client Secret 2024-03-16 11:55:48 -04:00
KaiserBh 334e9fb680 feat: add cross device sync (#1005)
* feat: add cross device sync.

* chore: add google api.

* chore: add SY specifics.

* feat: add backupSource, backupPref, and "SY" backupSavedSearches.

I forgot to add the data into the merging logic, So remote always have empty value :(. Better late than never.

* feat(sync): Allow to choose what to sync.

Various improvement and added the option to choose what they want to sync. Added sync library button to LibraryTab as well.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* oops.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* refactor: fix up the sync triggers, and update imports.

* refactor

* chore: review pointers.

* refactor: update imports

* refactor: add more error guard for gdrive.

Also changed it to be app specific, we don't want them to use sync data from SY or other forks as some of the model and backup is different. So if people using other forks they should use the same data and not mismatch.

* fix: conflict and refactor.

* refactor: update imports.

* chore: fix some of detekt error.

* refactor: add breaks and max retries.

I think we were reaching deadlock or infinite loop causing the sync to go forever.

* feat: db changes to accommodate new syncing logic.

Using timestamp to sync is a bit skewed due to system clock etc and therefore there was a lot of issues with it such as removing a manga that shouldn't have been removed. Marking chapters as unread even though it was marked as a read. Hopefully by using versioning system it should eliminate those issues.

* chore: add migrations

* chore: version and is_syncing fields.

* chore: add SY only stuff.

* fix: oops wrong index.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: review pointers.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: remove the option to reset timestamp

We don't need this anymore since we are utilizing versioning system.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* refactor: Forgot to use the new versioning system.

I forgot to cherry pick this from mihon branch.

* chore: remove isSyncing from Chapter/Manga model.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: remove unused import.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: remove isSyncing leftover.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* chore: remove isSyncing.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

* refactor: make sure the manga version is bumped.

When there is changes in the chapters table such as reading or updating last read page we should bump the manga version. This way the manga is synced properly as in the History and last_read history is synced properly. This should fix the sorting issue.

Signed-off-by: KaiserBh <kaiserbh@proton.me>

---------

Signed-off-by: KaiserBh <kaiserbh@proton.me>
2024-03-16 11:53:20 -04:00
ɴᴇᴋᴏ 6e0bc981a6 Update README.md (#1113)
Tachi -> Mihon
2024-03-15 20:36:28 -04:00
Cuong M. Tran b7e55bc9f8 Update detekt's baseline & run detekt for future build (#1106)
* Update detekt baseline from mihon

* Update detekt baseline to current code & enable gradle's detekt task for future build
2024-03-15 20:35:39 -04:00
Shamicen a069e577ba Change preferences containing passwords to appStateKeys (#1083)
* Change preferences containing passwords to appStateKeys

* Change versionCode to 65

* fix merge conflict and add instructions to get library back after migration
2024-03-15 20:28:37 -04:00
Jobobby04 0eb622643b Use github run_number to create tag 2024-03-15 20:18:44 -04:00
Jobobby04 d93d0eea89 Shorten Anilst UA(hopefully Cloudflare likes this one) 2024-03-15 20:14:37 -04:00
AntsyLich 82846205b2 Fix crash in track date selection dialog
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit f08713587685ddb27cb8ce7184e2dd21ae7968ae)
2024-03-15 20:08:14 -04:00
AntsyLich 4a4fecb1e8 Bump default user agent
(cherry picked from commit f66f52c244b786ae09f8e4ae683575907068d15f)
2024-03-15 20:08:06 -04:00
renovate[bot] ee6bc20f27 Update dependency io.nlopez.compose.rules:detekt to v0.3.12 (#500)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 0d6f426dbd8874c7861b6cc245f9e6ff43dbbe2c)
2024-03-15 20:07:56 -04:00
Weblate (bot) 446a5cd5b3 Translations update from Hosted Weblate (#445)
* Translated using Weblate (Esperanto)

Currently translated at 65.1% (517 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/

* Translated using Weblate (Serbian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/

* Translated using Weblate (Croatian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hr/

* Translated using Weblate (Serbian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sr/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hans/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/

* Translated using Weblate (Filipino)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fil/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hu/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Polish)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Russian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/

* Translated using Weblate (Russian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ru/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (18 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/id/

* Translated using Weblate (Dutch)

Currently translated at 94.2% (748 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/

* Translated using Weblate (Dutch)

Currently translated at 94.4% (17 of 18 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/nl/

* Translated using Weblate (German)

Currently translated at 100.0% (794 of 794 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/

---------

Co-authored-by: Radoŝ Porka <animatorzPolski@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: David Katrinka <davidkatrinka1995@gmail.com>
Co-authored-by: gekka <1778962971@qq.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Tim Bolhoeve <bolhoevetim@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
(cherry picked from commit edd7d0522c305a8aec8ab524214d3a26106dac31)

# Conflicts:
#	i18n/src/commonMain/resources/MR/hu/strings.xml
#	i18n/src/commonMain/resources/MR/sr/strings.xml
#	i18n/src/commonMain/resources/MR/zh-rTW/strings.xml
2024-03-15 20:07:49 -04:00
KaiserBh cdb07c893b feat: db changes to accommodate new cross device syncing logic. (#450)
* feat: db changes to accommodate new syncing logic.

Using timestamp to sync is a bit skewed due to system clock etc and therefore there was a lot of issues with it such as removing a manga that shouldn't have been removed. Marking chapters as unread even though it was marked as a read. Hopefully by using versioning system it should eliminate those issues.

* chore: add new line.

* chore: remove isSyncing from Chapter/Manga model.

* chore: remove isSyncing leftover.

* chore: remove isSyncing.

* refactor: remove isSync guard.

Just use it directly to 1 now since we don't have the isSyncing field in Manga or Chapter.

* Lint and stuff

* Add missing ,

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 4ae9dbe52487185ef9ee25f58fabe5025bb2278b)

# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt
#	data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt
#	data/src/main/sqldelight/tachiyomi/migrations/2.sqm
#	domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt
2024-03-15 20:06:20 -04:00
Redjard a4d88515fb Fix shizuku being buggy for multi user setups (#494)
* Fix #493

Fetch the current userid separately because shizuku always runs as the main user and would otherwise install and update for the main user

* Update app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 402e579a69391e7633754066eb8e6480bd9f247d)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt
2024-03-15 19:55:36 -04:00
FooIbar 345d0821c6 Fix dual page split for local source (#485)
`InputStream.available()` is implementation-dependent, should never assume it will return the total number of bytes in the stream.

(cherry picked from commit d0e64d3a66d227ca61fc8d956b03cab5ac3b84f0)

# Conflicts:
#	core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt
2024-03-15 19:54:06 -04:00
az4521 a9fd1f8811 Update image-decoder (#466)
Use newer image-decoder lib

fixes crashing when trying to load corrupt images below 12 bytes in size

(cherry picked from commit 154f4d327caea9ceef6a53e739324ee0a9ed959d)

# Conflicts:
#	gradle/libs.versions.toml
2024-03-15 19:53:26 -04:00
Jobobby04 31e5ba4caf Fix multiple issues regarding sources loading too late 2024-03-15 19:51:56 -04:00
Jobobby04 202900edf0 Fix build error after Android Gradle 8.3 2024-03-03 22:39:58 -05:00
AntsyLich f79959c7bc Fix ChapterDownloadIndicator
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit d8b9a9f593911569ff2bceb49b4f020978d0d2e1)
2024-03-03 22:01:15 -05:00
AntsyLich 237d8d6b33 Small cleanup
(cherry picked from commit b7e091d5d039e00cababc7daf555280df6cf9c03)
2024-03-03 22:01:05 -05:00
renovate[bot] 117e0d5792 Update dependency com.android.tools.build:gradle to v8.3.0 (#471)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 31e052ac15679496f9f2c3882e53096119e0e6cf)
2024-03-03 22:00:56 -05:00
renovate[bot] 64bbe941a4 Update dependency io.mockk:mockk to v1.13.10 (#470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 60480686daa9456c78d7cb45c5ad2e23644d1bd2)
2024-03-03 22:00:49 -05:00
Jobobby04 dcd44c42ed Merge branch 'release' 2024-03-02 15:39:33 -05:00
Jobobby04 6c563d7619 Fix for duplicate read 2024-03-02 15:38:32 -05:00
Jobobby04 97f22c500b 1.10.5 2024-03-02 15:38:05 -05:00
Jobobby04 9cbeccfa15 Fix for duplicate read 2024-03-02 15:34:28 -05:00
MajorTanya 86722a31d0 Fix DelayedTrackingUpdateJob spam on update errors (#411)
* Fix DelayedTrackingUpdateJob spam on update errors

DelayedTrackingUpdateJob would start spamming when it encountered an
error (e.g. a tracker has an issue) and never stop.
This seems to stem from a circular dependency between the Job's
`doWork` and TrackChapter's `await`.

TrackChapter sets up a completely new instance of the
DelayedTrackingUpdateJob if any Exception was thrown during the track
update.

This causes the Job to get replaced (as per the WorkManager's set
ExistingWorkPolicy).

Because of this, the guard clause at the start of doWork would never
trigger, as all instances of the Job would report being the 0th try
(because they were completely new instances).

This simple fix introduces a boolean `isRetry` parameter to
TrackChapter's await method, which is set to `false` by default.
DelayedTrackingUpdateJob however sets this parameter to `true`, which
means TrackChapter won't try to set up the Job again.

* Rename isRetry parameter to setupJobOnFailure

This also inverts the logic, so true & false were swapped.

(cherry picked from commit 617bf491eee1d1010dc23c3f6df5d148700feb44)
2024-03-02 15:32:56 -05:00
AntsyLich 589b33a673 Fix detekt issue
(cherry picked from commit 9254079957e383e4aa5c914ccd9705612e0892d0)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt
2024-03-02 15:32:31 -05:00
AntsyLich dae0348710 Don't add custom User Agent for MAL
Closes #298

(cherry picked from commit 7974a1fc0c2b0e237d5704a033a60ec0fd60cdbc)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt
2024-03-02 15:32:29 -05:00
Jobobby04 a7f6155627 Ignore chapters with 0 or under chapter numbers
(cherry picked from commit d8082de1db)
2024-03-02 15:31:38 -05:00
Jobobby04 7f5bc4a3e5 Use new gradle workflow 2024-03-02 15:28:40 -05:00
Jobobby04 ec32278f1a Throw IOException 2024-03-02 14:25:31 -05:00
AntsyLich c02c5aa915 Revert changes to gradle.properties
(cherry picked from commit d6ba3c824972c8a2196516edd42e9e8be6385f36)
2024-03-02 14:24:34 -05:00
AntsyLich f267f2ad5b detekt my beloved
(cherry picked from commit c56f4665ef0276c54f5abebd9ab93e2e283739a6)
2024-03-02 14:24:25 -05:00
renovate[bot] 03bc09c1aa Update dependency me.saket.swipe:swipe to v1.3.0 (#343)
* Update dependency me.saket.swipe:swipe to v1.3.0

* Update MangaChapterListItem.kt

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit b51a0a38bd9849b573bc2de0d936d6ab2becb83c)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt
2024-03-02 14:24:16 -05:00
AntsyLich c4df418081 Switch to Coil3
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit f72b6e4d7c1f2f93d705402e4d80c94160bef54d)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt
2024-03-02 14:22:54 -05:00
AntsyLich a9c79d5fb3 Remove custom Pager
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 84984ef7e1d7242924120cd2f171cb9dd75bc916)
2024-03-02 14:02:34 -05:00
AntsyLich 529100a947 Enable experimental Compose compiler optimization
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit 9f48def1e2718abd5b4aad3cb6ee8af6b39e76cc)
2024-03-02 14:02:12 -05:00
AntsyLich 062f6d5aa0 ChapterDownloadIndicator: Remove composed modifier usage
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
(cherry picked from commit e83bfb0d3511f3c049d1dfe6ca13e74467655e08)
2024-03-02 14:01:43 -05:00
AntsyLich deb0a95985 Upgrade Compose
(cherry picked from commit 0301362430af6f74678dcae801b70d6aeb371a56)
2024-03-02 14:01:34 -05:00
Shamicen ca70f80900 Made some changes to ComicInfo metadata (#459)
* Made some changes to ComicInfo metadata

The web field now contains a " " separated list of source and tracker urls.
The translator field will now use the source name if the scanlator field is empty.

* lint

* use already existing source instance

* made translator not nullable

* implemented requested changes

created new Mihon exclusive ComicInfo source field  and populated it with SourceName

reverted previous changes to translator field

* Update core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Update app/src/main/java/eu/kanade/domain/manga/model/Manga.kt

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Update app/src/main/java/eu/kanade/domain/manga/model/Manga.kt

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Update app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* Update app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 4bfc5e7b51452a1a9cac3a3644e6343d1c47aa95)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/manga/model/Manga.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
#	core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt
2024-03-02 14:01:12 -05:00
renovate[bot] 23e3ec20b6 Update Kotlin (#422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 802a2c5c1ea73c79967fb5b535b6249506df1870)
2024-03-02 13:59:43 -05:00
renovate[bot] 32d97ed194 Update dependency io.coil-kt:coil-bom to v2.6.0 (#447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit c1c174698525e982107cc955d8dff711c4498989)
2024-03-02 13:59:34 -05:00
renovate[bot] 167a4e9820 Update dependency org.junit.jupiter:junit-jupiter to v5.10.2 (#419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 4fcbd80a8ec72f4113d0ee9d457c000dcd8e7440)
2024-03-02 13:59:25 -05:00
renovate[bot] aef0b50663 Update dependency com.google.firebase:firebase-analytics-ktx to v21.5.1 (#417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 16969193c718cc8e6cabb473ecb1d0d7bc02f33e)

# Conflicts:
#	gradle/libs.versions.toml
2024-03-02 13:59:16 -05:00
renovate[bot] 5b5e6c8f44 Update dependency androidx.test.uiautomator:uiautomator to v2.3.0 (#416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 55637ddfe154c2ed60efb5d9d1466d2106c97f9f)
2024-03-02 13:58:48 -05:00
renovate[bot] 5dc96384bd Update detekt to v1.23.5 (#267)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit e50358dc4be7cbd866a79d052edc62689f0e4ca5)
2024-03-02 13:58:39 -05:00
AntsyLich affdea3ec2 Fix detekt issue
(cherry picked from commit 9254079957e383e4aa5c914ccd9705612e0892d0)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt
2024-03-02 13:57:49 -05:00
AntsyLich 1436d86c7e Don't add custom User Agent for MAL
Closes #298

(cherry picked from commit 7974a1fc0c2b0e237d5704a033a60ec0fd60cdbc)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt
2024-03-02 13:56:42 -05:00
renovate[bot] b7c9eaa981 Update dependency com.squareup.okio:okio to v3.8.0 (#423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 1521c359412518731ef7338255cdd280321faa70)
2024-03-02 13:56:12 -05:00
Splintor db99ab526a Allow disabling reader's zoom out (#302)
* Allow disabling reader's zoom out (#62)

* Renamed disable zoom out pref and string

* Zoom to default rate if the scale is inferior

* Fixed null value check and formatting

* Fixed detekt

(cherry picked from commit c15f3f2fd5b11cc9c2088ae2fa444f4fe35ea740)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt
2024-03-02 13:55:43 -05:00
renovate[bot] 133c34dee2 Update dependency com.google.gms:google-services to v4.4.1 (#418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 21020e1797ae9c0373e5132e1dbcd9a64455c17f)
2024-03-02 13:48:11 -05:00
Weblate (bot) 775cf258ba Translations update from Hosted Weblate (#301)
* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/

* Translated using Weblate (Chuvash)

Currently translated at 75.7% (601 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Chuvash)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cv/

* Translated using Weblate (Esperanto)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/eo/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/

* Translated using Weblate (Polish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/

* Translated using Weblate (Hungarian)

Currently translated at 97.3% (772 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/

* Translated using Weblate (Hungarian)

Currently translated at 94.1% (16 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hu/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Romanian)

Currently translated at 99.3% (788 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/

* Translated using Weblate (Romanian)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Nepali)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (793 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/

* Translated using Weblate (French)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/

* Translated using Weblate (Esperanto)

Currently translated at 62.0% (492 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/

* Translated using Weblate (Esperanto)

Currently translated at 100.0% (17 of 17 strings)

Translation: Mihon/Mihon Plurals
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/eo/

* Translated using Weblate (Esperanto)

Currently translated at 63.6% (505 of 793 strings)

Translation: Mihon/Mihon
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/

---------

Co-authored-by: bapeey <90949336+bapeey@users.noreply.github.com>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Radoŝ Porka <animatorzPolski@gmail.com>
Co-authored-by: Deniz <denizgezgin365@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: B4LiN7 <B4LiN7@users.noreply.hosted.weblate.org>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Naga <yz2000.pro@gmail.com>
(cherry picked from commit 7edecae57f77ece7a5a3b457620c61e225fdc906)
2024-03-02 13:48:00 -05:00
Maddie Witman ed34807a58 Fix some issues from 7ff95e2 (#415)
* Fixed extra header introduced in 7ff95e2

* Removed parentheses to make detekt happy

* Updated relative date display for dates in the future

* Small cleanup for header creation logic

* replaced "and" with "&&" for better formatting

(cherry picked from commit 07f963d5ae16c3c8d7be025a7e9439ad2110ac71)
2024-03-02 13:47:43 -05:00
beerpsi 31acbbdcdc [ExtensionLoader] Prioritize extension classpath over app classpath (#433)
(cherry picked from commit ab02568ac6e9dabc7a41036bb3d8c77138125544)
2024-03-02 13:47:21 -05:00
MajorTanya ef7708e324 Fix DelayedTrackingUpdateJob spam on update errors (#411)
* Fix DelayedTrackingUpdateJob spam on update errors

DelayedTrackingUpdateJob would start spamming when it encountered an
error (e.g. a tracker has an issue) and never stop.
This seems to stem from a circular dependency between the Job's
`doWork` and TrackChapter's `await`.

TrackChapter sets up a completely new instance of the
DelayedTrackingUpdateJob if any Exception was thrown during the track
update.

This causes the Job to get replaced (as per the WorkManager's set
ExistingWorkPolicy).

Because of this, the guard clause at the start of doWork would never
trigger, as all instances of the Job would report being the 0th try
(because they were completely new instances).

This simple fix introduces a boolean `isRetry` parameter to
TrackChapter's await method, which is set to `false` by default.
DelayedTrackingUpdateJob however sets this parameter to `true`, which
means TrackChapter won't try to set up the Job again.

* Rename isRetry parameter to setupJobOnFailure

This also inverts the logic, so true & false were swapped.

(cherry picked from commit 617bf491eee1d1010dc23c3f6df5d148700feb44)
2024-03-02 13:47:09 -05:00
MajorTanya de353c3334 Address overridePendingTransition deprecation (#410)
This function is deprecated starting with API 34 "UpsideDownCake" and
should be replaced with `overrideActivityTransition`.

(cherry picked from commit 840b647b4b4e738fac546b7437dd5449679232a1)
2024-03-02 13:46:42 -05:00
AntsyLich b47a317c48 Tweak detekt config
(cherry picked from commit 1b0bbb84401005801e61107a0c36443af691b8e6)
2024-03-02 13:44:22 -05:00
AntsyLich 2b163c91a9 Cleanup [BaseColorScheme.getColorScheme]
(cherry picked from commit 95d4df9ca80a88e87e633fc24c7fec677bc9d9b6)
2024-03-02 13:44:13 -05:00
AntsyLich d380a078a2 Update gradle.properties
(cherry picked from commit fb86c470f6cfcb0b6c1bb7b2790366e0d0c0662e)
2024-03-02 13:43:09 -05:00
AntsyLich 556afacd13 Small cleanup in WorkerInfoScreen
(cherry picked from commit 5aec8f8018236a38106483da08f9cbc28261ac9b)
2024-03-02 13:42:56 -05:00
AntsyLich 598d622d0b Revert a mishap in 7ff95e21babda98dd1b479912278d6029cd15f0d
(cherry picked from commit e183cbb231c6d48a17c573dbd3f2c8ce04ff7031)
2024-03-02 13:42:37 -05:00
AntsyLich 3a1d0d65bf Ignore detekt [LongParameterList] for composables
(cherry picked from commit 6bdb37be65757ca903c5c2a249ca03331b04d673)
2024-03-02 13:41:52 -05:00
Jobobby04 cc7b8a9b69 Improve duplicate chapter set as read 2024-03-02 13:41:35 -05:00
Maddie Witman 6c6f09ac5a Refactor use of Java.util.date to Java.time.*, to fix localized date issues. (#402)
* Add support for localdate based relative times

* Update History Screen to use new localdate based relative times

* Update Updates Screen to use new localdate based relative times

* Cleaned up date util classes

* Updated build time display

* Code cleanup

* Fixed crash in settings

* Updated Preferences item

* Worker Info works

* Fixed Tracker date display

* Code changes to pass detekt

(cherry picked from commit 7ff95e21babda98dd1b479912278d6029cd15f0d)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt
2024-03-02 13:41:18 -05:00
MajorTanya 1fe309f363 Minor refactor of theming when expressions (#396)
* Minor refactor of theming when expressions

Avoids triggering detekt's CyclomaticComplexMethod warning because of
too many when branches, which would happen with one more theme being
added in these two locations.

In TachiyomiTheme, the Monet theme is separated because it requires
the current Compose context to function. The other themes do not and
are delegated to a Map.

* Implement requested changes

- moved themeResources out of the ThemingDelegate interface
- replaced single condition when with if expression

(cherry picked from commit 96c236e5c38248c875f2ac7596cd51845aa651ea)
2024-03-02 12:10:21 -05:00
renovate[bot] 3417fdb1a4 Update dependency androidx.test.ext:junit-ktx to v1.2.0-alpha03 (#340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 72f3756a3b89de292d75d41fe6b5a59172039c45)
2024-03-02 12:10:13 -05:00
renovate[bot] a6394672e7 Update dependency androidx.test.espresso:espresso-core to v3.6.0-alpha03 (#339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 0780385d2ebc9caca0bda0151d828ea6e036c768)
2024-03-02 12:10:07 -05:00
renovate[bot] 1fc97e4b7a Update lifecycle.version to v2.7.0 (#268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 31e9273b1ff4ecfd1992beaa8ad10fa27f726cc2)
2024-03-02 12:09:58 -05:00
renovate[bot] fe853aa1c5 Update dependency com.github.requery:sqlite-android to v3.45.0 (#260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 088e37b2d8d4d790e926eda34119377c2f94ccf6)

# Conflicts:
#	gradle/libs.versions.toml
2024-03-02 12:09:47 -05:00
renovate[bot] 9c3f805eab Update dependency io.github.fornewid:material-motion-compose-core to v1.2.0 (#257)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 5b88f1bd94ae3c696681fe34a22453575e747ff6)
2024-03-02 12:08:30 -05:00
renovate[bot] 410eda6d6c Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.3 (#255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 18beb20aac774a83d6cb13f6dc276c3683aaf9a2)
2024-03-02 12:08:21 -05:00
renovate[bot] 719c24fb38 Update dependency gradle to v8.6 (#341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
(cherry picked from commit 9bff20cb1a0918d7789b281952624fed890fbab7)
2024-03-02 12:08:04 -05:00
Jobobby04 a7cb182bbe Cleanup 2024-03-02 12:01:49 -05:00
Fermín Cirella dbb970d7b5 Make manga page preview row count configurable (#1087)
* Make manga page preview row count configurable

* Replace string with plural
2024-03-02 11:58:16 -05:00
Dexroneum 887a27cf3e [RU] Translations (#1085) 2024-03-02 11:57:24 -05:00
Shamicen eed8ffb9d4 fix password protect downloads and copying ComicInfo files in LocalSource (#1084)
* fix password protect downloads

* fixed copying of ComicInfo file in LocalSource.kt

* Return correct archive file

* Applied upstream fix

* Use tempFileManager instead of file path

* Use streams instead of files
2024-03-02 11:56:57 -05:00
Cuong M. Tran dd412e33ad Rename MangaDex's FollowStatus's property to better reflect its type (#1082) 2024-03-02 11:56:11 -05:00
ɴᴇᴋᴏ 94e5c33785 Update zh-rTW strings.xml (#1080)
* Update strings.xml

* Update new zh-rTW strings.xml

* Update strings.xml

https://github.com/jobobby04/TachiyomiSY/pull/1079#issue-2140674661
2024-03-02 11:55:47 -05:00
Luqman 6f3f109723 add mark read dupes on reading (#1079)
* Feature: mark read dupes

* dupe chapter reading add summary

* Update ReaderViewModel.kt

* Update i18n/src/commonMain/resources/MR/base/strings.xml

* Update app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2024-03-02 11:55:09 -05:00
Jobobby04 d8082de1db Ignore chapters with 0 or under chapter numbers 2024-02-18 11:47:19 -05:00
570 changed files with 15289 additions and 5953 deletions
+2 -2
View File
@@ -53,7 +53,7 @@ body:
label: TachiyomiSY version label: TachiyomiSY version
description: You can find your TachiyomiSY version in **More → About**. description: You can find your TachiyomiSY version in **More → About**.
placeholder: | placeholder: |
Example: "1.10.4" Example: "1.10.5"
validations: validations:
required: true required: true
@@ -96,7 +96,7 @@ body:
required: true required: true
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https:/mihon.app/docs/guides/troubleshooting/). - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https:/mihon.app/docs/guides/troubleshooting/).
required: true required: true
- label: I have updated the app to version **[1.10.4](https://github.com/jobobby04/tachiyomisy/releases/latest)**. - label: I have updated the app to version **[1.10.5](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
required: true required: true
- label: I have updated all installed extensions. - label: I have updated all installed extensions.
required: true required: true
+1 -1
View File
@@ -31,7 +31,7 @@ body:
required: true required: true
- label: I have written a short but informative title. - label: I have written a short but informative title.
required: true required: true
- label: I have updated the app to version **[1.10.4](https://github.com/jobobby04/tachiyomisy/releases/latest)**. - label: I have updated the app to version **[1.10.5](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
required: true required: true
- label: I will fill out all of the requested information in this form. - label: I will fill out all of the requested information in this form.
required: true required: true
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"labels": ["Dependencies"],
"includePaths": [".github/workflows/*", "gradle/sy.versions.toml"],
}
+7 -6
View File
@@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/actions/wrapper-validation@v4
build: build:
name: Build app name: Build app
@@ -27,18 +27,19 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
java-version: 17 java-version: 17
distribution: adopt distribution: adopt
- name: Set up gradle
uses: gradle/actions/setup-gradle@v4
- name: Build app - name: Build app
uses: gradle/gradle-command-action@v2 run: ./gradlew detekt assembleDevDebug
with:
arguments: assembleDevDebug
- name: Upload APK - name: Upload APK
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: TachiyomiSY-${{ github.sha }}.apk name: TachiyomiSY-${{ github.sha }}.apk
path: app/build/outputs/apk/dev/debug/app-dev-debug.apk path: app/build/outputs/apk/dev/debug/app-dev-debug.apk
+12 -5
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v2 uses: gradle/actions/wrapper-validation@v4
- name: Setup Android SDK - name: Setup Android SDK
run: | run: |
@@ -31,19 +31,26 @@ jobs:
distribution: adopt distribution: adopt
- name: Set up gradle - name: Set up gradle
uses: gradle/actions/setup-gradle@v3 uses: gradle/actions/setup-gradle@v4
# SY <-- # SY <--
- name: Write google-services.json - name: Write google-services.json
uses: DamianReeves/write-file-action@v1.2 uses: DamianReeves/write-file-action@v1.3
with: with:
path: app/google-services.json path: app/google-services.json
contents: ${{ secrets.GOOGLE_SERVICES_TEXT }} contents: ${{ secrets.GOOGLE_SERVICES_TEXT }}
write-mode: overwrite write-mode: overwrite
- name: Write client_secrets.json
uses: DamianReeves/write-file-action@v1.3
with:
path: app/src/main/assets/client_secrets.json
contents: ${{ secrets.CLIENT_SECRETS_TEXT }}
write-mode: overwrite
# SY --> # SY -->
- name: Build app and run unit tests - name: Build app and run unit tests
run: ./gradlew assembleStandardRelease testStandardReleaseUnitTest --stacktrace run: ./gradlew detekt assembleStandardRelease testStandardReleaseUnitTest --stacktrace
- name: Sign APK - name: Sign APK
uses: r0adkll/sign-android-release@v1 uses: r0adkll/sign-android-release@v1
@@ -79,7 +86,7 @@ jobs:
echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV
- name: Create release - name: Create release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
with: with:
tag_name: ${{ github.run_number }} tag_name: ${{ github.run_number }}
name: TachiyomiSY name: TachiyomiSY
+6 -10
View File
@@ -3,12 +3,11 @@ name: Remote Dispatch Action Initiator
on: on:
push: push:
branches: branches:
- 'master' - 'preview'
jobs: jobs:
trigger_preview_build: trigger_preview_build:
name: Trigger preview build name: Trigger preview build
if: ${{ github.ref == 'refs/heads/master' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -16,15 +15,12 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1 uses: gradle/actions/wrapper-validation@v4
- name: TAG - Bump version and push tag - name: Create Tag
uses: anothrNick/github-tag-action@1.67.0 run: |
env: git tag "preview-${{ github.run_number }}"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} git push origin "preview-${{ github.run_number }}"
WITH_V: true
RELEASE_BRANCHES: master
DEFAULT_BUMP: patch
- name: PING - Dispatch initiating repository event - name: PING - Dispatch initiating repository event
run: | run: |
+6
View File
@@ -40,6 +40,12 @@ jobs:
"ignoreCase": true, "ignoreCase": true,
"labels": ["Cloudflare protected"], "labels": ["Cloudflare protected"],
"message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection." "message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
},
{
"type": "both",
"regex": "^.*(myanimelist|mal).*$",
"ignoreCase": true,
"message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Mihon to version 0.16.4 or newer\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart Mihon\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps."
} }
] ]
auto-close-ignore-label: do-not-autoclose auto-close-ignore-label: do-not-autoclose
+2
View File
@@ -1,4 +1,5 @@
.gradle .gradle
.kotlin
/local.properties /local.properties
/.idea/workspace.xml /.idea/workspace.xml
.DS_Store .DS_Store
@@ -22,3 +23,4 @@ TODO.md
CHANGELOG.md CHANGELOG.md
/captures /captures
build.sh build.sh
/app/src/main/assets/client_secrets.json
+18 -1
View File
@@ -26,7 +26,7 @@ Before you start, please note that the ability to use following technologies is
## Linting ## Linting
To auto-fix some linting errors, run the `ktlintFormat` Gradle task. Run the `detekt` gradle task. If the build fails, a report of issues can be found in `app/build/reports/detekt/`. The report is availble in several formats and details each issue that needs attention.
## Getting help ## Getting help
@@ -52,3 +52,20 @@ When creating a fork, remember to:
- To avoid having your data polluting the main app's analytics and crash report services: - To avoid having your data polluting the main app's analytics and crash report services:
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own - If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own
- If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own - If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own
### Supporting Cloud Sync - Google Drive Implementation
1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project
3. Go to API & Services -> Library -> Google Drive API and click enable
4. Go to API & Services -> Oauth consent screen
5. Create it, fill in the app name, user support email, and developer contact information
6. In the next screen, click add or remove scopes, and add the `.../auth/drive.appdata` and `.../auth/drive.file` scopes
7. Don't add any test users and go back to the dashboard
8. Click publish
9. Go to API & Services -> Credentials
10. Click Create credentials -> Oauth client ID
11. Select Android, give it a name, and set `eu.kanade.google.oauth` as the package name
12. To get the SHA-1 key, run `keytool -printcert -jarfile app-standard-universal-release.apk` on your apk, and copy the listed SHA-1
13. Expand advanced settings, and enable Custom URL scheme
14. After that just download the json, name it to `client_secrets.json` and put it in `app/src/main/assets/`
+14 -8
View File
@@ -1,20 +1,20 @@
| Preview Builds | Release Builds | Tachiyomi Support Server | | Preview Builds | Release Builds | Mihon Support Server |
|-------|----------|----------| |-------|----------|----------|
| [![Preview](https://github.com/jobobby04/TachiyomiSYPreview/workflows/Remote%20Dispatch%20Build%20App/badge.svg)](https://github.com/jobobby04/TachiyomiSYPreview/releases) | [![stable release](https://img.shields.io/github/release/jobobby04/tachiyomisy.svg?maxAge=3600&label=download)](https://github.com/jobobby04/tachiyomisy/releases/latest) | [![Discord](https://img.shields.io/discord/1195734228319617024.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/mihon) | | [![Preview](https://github.com/jobobby04/TachiyomiSYPreview/workflows/Remote%20Dispatch%20Build%20App/badge.svg)](https://github.com/jobobby04/TachiyomiSYPreview/releases) | [![stable release](https://img.shields.io/github/release/jobobby04/tachiyomisy.svg?maxAge=3600&label=download)](https://github.com/jobobby04/tachiyomisy/releases/latest) | [![Discord](https://img.shields.io/discord/1195734228319617024.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/mihon) |
# ![app icon](./.github/readme-images/app-icon.png)TachiyomiSY # ![app icon](./.github/readme-images/app-icon.png)TachiyomiSY
Tachiyomi is a free and open source manga reader for Android 6.0 and above. This version of Tachiyomi, TachiyomiSY was based off TachiyomiAZ. This version is meant to push forward in the ways of usability and features. TachiyomiSY tries to push forward where it can, but staying in a place where it can easily grab updates and features from the main app, it tries to make new features, or take features from other forks like J2K and Neko. Mihon is a free and open source manga reader for Android 6.0 and above. This version of Mihon, TachiyomiSY was based off TachiyomiAZ. This version is meant to push forward in the ways of usability and features. TachiyomiSY tries to push forward where it can, but staying in a place where it can easily grab updates and features from the main app, it tries to make new features, or take features from other forks like J2K and Neko.
![screenshots of app](./.github/readme-images/screens.png) ![screenshots of app](./.github/readme-images/screens.png)
## Features ## Features
Features of Tachiyomi(original) include: Features of Mihon(original) include:
* Online reading from a variety of sources * Online reading from a variety of sources
* Local reading of downloaded content * Local reading of downloaded content
* A configurable reader with multiple viewers, reading directions and other settings. * A configurable reader with multiple viewers, reading directions and other settings.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support * Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.app/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support
* Categories to organize your library * Categories to organize your library
* Light and dark themes * Light and dark themes
* Schedule updating your library for new chapters * Schedule updating your library for new chapters
@@ -42,7 +42,6 @@ Features of TachiyomiSY include:
* Page preload customization * Page preload customization
* Customize image cache size * Customize image cache size
* Batch import of custom sources and featured extensions * Batch import of custom sources and featured extensions
* Automatic CAPTCHA solving
* Advanced source settings page, searching, enable/disable all * Advanced source settings page, searching, enable/disable all
* Click tag for local search, long click tag for global search * Click tag for local search, long click tag for global search
* Merge multiple of the same manga from different sources * Merge multiple of the same manga from different sources
@@ -68,6 +67,15 @@ Get the app from our [releases page](https://github.com/jobobby04/tachiyomisy/re
If you want to try new features before they get to the stable release, you can download the preview version [here](https://github.com/jobobby04/tachiyomisypreview/releases). If you want to try new features before they get to the stable release, you can download the preview version [here](https://github.com/jobobby04/tachiyomisypreview/releases).
## Translation
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/mihon/tachiyomisy/)
<details><summary>Translation Progress</summary>
<a href="https://hosted.weblate.org/engage/mihon/">
<img src="https://hosted.weblate.org/widgets/mihon/-/tachiyomisy/multi-auto.svg" alt="Translation status" />
</a>
</details>
## Issues, Feature Requests and Contributing ## Issues, Feature Requests and Contributing
Please make sure to read the full guidelines. Your issue may be closed without warning if you do not. Please make sure to read the full guidelines. Your issue may be closed without warning if you do not.
@@ -89,9 +97,7 @@ Please make sure to read the full guidelines. Your issue may be closed without w
* If it could be device-dependent, try reproducing on another device (if possible) * If it could be device-dependent, try reproducing on another device (if possible)
* Don't group unrelated requests into one issue * Don't group unrelated requests into one issue
DO: https://github.com/tachiyomiorg/tachiyomi/issues/24 https://github.com/tachiyomiorg/tachiyomi/issues/71 Use the [issue forms](https://github.com/jobobby04/TachiyomiSY/issues/new/choose) to submit a bug.
DON'T: https://github.com/tachiyomiorg/tachiyomi/issues/75
</details> </details>
+22 -31
View File
@@ -1,9 +1,12 @@
import mihon.buildlogic.getBuildTime
import mihon.buildlogic.getCommitCount
import mihon.buildlogic.getGitSha
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
id("com.android.application") id("mihon.android.application")
id("mihon.android.application.compose")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
kotlin("android")
kotlin("plugin.parcelize") kotlin("plugin.parcelize")
kotlin("plugin.serialization") kotlin("plugin.serialization")
// id("com.github.zellius.shortcut-helper") // id("com.github.zellius.shortcut-helper")
@@ -26,8 +29,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "eu.kanade.tachiyomi.sy" applicationId = "eu.kanade.tachiyomi.sy"
versionCode = 64 versionCode = 69
versionName = "1.10.4" versionName = "1.10.5"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@@ -120,7 +123,6 @@ android {
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
compose = true
buildConfig = true buildConfig = true
// Disable some unused things // Disable some unused things
@@ -133,10 +135,6 @@ android {
abortOnError = false abortOnError = false
checkReleaseBuilds = false checkReleaseBuilds = false
} }
composeOptions {
kotlinCompilerExtensionVersion = compose.versions.compiler.get()
}
} }
dependencies { dependencies {
@@ -154,26 +152,25 @@ dependencies {
implementation(projects.presentationWidget) implementation(projects.presentationWidget)
// Compose // Compose
implementation(platform(compose.bom))
implementation(compose.activity) implementation(compose.activity)
implementation(compose.foundation) implementation(compose.foundation)
implementation(compose.material3.core) implementation(compose.material3.core)
implementation(compose.material.core)
implementation(compose.material.icons) implementation(compose.material.icons)
implementation(compose.animation) implementation(compose.animation)
implementation(compose.animation.graphics) implementation(compose.animation.graphics)
debugImplementation(compose.ui.tooling) debugImplementation(compose.ui.tooling)
implementation(compose.ui.tooling.preview) implementation(compose.ui.tooling.preview)
implementation(compose.ui.util) implementation(compose.ui.util)
implementation(compose.accompanist.webview)
implementation(compose.accompanist.systemuicontroller) implementation(compose.accompanist.systemuicontroller)
implementation(androidx.interpolator)
implementation(androidx.paging.runtime) implementation(androidx.paging.runtime)
implementation(androidx.paging.compose) implementation(androidx.paging.compose)
implementation(libs.bundles.sqlite) implementation(libs.bundles.sqlite)
// SY --> // SY -->
implementation(libs.sqlcipher) implementation(sylibs.sqlcipher)
// SY <-- // SY <--
implementation(kotlinx.reflect) implementation(kotlinx.reflect)
@@ -215,7 +212,7 @@ dependencies {
// Disk // Disk
implementation(libs.disklrucache) implementation(libs.disklrucache)
implementation(libs.unifile) implementation(libs.unifile)
implementation(libs.junrar) implementation(libs.bundles.archive)
// SY --> // SY -->
implementation(libs.zip4j) implementation(libs.zip4j)
// SY <-- // SY <--
@@ -247,12 +244,14 @@ dependencies {
implementation(libs.bundles.voyager) implementation(libs.bundles.voyager)
implementation(libs.compose.materialmotion) implementation(libs.compose.materialmotion)
implementation(libs.swipe) implementation(libs.swipe)
implementation(libs.compose.webview)
implementation(libs.compose.grid)
// Logging // Logging
implementation(libs.logcat) implementation(libs.logcat)
// Crash reports/analytics // Crash reports/analytics
// implementation(libs.bundles.acra)
// "standardImplementation"(libs.firebase.analytics) // "standardImplementation"(libs.firebase.analytics)
// Shizuku // Shizuku
@@ -265,6 +264,8 @@ dependencies {
// debugImplementation(libs.leakcanary.android) // debugImplementation(libs.leakcanary.android)
implementation(libs.leakcanary.plumber) implementation(libs.leakcanary.plumber)
testImplementation(kotlinx.coroutines.test)
// SY --> // SY -->
// Text distance (EH) // Text distance (EH)
implementation(sylibs.simularity) implementation(sylibs.simularity)
@@ -279,6 +280,10 @@ dependencies {
// RatingBar (SY) // RatingBar (SY)
implementation(sylibs.ratingbar) implementation(sylibs.ratingbar)
implementation(sylibs.composeRatingbar) implementation(sylibs.composeRatingbar)
// Google drive
implementation(sylibs.google.api.services.drive)
implementation(sylibs.google.api.client.oauth)
} }
androidComponents { androidComponents {
@@ -300,7 +305,7 @@ androidComponents {
tasks { tasks {
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers) // See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> { withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs += listOf( compilerOptions.freeCompilerArgs.addAll(
"-Xcontext-receivers", "-Xcontext-receivers",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material.ExperimentalMaterialApi", "-opt-in=androidx.compose.material.ExperimentalMaterialApi",
@@ -310,26 +315,12 @@ tasks {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=coil.annotation.ExperimentalCoilApi", "-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi", "-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
) )
if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") {
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
)
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
)
}
} }
} }
+29
View File
@@ -2,6 +2,7 @@
-keep,allowoptimization class eu.kanade.** -keep,allowoptimization class eu.kanade.**
-keep,allowoptimization class tachiyomi.** -keep,allowoptimization class tachiyomi.**
-keep,allowoptimization class mihon.**
# Keep common dependencies used in extensions # Keep common dependencies used in extensions
-keep,allowoptimization class androidx.preference.** { public protected *; } -keep,allowoptimization class androidx.preference.** { public protected *; }
@@ -46,6 +47,10 @@
-dontnote rx.internal.util.PlatformDependent -dontnote rx.internal.util.PlatformDependent
##---------------End: proguard configuration for RxJava 1.x ---------- ##---------------End: proguard configuration for RxJava 1.x ----------
##---------------Begin: proguard configuration for okhttp ----------
-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; }
##---------------End: proguard configuration for okhttp ----------
##---------------Begin: proguard configuration for kotlinx.serialization ---------- ##---------------Begin: proguard configuration for kotlinx.serialization ----------
-keepattributes *Annotation*, InnerClasses -keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.** # core serialization annotations -dontnote kotlinx.serialization.** # core serialization annotations
@@ -122,10 +127,19 @@
# XmlUtil # XmlUtil
-keep public enum nl.adaptivity.xmlutil.EventType { *; } -keep public enum nl.adaptivity.xmlutil.EventType { *; }
# Apache Commons Compress
-keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { <init>(); }
# Firebase # Firebase
-keep class com.google.firebase.installations.** { *; } -keep class com.google.firebase.installations.** { *; }
-keep interface com.google.firebase.installations.** { *; } -keep interface com.google.firebase.installations.** { *; }
# Google Drive
-keep class com.google.api.services.** { *; }
# Google OAuth
-keep class com.google.api.client.** { *; }
# SY --> # SY -->
# SqlCipher # SqlCipher
-keepclassmembers class net.zetetic.database.sqlcipher.SQLiteCustomFunction { *; } -keepclassmembers class net.zetetic.database.sqlcipher.SQLiteCustomFunction { *; }
@@ -260,6 +274,9 @@
-keep,allowoptimization class * extends uy.kohesive.injekt.api.TypeReference -keep,allowoptimization class * extends uy.kohesive.injekt.api.TypeReference
-keep,allowoptimization public class io.requery.android.database.sqlite.SQLiteConnection { *; } -keep,allowoptimization public class io.requery.android.database.sqlite.SQLiteConnection { *; }
# Keep apache http client
-keep class org.apache.http.** { *; }
# Suggested rules # Suggested rules
-dontwarn com.oracle.svm.core.annotate.AutomaticFeature -dontwarn com.oracle.svm.core.annotate.AutomaticFeature
-dontwarn com.oracle.svm.core.annotate.Delete -dontwarn com.oracle.svm.core.annotate.Delete
@@ -273,3 +290,15 @@
-dontwarn java.lang.Module -dontwarn java.lang.Module
-dontwarn org.graalvm.nativeimage.hosted.RuntimeResourceAccess -dontwarn org.graalvm.nativeimage.hosted.RuntimeResourceAccess
-dontwarn org.jspecify.annotations.NullMarked -dontwarn org.jspecify.annotations.NullMarked
-dontwarn javax.naming.InvalidNameException
-dontwarn javax.naming.NamingException
-dontwarn javax.naming.directory.Attribute
-dontwarn javax.naming.directory.Attributes
-dontwarn javax.naming.ldap.LdapName
-dontwarn javax.naming.ldap.Rdn
-dontwarn org.ietf.jgss.GSSContext
-dontwarn org.ietf.jgss.GSSCredential
-dontwarn org.ietf.jgss.GSSException
-dontwarn org.ietf.jgss.GSSManager
-dontwarn org.ietf.jgss.GSSName
-dontwarn org.ietf.jgss.Oid
+14
View File
@@ -188,6 +188,20 @@
<data android:host="shikimori-auth" /> <data android:host="shikimori-auth" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ui.setting.track.GoogleDriveLoginActivity"
android:label="GoogleDrive"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="eu.kanade.google.oauth" />
</intent-filter>
</activity>
<activity <activity
android:name="exh.ui.login.EhLoginActivity" android:name="exh.ui.login.EhLoginActivity"
@@ -4,10 +4,7 @@ import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.download.interactor.DeleteDownload import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.extension.interactor.CreateExtensionRepo
import eu.kanade.domain.extension.interactor.DeleteExtensionRepo
import eu.kanade.domain.extension.interactor.GetExtensionLanguages import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionRepos
import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.extension.interactor.GetExtensionsByType import eu.kanade.domain.extension.interactor.GetExtensionsByType
import eu.kanade.domain.extension.interactor.TrustExtension import eu.kanade.domain.extension.interactor.TrustExtension
@@ -26,6 +23,16 @@ import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.interactor.RefreshTracks import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.interactor.TrackChapter
import mihon.data.repository.ExtensionRepoRepositoryImpl
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import mihon.domain.extensionrepo.service.ExtensionRepoService
import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl
import tachiyomi.data.history.HistoryRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl
@@ -111,6 +118,7 @@ class DomainModule : InjektModule {
addFactory { GetMangaByUrlAndSourceId(get()) } addFactory { GetMangaByUrlAndSourceId(get()) }
addFactory { GetManga(get()) } addFactory { GetManga(get()) }
addFactory { GetNextChapters(get(), get(), get(), get()) } addFactory { GetNextChapters(get(), get(), get(), get()) }
addFactory { GetUpcomingManga(get()) }
addFactory { ResetViewerFlags(get()) } addFactory { ResetViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) } addFactory { SetMangaChapterFlags(get()) }
addFactory { FetchInterval(get()) } addFactory { FetchInterval(get()) }
@@ -171,10 +179,15 @@ class DomainModule : InjektModule {
addFactory { ToggleLanguage(get()) } addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) } addFactory { ToggleSource(get()) }
addFactory { ToggleSourcePin(get()) } addFactory { ToggleSourcePin(get()) }
addFactory { TrustExtension(get()) } addFactory { TrustExtension(get(), get()) }
addFactory { CreateExtensionRepo(get()) } addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
addFactory { ExtensionRepoService(get(), get()) }
addFactory { GetExtensionRepo(get()) }
addFactory { GetExtensionRepoCount(get()) }
addFactory { CreateExtensionRepo(get(), get()) }
addFactory { DeleteExtensionRepo(get()) } addFactory { DeleteExtensionRepo(get()) }
addFactory { GetExtensionRepos(get()) } addFactory { ReplaceExtensionRepo(get()) }
addFactory { UpdateExtensionRepo(get(), get()) }
} }
} }
@@ -2,8 +2,6 @@ package eu.kanade.domain.base
import android.content.Context import android.content.Context
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -22,8 +20,6 @@ class BasePreferences(
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore) fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType)
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false) fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false)
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) { enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
@@ -32,4 +28,6 @@ class BasePreferences(
SHIZUKU(MR.strings.ext_installer_shizuku, false), SHIZUKU(MR.strings.ext_installer_shizuku, false),
PRIVATE(MR.strings.ext_installer_private, false), PRIVATE(MR.strings.ext_installer_private, false),
} }
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
} }
@@ -39,4 +39,5 @@ fun Chapter.toDbChapter(): DbChapter = ChapterImpl().also {
it.date_upload = dateUpload it.date_upload = dateUpload
it.chapter_number = chapterNumber.toFloat() it.chapter_number = chapterNumber.toFloat()
it.source_order = sourceOrder.toInt() it.source_order = sourceOrder.toInt()
it.last_modified = lastModifiedAt
} }
@@ -1,25 +0,0 @@
package eu.kanade.domain.extension.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.plusAssign
class CreateExtensionRepo(private val preferences: SourcePreferences) {
fun await(name: String): Result {
// Do not allow invalid formats
if (!name.matches(repoRegex)) {
return Result.InvalidUrl
}
preferences.extensionRepos() += name.removeSuffix("/index.min.json")
return Result.Success
}
sealed interface Result {
data object InvalidUrl : Result
data object Success : Result
}
}
private val repoRegex = """^https://.*/index\.min\.json$""".toRegex()
@@ -1,11 +0,0 @@
package eu.kanade.domain.extension.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.common.preference.minusAssign
class DeleteExtensionRepo(private val preferences: SourcePreferences) {
fun await(repo: String) {
preferences.extensionRepos() -= repo
}
}
@@ -1,11 +0,0 @@
package eu.kanade.domain.extension.interactor
import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow
class GetExtensionRepos(private val preferences: SourcePreferences) {
fun subscribe(): Flow<Set<String>> {
return preferences.extensionRepos().changes()
}
}
@@ -20,7 +20,7 @@ class GetExtensionsByType(
extensionManager.installedExtensionsFlow, extensionManager.installedExtensionsFlow,
extensionManager.untrustedExtensionsFlow, extensionManager.untrustedExtensionsFlow,
extensionManager.availableExtensionsFlow, extensionManager.availableExtensionsFlow,
) { _activeLanguages, _installed, _untrusted, _available -> ) { enabledLanguages, _installed, _untrusted, _available ->
val (updates, installed) = _installed val (updates, installed) = _installed
.filter { (showNsfwSources || !it.isNsfw) } .filter { (showNsfwSources || !it.isNsfw) }
.sortedWith( .sortedWith(
@@ -41,9 +41,9 @@ class GetExtensionsByType(
} }
.flatMap { ext -> .flatMap { ext ->
if (ext.sources.isEmpty()) { if (ext.sources.isEmpty()) {
return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList() return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList()
} }
ext.sources.filter { it.lang in _activeLanguages } ext.sources.filter { it.lang in enabledLanguages }
.map { .map {
ext.copy( ext.copy(
name = it.name, name = it.name,
@@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import tachiyomi.core.common.preference.getAndSet import tachiyomi.core.common.preference.getAndSet
class TrustExtension( class TrustExtension(
private val extensionRepoRepository: ExtensionRepoRepository,
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean { suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash" val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
return key in preferences.trustedExtensions().get() val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get()
} }
fun trust(pkgName: String, versionCode: Long, signatureHash: String) { fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
@@ -19,9 +22,7 @@ class TrustExtension(
// Remove previously trusted versions // Remove previously trusted versions
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet() val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
removed.also { removed.also { it += "$pkgName:$versionCode:$signatureHash" }
it += "$pkgName:$versionCode:$signatureHash"
}
} }
} }
@@ -104,7 +104,13 @@ fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
/** /**
* Creates a ComicInfo instance based on the manga and chapter metadata. * Creates a ComicInfo instance based on the manga and chapter metadata.
*/ */
fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: List<String>?) = ComicInfo( fun getComicInfo(
manga: Manga,
chapter: Chapter,
urls: List<String>,
categories: List<String>?,
sourceName: String,
) = ComicInfo(
title = ComicInfo.Title(chapter.name), title = ComicInfo.Title(chapter.name),
series = ComicInfo.Series(manga.title), series = ComicInfo.Series(manga.title),
number = chapter.chapterNumber.takeIf { it >= 0 }?.let { number = chapter.chapterNumber.takeIf { it >= 0 }?.let {
@@ -114,7 +120,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories:
ComicInfo.Number(it.toString()) ComicInfo.Number(it.toString())
} }
}, },
web = ComicInfo.Web(chapterUrl), web = ComicInfo.Web(urls.joinToString(" ")),
summary = manga.description?.let { ComicInfo.Summary(it) }, summary = manga.description?.let { ComicInfo.Summary(it) },
writer = manga.author?.let { ComicInfo.Writer(it) }, writer = manga.author?.let { ComicInfo.Writer(it) },
penciller = manga.artist?.let { ComicInfo.Penciller(it) }, penciller = manga.artist?.let { ComicInfo.Penciller(it) },
@@ -124,6 +130,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories:
ComicInfoPublishingStatus.toComicInfoValue(manga.status), ComicInfoPublishingStatus.toComicInfoValue(manga.status),
), ),
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) }, categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
source = ComicInfo.SourceMihon(sourceName),
// SY --> // SY -->
padding = CbzCrypto.createComicInfoPadding()?.let { ComicInfo.PaddingTachiyomiSY(it) }, padding = CbzCrypto.createComicInfoPadding()?.let { ComicInfo.PaddingTachiyomiSY(it) },
// SY <-- // SY <--
@@ -49,6 +49,11 @@ class SourcePreferences(
emptySet(), emptySet(),
) )
fun globalSearchFilterState() = preferenceStore.getBoolean(
Preference.appStateKey("has_filters_toggle_state"),
false,
)
// SY --> // SY -->
fun enableSourceBlacklist() = preferenceStore.getBoolean("eh_enable_source_blacklist", true) fun enableSourceBlacklist() = preferenceStore.getBoolean("eh_enable_source_blacklist", true)
@@ -0,0 +1,105 @@
package eu.kanade.domain.sync
import eu.kanade.domain.sync.models.SyncSettings
import eu.kanade.tachiyomi.data.sync.models.SyncTriggerOptions
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import java.util.UUID
class SyncPreferences(
private val preferenceStore: PreferenceStore,
) {
fun clientHost() = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "")
fun lastSyncTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
fun lastSyncEtag() = preferenceStore.getString("sync_etag", "")
fun syncInterval() = preferenceStore.getInt("sync_interval", 0)
fun syncService() = preferenceStore.getInt("sync_service", 0)
fun googleDriveAccessToken() = preferenceStore.getString(
Preference.appStateKey("google_drive_access_token"),
"",
)
fun googleDriveRefreshToken() = preferenceStore.getString(
Preference.appStateKey("google_drive_refresh_token"),
"",
)
fun uniqueDeviceID(): String {
val uniqueIDPreference = preferenceStore.getString(Preference.appStateKey("unique_device_id"), "")
// Retrieve the current value of the preference
var uniqueID = uniqueIDPreference.get()
if (uniqueID.isBlank()) {
uniqueID = UUID.randomUUID().toString()
uniqueIDPreference.set(uniqueID)
}
return uniqueID
}
fun isSyncEnabled(): Boolean {
return syncService().get() != 0
}
fun getSyncSettings(): SyncSettings {
return SyncSettings(
libraryEntries = preferenceStore.getBoolean("library_entries", true).get(),
categories = preferenceStore.getBoolean("categories", true).get(),
chapters = preferenceStore.getBoolean("chapters", true).get(),
tracking = preferenceStore.getBoolean("tracking", true).get(),
history = preferenceStore.getBoolean("history", true).get(),
appSettings = preferenceStore.getBoolean("appSettings", true).get(),
extensionRepoSettings = preferenceStore.getBoolean("extensionRepoSettings", true).get(),
sourceSettings = preferenceStore.getBoolean("sourceSettings", true).get(),
privateSettings = preferenceStore.getBoolean("privateSettings", true).get(),
// SY -->
customInfo = preferenceStore.getBoolean("customInfo", true).get(),
readEntries = preferenceStore.getBoolean("readEntries", true).get(),
savedSearches = preferenceStore.getBoolean("savedSearches", true).get(),
// SY <--
)
}
fun setSyncSettings(syncSettings: SyncSettings) {
preferenceStore.getBoolean("library_entries", true).set(syncSettings.libraryEntries)
preferenceStore.getBoolean("categories", true).set(syncSettings.categories)
preferenceStore.getBoolean("chapters", true).set(syncSettings.chapters)
preferenceStore.getBoolean("tracking", true).set(syncSettings.tracking)
preferenceStore.getBoolean("history", true).set(syncSettings.history)
preferenceStore.getBoolean("appSettings", true).set(syncSettings.appSettings)
preferenceStore.getBoolean("extensionRepoSettings", true).set(syncSettings.extensionRepoSettings)
preferenceStore.getBoolean("sourceSettings", true).set(syncSettings.sourceSettings)
preferenceStore.getBoolean("privateSettings", true).set(syncSettings.privateSettings)
// SY -->
preferenceStore.getBoolean("customInfo", true).set(syncSettings.customInfo)
preferenceStore.getBoolean("readEntries", true).set(syncSettings.readEntries)
preferenceStore.getBoolean("savedSearches", true).set(syncSettings.savedSearches)
// SY <--
}
fun getSyncTriggerOptions(): SyncTriggerOptions {
return SyncTriggerOptions(
syncOnChapterRead = preferenceStore.getBoolean("sync_on_chapter_read", false).get(),
syncOnChapterOpen = preferenceStore.getBoolean("sync_on_chapter_open", false).get(),
syncOnAppStart = preferenceStore.getBoolean("sync_on_app_start", false).get(),
syncOnAppResume = preferenceStore.getBoolean("sync_on_app_resume", false).get(),
)
}
fun setSyncTriggerOptions(syncTriggerOptions: SyncTriggerOptions) {
preferenceStore.getBoolean("sync_on_chapter_read", false)
.set(syncTriggerOptions.syncOnChapterRead)
preferenceStore.getBoolean("sync_on_chapter_open", false)
.set(syncTriggerOptions.syncOnChapterOpen)
preferenceStore.getBoolean("sync_on_app_start", false)
.set(syncTriggerOptions.syncOnAppStart)
preferenceStore.getBoolean("sync_on_app_resume", false)
.set(syncTriggerOptions.syncOnAppResume)
}
}
@@ -0,0 +1,19 @@
package eu.kanade.domain.sync.models
data class SyncSettings(
val libraryEntries: Boolean = true,
val categories: Boolean = true,
val chapters: Boolean = true,
val tracking: Boolean = true,
val history: Boolean = true,
val appSettings: Boolean = true,
val extensionRepoSettings: Boolean = true,
val sourceSettings: Boolean = true,
val privateSettings: Boolean = false,
// SY -->
val customInfo: Boolean = true,
val readEntries: Boolean = true,
val savedSearches: Boolean = true,
// SY <--
)
@@ -23,7 +23,7 @@ class TrackChapter(
private val delayedTrackingStore: DelayedTrackingStore, private val delayedTrackingStore: DelayedTrackingStore,
) { ) {
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) { suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) {
withNonCancellableContext { withNonCancellableContext {
val tracks = getTracks.await(mangaId) val tracks = getTracks.await(mangaId)
if (tracks.isEmpty()) return@withNonCancellableContext if (tracks.isEmpty()) return@withNonCancellableContext
@@ -34,7 +34,7 @@ class TrackChapter(
service == null || service == null ||
!service.isLoggedIn || !service.isLoggedIn ||
chapterNumber <= track.lastChapterRead /* SY --> */ || chapterNumber <= track.lastChapterRead /* SY --> */ ||
(service is MdList && track.status == FollowStatus.UNFOLLOWED.int.toLong())/* SY <-- */ (service is MdList && track.status == FollowStatus.UNFOLLOWED.long)/* SY <-- */
) { ) {
return@mapNotNull null return@mapNotNull null
} }
@@ -50,7 +50,9 @@ class TrackChapter(
delayedTrackingStore.remove(track.id) delayedTrackingStore.remove(track.id)
} catch (e: Exception) { } catch (e: Exception) {
delayedTrackingStore.add(track.id, chapterNumber) delayedTrackingStore.add(track.id, chapterNumber)
DelayedTrackingUpdateJob.setupTask(context) if (setupJobOnFailure) {
DelayedTrackingUpdateJob.setupTask(context)
}
throw e throw e
} }
} }
@@ -45,7 +45,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke
logcat(LogPriority.DEBUG) { logcat(LogPriority.DEBUG) {
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
} }
trackChapter.await(context, track.mangaId, track.lastChapterRead) trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false)
} }
} }
@@ -8,8 +8,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getEnum
import java.text.DateFormat import java.time.format.DateTimeFormatter
import java.text.SimpleDateFormat import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale
class UiPreferences( class UiPreferences(
@@ -46,6 +46,8 @@ class UiPreferences(
fun mergeInOverflow() = preferenceStore.getBoolean("merge_in_overflow", true) fun mergeInOverflow() = preferenceStore.getBoolean("merge_in_overflow", true)
fun previewsRowCount() = preferenceStore.getInt("pref_previews_row_count", 4)
fun useNewSourceNavigation() = preferenceStore.getBoolean("use_new_source_navigation", true) fun useNewSourceNavigation() = preferenceStore.getBoolean("use_new_source_navigation", true)
fun bottomBarLabels() = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true) fun bottomBarLabels() = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true)
@@ -57,9 +59,9 @@ class UiPreferences(
// SY <-- // SY <--
companion object { companion object {
fun dateFormat(format: String): DateFormat = when (format) { fun dateFormat(format: String): DateTimeFormatter = when (format) {
"" -> DateFormat.getDateInstance(DateFormat.SHORT) "" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
else -> SimpleDateFormat(format, Locale.getDefault()) else -> DateTimeFormatter.ofPattern(format, Locale.getDefault())
} }
} }
} }
@@ -5,7 +5,6 @@ import android.net.Uri
import android.provider.Settings import android.provider.Settings
import android.util.DisplayMetrics import android.util.DisplayMetrics
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -362,10 +361,8 @@ private fun InfoText(
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
) { ) {
val interactionSource = remember { MutableInteractionSource() }
val clickableModifier = if (onClick != null) { val clickableModifier = if (onClick != null) {
Modifier.clickable(interactionSource, indication = null) { onClick() } Modifier.clickable(interactionSource = null, indication = null, onClick = onClick)
} else { } else {
Modifier Modifier
} }
@@ -70,7 +70,7 @@ fun FeedScreen(
onClickDelete: (FeedSavedSearch) -> Unit, onClickDelete: (FeedSavedSearch) -> Unit,
onClickManga: (Manga) -> Unit, onClickManga: (Manga) -> Unit,
onRefresh: () -> Unit, onRefresh: () -> Unit,
getMangaState: @Composable (Manga, CatalogueSource?) -> State<Manga>, getMangaState: @Composable (Manga) -> State<Manga>,
) { ) {
when { when {
state.isLoading -> LoadingScreen() state.isLoading -> LoadingScreen()
@@ -119,7 +119,7 @@ fun FeedScreen(
) { ) {
FeedItem( FeedItem(
item = item, item = item,
getMangaState = { getMangaState(it, item.source) }, getMangaState = { getMangaState(it) },
onClickManga = onClickManga, onClickManga = onClickManga,
) )
} }
@@ -25,7 +25,7 @@ import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.domain.source.model.icon import eu.kanade.domain.source.model.icon
import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@@ -50,7 +50,8 @@ import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import java.util.Date import java.time.Instant
import java.time.ZoneId
@Composable @Composable
fun BrowseSourceEHentaiList( fun BrowseSourceEHentaiList(
@@ -128,9 +129,11 @@ fun BrowseSourceEHentaiListItem(
} }
val datePosted by produceState("", metadata) { val datePosted by produceState("", metadata) {
value = withIOContext { value = withIOContext {
runCatching { metadata.datePosted?.let { MetadataUtil.EX_DATE_FORMAT.format(Date(it)) } } runCatching {
.getOrNull() metadata.datePosted?.let {
.orEmpty() MetadataUtil.EX_DATE_FORMAT.format(Instant.ofEpochMilli(it).atZone(ZoneId.systemDefault()))
}
}.getOrNull().orEmpty()
} }
} }
val genre by produceState<Pair<GenreColor, StringResource>?>(null, metadata) { val genre by produceState<Pair<GenreColor, StringResource>?>(null, metadata) {
@@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
@@ -23,7 +21,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
@Composable @Composable
fun NavigatorAdaptiveSheet( fun NavigatorAdaptiveSheet(
screen: Screen, screen: Screen,
tonalElevation: Dp = 1.dp,
enableSwipeDismiss: (Navigator) -> Boolean = { true }, enableSwipeDismiss: (Navigator) -> Boolean = { true },
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
) { ) {
@@ -31,7 +28,6 @@ fun NavigatorAdaptiveSheet(
screen = screen, screen = screen,
content = { sheetNavigator -> content = { sheetNavigator ->
AdaptiveSheet( AdaptiveSheet(
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator), enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
) { ) {
@@ -73,7 +69,6 @@ fun NavigatorAdaptiveSheet(
fun AdaptiveSheet( fun AdaptiveSheet(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
tonalElevation: Dp = 1.dp,
enableSwipeDismiss: Boolean = true, enableSwipeDismiss: Boolean = true,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
@@ -86,7 +81,6 @@ fun AdaptiveSheet(
AdaptiveSheetImpl( AdaptiveSheetImpl(
modifier = modifier, modifier = modifier,
isTabletUi = isTabletUi, isTabletUi = isTabletUi,
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss, enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
) { ) {
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Close
@@ -21,6 +20,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
@@ -312,7 +312,7 @@ fun SearchToolbar(
visualTransformation = visualTransformation, visualTransformation = visualTransformation,
interactionSource = interactionSource, interactionSource = interactionSource,
decorationBox = { innerTextField -> decorationBox = { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox( TextFieldDefaults.DecorationBox(
value = searchQuery, value = searchQuery,
innerTextField = innerTextField, innerTextField = innerTextField,
enabled = true, enabled = true,
@@ -331,6 +331,7 @@ fun SearchToolbar(
), ),
) )
}, },
container = {},
) )
}, },
) )
@@ -9,20 +9,26 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
@Composable @Composable
fun relativeDateText( fun relativeDateText(
dateEpochMillis: Long, dateEpochMillis: Long,
): String { ): String {
return relativeDateText( return relativeDateText(
date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L }, localDate = LocalDate.ofInstant(
Instant.ofEpochMilli(dateEpochMillis),
ZoneId.systemDefault(),
)
.takeIf { dateEpochMillis > 0L },
) )
} }
@Composable @Composable
fun relativeDateText( fun relativeDateText(
date: Date?, localDate: LocalDate?,
): String { ): String {
val context = LocalContext.current val context = LocalContext.current
@@ -30,11 +36,10 @@ fun relativeDateText(
val relativeTime = remember { preferences.relativeTime().get() } val relativeTime = remember { preferences.relativeTime().get() }
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
return date return localDate?.toRelativeString(
?.toRelativeString( context = context,
context = context, relative = relativeTime,
relative = relativeTime, dateFormat = dateFormat,
dateFormat = dateFormat, )
)
?: stringResource(MR.strings.not_applicable) ?: stringResource(MR.strings.not_applicable)
} }
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -29,7 +30,6 @@ import androidx.compose.ui.util.fastForEachIndexed
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.components.material.TabText
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -58,6 +58,7 @@ fun TabbedDialog(
PrimaryTabRow( PrimaryTabRow(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage, selectedTabIndex = pagerState.currentPage,
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
divider = {}, divider = {},
) { ) {
tabTitles.fastForEachIndexed { index, tab -> tabTitles.fastForEachIndexed { index, tab ->
@@ -78,9 +79,8 @@ fun TabbedDialog(
modifier = Modifier.animateContentSize(), modifier = Modifier.animateContentSize(),
state = pagerState, state = pagerState,
verticalAlignment = Alignment.Top, verticalAlignment = Alignment.Top,
) { page -> pageContent = { page -> content(page) }
content(page) )
}
} }
} }
} }
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow import androidx.compose.material3.PrimaryTabRow
@@ -24,7 +25,6 @@ import dev.icerock.moko.resources.StringResource
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.components.material.TabText
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -37,7 +37,7 @@ fun CrashScreen(
acceptText = stringResource(MR.strings.pref_dump_crash_logs), acceptText = stringResource(MR.strings.pref_dump_crash_logs),
onAcceptClick = { onAcceptClick = {
scope.launch { scope.launch {
CrashLogUtil(context).dumpLogs() CrashLogUtil(context).dumpLogs(exception)
} }
}, },
rejectText = stringResource(MR.strings.crash_screen_restart_application), rejectText = stringResource(MR.strings.crash_screen_restart_application),
@@ -29,7 +29,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
import java.util.Date import java.time.LocalDate
@Composable @Composable
fun HistoryScreen( fun HistoryScreen(
@@ -134,7 +134,7 @@ private fun HistoryScreenContent(
} }
sealed interface HistoryUiModel { sealed interface HistoryUiModel {
data class Header(val date: Date) : HistoryUiModel data class Header(val date: LocalDate) : HistoryUiModel
data class Item(val item: HistoryWithRelations) : HistoryUiModel data class Item(val item: HistoryWithRelations) : HistoryUiModel
} }
@@ -7,6 +7,7 @@ import kotlinx.collections.immutable.toImmutableList
import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.history.model.HistoryWithRelations
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
import java.time.Instant import java.time.Instant
import java.time.LocalDate
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.Date import java.util.Date
import kotlin.random.Random import kotlin.random.Random
@@ -73,10 +74,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo
private object HistoryUiModelExamples { private object HistoryUiModelExamples {
val headerToday = header() val headerToday = header()
val headerTomorrow = val headerTomorrow =
HistoryUiModel.Header(Date.from(Instant.now().plus(1, ChronoUnit.DAYS))) HistoryUiModel.Header(LocalDate.now().plusDays(1))
fun header(instantBuilder: (Instant) -> Instant = { it }) = fun header(instantBuilder: (Instant) -> Instant = { it }) =
HistoryUiModel.Header(Date.from(instantBuilder(Instant.now()))) HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now())))
fun items() = sequence { fun items() = sequence {
var count = 1 var count = 1
@@ -155,7 +155,7 @@ private fun ColumnScope.FilterPage(
) )
// SY <-- // SY <--
val trackers = remember { screenModel.trackers } val trackers by screenModel.trackersFlow.collectAsState()
when (trackers.size) { when (trackers.size) {
0 -> { 0 -> {
// No trackers // No trackers
@@ -188,6 +188,7 @@ private fun ColumnScope.SortPage(
category: Category?, category: Category?,
screenModel: LibrarySettingsScreenModel, screenModel: LibrarySettingsScreenModel,
) { ) {
val trackers by screenModel.trackersFlow.collectAsState()
// SY --> // SY -->
val globalSortMode by screenModel.libraryPreferences.sortingMode().collectAsState() val globalSortMode by screenModel.libraryPreferences.sortingMode().collectAsState()
val sortingMode = if (screenModel.grouping == LibraryGroup.BY_DEFAULT) { val sortingMode = if (screenModel.grouping == LibraryGroup.BY_DEFAULT) {
@@ -206,12 +207,12 @@ private fun ColumnScope.SortPage(
}.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary().get().isNotEmpty()) }.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary().get().isNotEmpty())
// SY <-- // SY <--
val trackerSortOption =
if (screenModel.trackers.isEmpty()) { val trackerSortOption = if (trackers.isEmpty()) {
emptyList() emptyList()
} else { } else {
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean) listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
} }
listOfNotNull( listOfNotNull(
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical, MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
@@ -346,12 +347,13 @@ private fun ColumnScope.GroupPage(
screenModel: LibrarySettingsScreenModel, screenModel: LibrarySettingsScreenModel,
hasCategories: Boolean, hasCategories: Boolean,
) { ) {
val groups = remember(hasCategories, screenModel.trackers) { val trackers by screenModel.trackersFlow.collectAsState()
val groups = remember(hasCategories, trackers) {
buildList { buildList {
add(LibraryGroup.BY_DEFAULT) add(LibraryGroup.BY_DEFAULT)
add(LibraryGroup.BY_SOURCE) add(LibraryGroup.BY_SOURCE)
add(LibraryGroup.BY_STATUS) add(LibraryGroup.BY_STATUS)
if (screenModel.trackers.isNotEmpty()) { if (trackers.isNotEmpty()) {
add(LibraryGroup.BY_TRACK_STATUS) add(LibraryGroup.BY_TRACK_STATUS)
} }
if (hasCategories) { if (hasCategories) {
@@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.manga.components.MangaCover
@@ -42,15 +43,22 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground import tachiyomi.presentation.core.util.selectedBackground
import tachiyomi.domain.manga.model.MangaCover as MangaCoverModel
object CommonMangaItemDefaults { object CommonMangaItemDefaults {
val GridHorizontalSpacer = 4.dp val GridHorizontalSpacer = 4.dp
val GridVerticalSpacer = 4.dp val GridVerticalSpacer = 4.dp
@Suppress("ConstPropertyName")
const val BrowseFavoriteCoverAlpha = 0.34f const val BrowseFavoriteCoverAlpha = 0.34f
} }
private val ContinueReadingButtonSize = 28.dp private val ContinueReadingButtonSizeSmall = 28.dp
private val ContinueReadingButtonSizeLarge = 32.dp
private val ContinueReadingButtonIconSizeSmall = 16.dp
private val ContinueReadingButtonIconSizeLarge = 20.dp
private val ContinueReadingButtonGridPadding = 6.dp private val ContinueReadingButtonGridPadding = 6.dp
private val ContinueReadingButtonListSpacing = 8.dp private val ContinueReadingButtonListSpacing = 8.dp
@@ -62,7 +70,7 @@ private const val GridSelectedCoverAlpha = 0.76f
*/ */
@Composable @Composable
fun MangaCompactGridItem( fun MangaCompactGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover, coverData: MangaCoverModel,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
isSelected: Boolean = false, isSelected: Boolean = false,
@@ -96,10 +104,12 @@ fun MangaCompactGridItem(
) )
} else if (onClickContinueReading != null) { } else if (onClickContinueReading != null) {
ContinueReadingButton( ContinueReadingButton(
size = ContinueReadingButtonSizeLarge,
iconSize = ContinueReadingButtonIconSizeLarge,
onClick = onClickContinueReading,
modifier = Modifier modifier = Modifier
.padding(ContinueReadingButtonGridPadding) .padding(ContinueReadingButtonGridPadding)
.align(Alignment.BottomEnd), .align(Alignment.BottomEnd),
onClickContinueReading = onClickContinueReading,
) )
} }
}, },
@@ -148,11 +158,13 @@ private fun BoxScope.CoverTextOverlay(
) )
if (onClickContinueReading != null) { if (onClickContinueReading != null) {
ContinueReadingButton( ContinueReadingButton(
size = ContinueReadingButtonSizeSmall,
iconSize = ContinueReadingButtonIconSizeSmall,
onClick = onClickContinueReading,
modifier = Modifier.padding( modifier = Modifier.padding(
end = ContinueReadingButtonGridPadding, end = ContinueReadingButtonGridPadding,
bottom = ContinueReadingButtonGridPadding, bottom = ContinueReadingButtonGridPadding,
), ),
onClickContinueReading = onClickContinueReading,
) )
} }
} }
@@ -163,7 +175,7 @@ private fun BoxScope.CoverTextOverlay(
*/ */
@Composable @Composable
fun MangaComfortableGridItem( fun MangaComfortableGridItem(
coverData: tachiyomi.domain.manga.model.MangaCover, coverData: MangaCoverModel,
title: String, title: String,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
@@ -194,10 +206,12 @@ fun MangaComfortableGridItem(
content = { content = {
if (onClickContinueReading != null) { if (onClickContinueReading != null) {
ContinueReadingButton( ContinueReadingButton(
size = ContinueReadingButtonSizeLarge,
iconSize = ContinueReadingButtonIconSizeLarge,
onClick = onClickContinueReading,
modifier = Modifier modifier = Modifier
.padding(ContinueReadingButtonGridPadding) .padding(ContinueReadingButtonGridPadding)
.align(Alignment.BottomEnd), .align(Alignment.BottomEnd),
onClickContinueReading = onClickContinueReading,
) )
} }
}, },
@@ -309,14 +323,14 @@ private fun GridItemSelectable(
private fun Modifier.selectedOutline( private fun Modifier.selectedOutline(
isSelected: Boolean, isSelected: Boolean,
color: Color, color: Color,
) = this then drawBehind { if (isSelected) drawRect(color = color) } ) = drawBehind { if (isSelected) drawRect(color = color) }
/** /**
* Layout of list item. * Layout of list item.
*/ */
@Composable @Composable
fun MangaListItem( fun MangaListItem(
coverData: tachiyomi.domain.manga.model.MangaCover, coverData: MangaCoverModel,
title: String, title: String,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
@@ -354,8 +368,10 @@ fun MangaListItem(
BadgeGroup(content = badge) BadgeGroup(content = badge)
if (onClickContinueReading != null) { if (onClickContinueReading != null) {
ContinueReadingButton( ContinueReadingButton(
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing), size = ContinueReadingButtonSizeSmall,
onClickContinueReading = onClickContinueReading, iconSize = ContinueReadingButtonIconSizeSmall,
onClick = onClickContinueReading,
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing)
) )
} }
} }
@@ -363,23 +379,25 @@ fun MangaListItem(
@Composable @Composable
private fun ContinueReadingButton( private fun ContinueReadingButton(
size: Dp,
iconSize: Dp,
onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClickContinueReading: () -> Unit,
) { ) {
Box(modifier = modifier) { Box(modifier = modifier) {
FilledIconButton( FilledIconButton(
onClick = onClickContinueReading, onClick = onClick,
modifier = Modifier.size(ContinueReadingButtonSize),
shape = MaterialTheme.shapes.small, shape = MaterialTheme.shapes.small,
colors = IconButtonDefaults.filledIconButtonColors( colors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f), containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer), contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
), ),
modifier = Modifier.size(size)
) { ) {
Icon( Icon(
imageVector = Icons.Filled.PlayArrow, imageVector = Icons.Filled.PlayArrow,
contentDescription = stringResource(MR.strings.action_resume), contentDescription = stringResource(MR.strings.action_resume),
modifier = Modifier.size(16.dp), modifier = Modifier.size(iconSize),
) )
} }
} }
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -22,7 +23,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryItem
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus
@@ -38,6 +38,7 @@ fun LibraryToolbar(
onClickRefresh: () -> Unit, onClickRefresh: () -> Unit,
onClickGlobalUpdate: () -> Unit, onClickGlobalUpdate: () -> Unit,
onClickOpenRandomManga: () -> Unit, onClickOpenRandomManga: () -> Unit,
onClickSyncNow: () -> Unit,
// SY --> // SY -->
onClickSyncExh: (() -> Unit)?, onClickSyncExh: (() -> Unit)?,
// SY <-- // SY <--
@@ -60,6 +61,7 @@ fun LibraryToolbar(
onClickRefresh = onClickRefresh, onClickRefresh = onClickRefresh,
onClickGlobalUpdate = onClickGlobalUpdate, onClickGlobalUpdate = onClickGlobalUpdate,
onClickOpenRandomManga = onClickOpenRandomManga, onClickOpenRandomManga = onClickOpenRandomManga,
onClickSyncNow = onClickSyncNow,
// SY --> // SY -->
onClickSyncExh = onClickSyncExh, onClickSyncExh = onClickSyncExh,
// SY <-- // SY <--
@@ -77,6 +79,7 @@ private fun LibraryRegularToolbar(
onClickRefresh: () -> Unit, onClickRefresh: () -> Unit,
onClickGlobalUpdate: () -> Unit, onClickGlobalUpdate: () -> Unit,
onClickOpenRandomManga: () -> Unit, onClickOpenRandomManga: () -> Unit,
onClickSyncNow: () -> Unit,
// SY --> // SY -->
onClickSyncExh: (() -> Unit)?, onClickSyncExh: (() -> Unit)?,
// SY <-- // SY <--
@@ -125,7 +128,10 @@ private fun LibraryRegularToolbar(
title = stringResource(MR.strings.action_open_random_manga), title = stringResource(MR.strings.action_open_random_manga),
onClick = onClickOpenRandomManga, onClick = onClickOpenRandomManga,
), ),
AppBar.OverflowAction(
title = stringResource(SYMR.strings.sync_library),
onClick = onClickSyncNow,
),
).builder().apply { ).builder().apply {
// SY --> // SY -->
if (onClickSyncExh != null) { if (onClickSyncExh != null) {
@@ -1,16 +1,33 @@
package eu.kanade.presentation.manga package eu.kanade.presentation.manga
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.AlertDialog import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Book
import androidx.compose.material.icons.outlined.SwapVert
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@Composable @Composable
@@ -18,42 +35,92 @@ fun DuplicateMangaDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onOpenManga: () -> Unit, onOpenManga: () -> Unit,
onMigrate: () -> Unit,
modifier: Modifier = Modifier,
) { ) {
AlertDialog( val minHeight = LocalPreferenceMinHeight.current
AdaptiveSheet(
modifier = modifier,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
title = { ) {
Text(text = stringResource(MR.strings.are_you_sure)) Column(
}, modifier = Modifier
text = { .padding(
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga)) vertical = TabbedDialogPaddings.Vertical,
}, horizontal = TabbedDialogPaddings.Horizontal,
confirmButton = { )
FlowRow( .fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), ) {
Text(
modifier = Modifier.padding(TitlePadding),
text = stringResource(MR.strings.are_you_sure),
style = MaterialTheme.typography.headlineMedium,
)
Text(
text = stringResource(MR.strings.confirm_add_duplicate_manga),
style = MaterialTheme.typography.bodyMedium,
)
Spacer(Modifier.height(PaddingSize))
TextPreferenceWidget(
title = stringResource(MR.strings.action_show_manga),
icon = Icons.Outlined.Book,
onPreferenceClick = {
onDismissRequest()
onOpenManga()
},
)
HorizontalDivider()
TextPreferenceWidget(
title = stringResource(MR.strings.action_migrate_duplicate),
icon = Icons.Outlined.SwapVert,
onPreferenceClick = {
onDismissRequest()
onMigrate()
},
)
HorizontalDivider()
TextPreferenceWidget(
title = stringResource(MR.strings.action_add_anyway),
icon = Icons.Outlined.Add,
onPreferenceClick = {
onDismissRequest()
onConfirm()
},
)
Row(
modifier = Modifier
.sizeIn(minHeight = minHeight)
.clickable { onDismissRequest.invoke() }
.padding(ButtonPadding)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) { ) {
TextButton( OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) {
onClick = { Text(
onDismissRequest() modifier = Modifier
onOpenManga() .padding(vertical = 8.dp),
}, text = stringResource(MR.strings.action_cancel),
) { color = MaterialTheme.colorScheme.primary,
Text(text = stringResource(MR.strings.action_show_manga)) style = MaterialTheme.typography.titleLarge,
} fontSize = 16.sp,
)
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
TextButton(
onClick = {
onDismissRequest()
onConfirm()
},
) {
Text(text = stringResource(MR.strings.action_add))
} }
} }
}, }
) }
} }
private val PaddingSize = 16.dp
private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp)
private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp)
@@ -106,7 +106,8 @@ import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.source.local.isLocal import tachiyomi.source.local.isLocal
import java.time.Instant import java.time.Instant
import java.util.Date import java.time.ZoneId
import java.time.ZonedDateTime
@Composable @Composable
fun MangaScreen( fun MangaScreen(
@@ -150,6 +151,7 @@ fun MangaScreen(
onMergeWithAnotherClicked: () -> Unit, onMergeWithAnotherClicked: () -> Unit,
onOpenPagePreview: (Int) -> Unit, onOpenPagePreview: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit, onMorePreviewsClicked: () -> Unit,
previewsRowCount: Int,
// SY <-- // SY <--
// For bottom action menu // For bottom action menu
@@ -208,6 +210,7 @@ fun MangaScreen(
onMergeWithAnotherClicked = onMergeWithAnotherClicked, onMergeWithAnotherClicked = onMergeWithAnotherClicked,
onOpenPagePreview = onOpenPagePreview, onOpenPagePreview = onOpenPagePreview,
onMorePreviewsClicked = onMorePreviewsClicked, onMorePreviewsClicked = onMorePreviewsClicked,
previewsRowCount = previewsRowCount,
// SY <-- // SY <--
onMultiBookmarkClicked = onMultiBookmarkClicked, onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked, onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
@@ -253,6 +256,7 @@ fun MangaScreen(
onMergeWithAnotherClicked = onMergeWithAnotherClicked, onMergeWithAnotherClicked = onMergeWithAnotherClicked,
onOpenPagePreview = onOpenPagePreview, onOpenPagePreview = onOpenPagePreview,
onMorePreviewsClicked = onMorePreviewsClicked, onMorePreviewsClicked = onMorePreviewsClicked,
previewsRowCount = previewsRowCount,
// SY <-- // SY <--
onMultiBookmarkClicked = onMultiBookmarkClicked, onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked, onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
@@ -308,6 +312,7 @@ private fun MangaScreenSmallImpl(
onMergeWithAnotherClicked: () -> Unit, onMergeWithAnotherClicked: () -> Unit,
onOpenPagePreview: (Int) -> Unit, onOpenPagePreview: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit, onMorePreviewsClicked: () -> Unit,
previewsRowCount: Int,
// SY <-- // SY <--
// For bottom action menu // For bottom action menu
@@ -544,13 +549,14 @@ private fun MangaScreenSmallImpl(
} }
} }
if (state.pagePreviewsState !is PagePreviewState.Unused) { if (state.pagePreviewsState !is PagePreviewState.Unused && previewsRowCount > 0) {
PagePreviewItems( PagePreviewItems(
pagePreviewState = state.pagePreviewsState, pagePreviewState = state.pagePreviewsState,
onOpenPage = onOpenPagePreview, onOpenPage = onOpenPagePreview,
onMorePreviewsClicked = onMorePreviewsClicked, onMorePreviewsClicked = onMorePreviewsClicked,
maxWidth = maxWidth, maxWidth = maxWidth,
setMaxWidth = { maxWidth = it } setMaxWidth = { maxWidth = it },
rowCount = previewsRowCount,
) )
} }
// SY <-- // SY <--
@@ -632,6 +638,7 @@ fun MangaScreenLargeImpl(
onMergeWithAnotherClicked: () -> Unit, onMergeWithAnotherClicked: () -> Unit,
onOpenPagePreview: (Int) -> Unit, onOpenPagePreview: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit, onMorePreviewsClicked: () -> Unit,
previewsRowCount: Int,
// SY <-- // SY <--
// For bottom action menu // For bottom action menu
@@ -832,11 +839,12 @@ fun MangaScreenLargeImpl(
onMergeWithAnotherClicked = onMergeWithAnotherClicked, onMergeWithAnotherClicked = onMergeWithAnotherClicked,
) )
} }
if (state.pagePreviewsState !is PagePreviewState.Unused) { if (state.pagePreviewsState !is PagePreviewState.Unused && previewsRowCount > 0) {
PagePreviews( PagePreviews(
pagePreviewState = state.pagePreviewsState, pagePreviewState = state.pagePreviewsState,
onOpenPage = onOpenPagePreview, onOpenPage = onOpenPagePreview,
onMorePreviewsClicked = onMorePreviewsClicked, onMorePreviewsClicked = onMorePreviewsClicked,
rowCount = previewsRowCount,
) )
} }
// SY <-- // SY <--
@@ -979,9 +987,10 @@ private fun LazyListScope.sharedChapterItems(
?.let { ?.let {
// SY --> // SY -->
if (manga.isEhBasedManga()) { if (manga.isEhBasedManga()) {
MetadataUtil.EX_DATE_FORMAT.format(Date(it)) MetadataUtil.EX_DATE_FORMAT
.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.systemDefault()))
} else { } else {
relativeDateText(Date(item.chapter.dateUpload)) relativeDateText(item.chapter.dateUpload)
} }
// SY <-- // SY <--
}, },
@@ -2,7 +2,6 @@ package eu.kanade.presentation.manga.components
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@@ -10,13 +9,13 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ArrowDownward
import androidx.compose.material.icons.outlined.ErrorOutline import androidx.compose.material.icons.outlined.ErrorOutline
import androidx.compose.material.ripple
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -24,8 +23,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -91,6 +91,7 @@ private fun NotDownloadedIndicator(
.size(IconButtonTokens.StateLayerSize) .size(IconButtonTokens.StateLayerSize)
.commonClickable( .commonClickable(
enabled = enabled, enabled = enabled,
hapticFeedback = LocalHapticFeedback.current,
onLongClick = { onClick(ChapterDownloadAction.START_NOW) }, onLongClick = { onClick(ChapterDownloadAction.START_NOW) },
onClick = { onClick(ChapterDownloadAction.START) }, onClick = { onClick(ChapterDownloadAction.START) },
) )
@@ -120,6 +121,7 @@ private fun DownloadingIndicator(
.size(IconButtonTokens.StateLayerSize) .size(IconButtonTokens.StateLayerSize)
.commonClickable( .commonClickable(
enabled = enabled, enabled = enabled,
hapticFeedback = LocalHapticFeedback.current,
onLongClick = { onClick(ChapterDownloadAction.CANCEL) }, onLongClick = { onClick(ChapterDownloadAction.CANCEL) },
onClick = { isMenuExpanded = true }, onClick = { isMenuExpanded = true },
), ),
@@ -136,6 +138,8 @@ private fun DownloadingIndicator(
modifier = IndicatorModifier, modifier = IndicatorModifier,
color = strokeColor, color = strokeColor,
strokeWidth = IndicatorStrokeWidth, strokeWidth = IndicatorStrokeWidth,
trackColor = Color.Transparent,
strokeCap = StrokeCap.Butt,
) )
} else { } else {
val animatedProgress by animateFloatAsState( val animatedProgress by animateFloatAsState(
@@ -152,6 +156,9 @@ private fun DownloadingIndicator(
modifier = IndicatorModifier, modifier = IndicatorModifier,
color = strokeColor, color = strokeColor,
strokeWidth = IndicatorSize / 2, strokeWidth = IndicatorSize / 2,
trackColor = Color.Transparent,
strokeCap = StrokeCap.Butt,
gapSize = 0.dp,
) )
} }
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) { DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
@@ -191,6 +198,7 @@ private fun DownloadedIndicator(
.size(IconButtonTokens.StateLayerSize) .size(IconButtonTokens.StateLayerSize)
.commonClickable( .commonClickable(
enabled = enabled, enabled = enabled,
hapticFeedback = LocalHapticFeedback.current,
onLongClick = { isMenuExpanded = true }, onLongClick = { isMenuExpanded = true },
onClick = { isMenuExpanded = true }, onClick = { isMenuExpanded = true },
), ),
@@ -225,6 +233,7 @@ private fun ErrorIndicator(
.size(IconButtonTokens.StateLayerSize) .size(IconButtonTokens.StateLayerSize)
.commonClickable( .commonClickable(
enabled = enabled, enabled = enabled,
hapticFeedback = LocalHapticFeedback.current,
onLongClick = { onClick(ChapterDownloadAction.START) }, onLongClick = { onClick(ChapterDownloadAction.START) },
onClick = { onClick(ChapterDownloadAction.START) }, onClick = { onClick(ChapterDownloadAction.START) },
), ),
@@ -241,26 +250,23 @@ private fun ErrorIndicator(
private fun Modifier.commonClickable( private fun Modifier.commonClickable(
enabled: Boolean, enabled: Boolean,
hapticFeedback: HapticFeedback,
onLongClick: () -> Unit, onLongClick: () -> Unit,
onClick: () -> Unit, onClick: () -> Unit,
) = composed { ) = this.combinedClickable(
val haptic = LocalHapticFeedback.current enabled = enabled,
onLongClick = {
Modifier.combinedClickable( onLongClick()
enabled = enabled, hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
onLongClick = { },
onLongClick() onClick = onClick,
haptic.performHapticFeedback(HapticFeedbackType.LongPress) role = Role.Button,
}, interactionSource = null,
onClick = onClick, indication = ripple(
role = Role.Button, bounded = false,
interactionSource = remember { MutableInteractionSource() }, radius = IconButtonTokens.StateLayerSize / 2,
indication = ripple( ),
bounded = false, )
radius = IconButtonTokens.StateLayerSize / 2,
),
)
}
private val IndicatorSize = 26.dp private val IndicatorSize = 26.dp
private val IndicatorPadding = 2.dp private val IndicatorPadding = 2.dp
@@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -33,12 +32,12 @@ import androidx.compose.material.icons.outlined.Label
import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.RemoveDone import androidx.compose.material.icons.outlined.RemoveDone
import androidx.compose.material.icons.outlined.SwapCalls import androidx.compose.material.icons.outlined.SwapCalls
import androidx.compose.material.ripple
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
@@ -90,7 +89,7 @@ fun MangaBottomActionMenu(
Surface( Surface(
modifier = modifier, modifier = modifier,
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize), shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
tonalElevation = 3.dp, color = MaterialTheme.colorScheme.surfaceContainerHigh,
) { ) {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) } val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
@@ -199,7 +198,7 @@ private fun RowScope.Button(
.size(48.dp) .size(48.dp)
.weight(animatedWeight) .weight(animatedWeight)
.combinedClickable( .combinedClickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = null,
indication = ripple(bounded = false), indication = ripple(bounded = false),
onLongClick = onLongClick, onLongClick = onLongClick,
onClick = onClick, onClick = onClick,
@@ -239,6 +238,7 @@ fun LibraryBottomActionMenu(
onClickCleanTitles: (() -> Unit)?, onClickCleanTitles: (() -> Unit)?,
onClickMigrate: (() -> Unit)?, onClickMigrate: (() -> Unit)?,
onClickAddToMangaDex: (() -> Unit)?, onClickAddToMangaDex: (() -> Unit)?,
onClickResetInfo: (() -> Unit)?,
// SY <-- // SY <--
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@@ -251,7 +251,7 @@ fun LibraryBottomActionMenu(
Surface( Surface(
modifier = modifier, modifier = modifier,
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize), shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
tonalElevation = 3.dp, color = MaterialTheme.colorScheme.surfaceContainerHigh,
) { ) {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val confirm = val confirm =
@@ -267,7 +267,7 @@ fun LibraryBottomActionMenu(
} }
} }
// SY --> // SY -->
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null || onClickResetInfo != null
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val moveMarkPrev = remember { !configuration.isTabletUi() } val moveMarkPrev = remember { !configuration.isTabletUi() }
var overFlowOpen by remember { mutableStateOf(false) } var overFlowOpen by remember { mutableStateOf(false) }
@@ -364,6 +364,12 @@ fun LibraryBottomActionMenu(
onClick = onClickAddToMangaDex, onClick = onClickAddToMangaDex,
) )
} }
if (onClickResetInfo != null) {
DropdownMenuItem(
text = { Text(text = stringResource(SYMR.strings.reset_info)) },
onClick = onClickResetInfo,
)
}
} }
} else { } else {
Button( Button(
@@ -24,36 +24,26 @@ import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import me.saket.swipe.SwipeableActionsBox import me.saket.swipe.SwipeableActionsBox
import me.saket.swipe.rememberSwipeableActionsState
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ReadItemAlpha import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground import tachiyomi.presentation.core.util.selectedBackground
import kotlin.math.absoluteValue
@Composable @Composable
fun MangaChapterListItem( fun MangaChapterListItem(
@@ -78,158 +68,129 @@ fun MangaChapterListItem(
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val haptic = LocalHapticFeedback.current val start = getSwipeAction(
val density = LocalDensity.current action = chapterSwipeStartAction,
read = read,
bookmark = bookmark,
downloadState = downloadStateProvider(),
background = MaterialTheme.colorScheme.primaryContainer,
onSwipe = { onChapterSwipe(chapterSwipeStartAction) },
)
val end = getSwipeAction(
action = chapterSwipeEndAction,
read = read,
bookmark = bookmark,
downloadState = downloadStateProvider(),
background = MaterialTheme.colorScheme.primaryContainer,
onSwipe = { onChapterSwipe(chapterSwipeEndAction) },
)
val textAlpha = if (read) ReadItemAlpha else 1f SwipeableActionsBox(
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha modifier = Modifier.clipToBounds(),
startActions = listOfNotNull(start),
// Increase touch slop of swipe action to reduce accidental trigger endActions = listOfNotNull(end),
val configuration = LocalViewConfiguration.current swipeThreshold = swipeActionThreshold,
CompositionLocalProvider( backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest,
LocalViewConfiguration provides object : ViewConfiguration by configuration {
override val touchSlop: Float = configuration.touchSlop * 3f
},
) { ) {
val start = getSwipeAction( Row(
action = chapterSwipeStartAction, modifier = modifier
read = read, .selectedBackground(selected)
bookmark = bookmark, .combinedClickable(
downloadState = downloadStateProvider(), onClick = onClick,
background = MaterialTheme.colorScheme.primaryContainer, onLongClick = onLongClick,
onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, )
) .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
val end = getSwipeAction(
action = chapterSwipeEndAction,
read = read,
bookmark = bookmark,
downloadState = downloadStateProvider(),
background = MaterialTheme.colorScheme.primaryContainer,
onSwipe = { onChapterSwipe(chapterSwipeEndAction) },
)
val swipeableActionsState = rememberSwipeableActionsState()
LaunchedEffect(Unit) {
// Haptic effect when swipe over threshold
val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() }
snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx }
.collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) }
}
SwipeableActionsBox(
modifier = Modifier.clipToBounds(),
state = swipeableActionsState,
startActions = listOfNotNull(start),
endActions = listOfNotNull(end),
swipeThreshold = swipeActionThreshold,
backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest,
) { ) {
Row( Column(
modifier = modifier modifier = Modifier.weight(1f),
.selectedBackground(selected) verticalArrangement = Arrangement.spacedBy(6.dp),
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
)
.padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
) { ) {
Column( Row(
modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(2.dp),
verticalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically,
) { ) {
Row( var textHeight by remember { mutableIntStateOf(0) }
horizontalArrangement = Arrangement.spacedBy(2.dp), if (!read) {
verticalAlignment = Alignment.CenterVertically, Icon(
) { imageVector = Icons.Filled.Circle,
var textHeight by remember { mutableIntStateOf(0) } contentDescription = stringResource(MR.strings.unread),
if (!read) { modifier = Modifier
Icon( .height(8.dp)
imageVector = Icons.Filled.Circle, .padding(end = 4.dp),
contentDescription = stringResource(MR.strings.unread), tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.height(8.dp)
.padding(end = 4.dp),
tint = MaterialTheme.colorScheme.primary,
)
}
if (bookmark) {
Icon(
imageVector = Icons.Filled.Bookmark,
contentDescription = stringResource(MR.strings.action_filter_bookmarked),
modifier = Modifier
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
tint = MaterialTheme.colorScheme.primary,
)
}
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
color = LocalContentColor.current.copy(alpha = textAlpha),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
) )
} }
if (bookmark) {
Icon(
imageVector = Icons.Filled.Bookmark,
contentDescription = stringResource(MR.strings.action_filter_bookmarked),
modifier = Modifier
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
tint = MaterialTheme.colorScheme.primary,
)
}
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
color = LocalContentColor.current.copy(alpha = if (read) ReadItemAlpha else 1f),
)
}
Row { Row {
ProvideTextStyle( val subtitleStyle = MaterialTheme.typography.bodySmall
value = MaterialTheme.typography.bodyMedium.copy( .merge(
fontSize = 12.sp, color = LocalContentColor.current
color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), .copy(alpha = if (read) ReadItemAlpha else SecondaryItemAlpha)
), )
) { ProvideTextStyle(value = subtitleStyle) {
if (date != null) { if (date != null) {
Text( Text(
text = date, text = date,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
if ( if (readProgress != null || scanlator != null/* SY --> */ || sourceName != null/* SY <-- */) DotSeparatorText()
readProgress != null || }
scanlator != null/* SY --> */ || if (readProgress != null) {
sourceName != null/* SY <-- */ Text(
) { text = readProgress,
DotSeparatorText() maxLines = 1,
} overflow = TextOverflow.Ellipsis,
} color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
if (readProgress != null) { )
Text( if (scanlator != null/* SY --> */ || sourceName != null/* SY <-- */) DotSeparatorText()
text = readProgress, }
maxLines = 1, // SY -->
overflow = TextOverflow.Ellipsis, if (sourceName != null) {
color = LocalContentColor.current.copy(alpha = ReadItemAlpha), Text(
) text = sourceName,
if (scanlator != null/* SY --> */ || sourceName != null/* SY <-- */) DotSeparatorText() maxLines = 1,
} overflow = TextOverflow.Ellipsis,
// SY --> )
if (sourceName != null) { if (scanlator != null) DotSeparatorText()
Text( }
text = sourceName, // SY <--
maxLines = 1, if (scanlator != null) {
overflow = TextOverflow.Ellipsis, Text(
) text = scanlator,
if (scanlator != null) DotSeparatorText() maxLines = 1,
} overflow = TextOverflow.Ellipsis,
// SY <-- )
if (scanlator != null) {
Text(
text = scanlator,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
} }
} }
} }
ChapterDownloadIndicator(
enabled = downloadIndicatorEnabled,
modifier = Modifier.padding(start = 4.dp),
downloadStateProvider = downloadStateProvider,
downloadProgressProvider = downloadProgressProvider,
onClick = { onDownloadClick?.invoke(it) },
)
} }
ChapterDownloadIndicator(
enabled = downloadIndicatorEnabled,
modifier = Modifier.padding(start = 4.dp),
downloadStateProvider = downloadStateProvider,
downloadProgressProvider = downloadProgressProvider,
onClick = { onDownloadClick?.invoke(it) },
)
} }
} }
} }
@@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@@ -38,10 +38,11 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import coil.imageLoader import coil3.asDrawable
import coil.request.CachePolicy import coil3.imageLoader
import coil.request.ImageRequest import coil3.request.CachePolicy
import coil.size.Size import coil3.request.ImageRequest
import coil3.size.Size
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
@@ -169,7 +170,9 @@ fun MangaCoverDialog(
.data(coverDataProvider()) .data(coverDataProvider())
.size(Size.ORIGINAL) .size(Size.ORIGINAL)
.memoryCachePolicy(CachePolicy.DISABLED) .memoryCachePolicy(CachePolicy.DISABLED)
.target { drawable -> .target { image ->
val drawable = image.asDrawable(view.context.resources)
// Copy bitmap in case it came from memory cache // Copy bitmap in case it came from memory cache
// Because SSIV needs to thoroughly read the image // Because SSIV needs to thoroughly read the image
val copy = (drawable as? BitmapDrawable)?.let { val copy = (drawable as? BitmapDrawable)?.let {
@@ -102,9 +102,12 @@ fun SetIntervalDialog(
), ),
), ),
) )
} else {
Spacer(Modifier.height(MaterialTheme.padding.small)) Text(
stringResource(MR.strings.manga_interval_expected_update_null),
)
} }
Spacer(Modifier.height(MaterialTheme.padding.small))
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) { if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
Text(stringResource(MR.strings.manga_interval_custom_amount)) Text(stringResource(MR.strings.manga_interval_custom_amount))
@@ -2,6 +2,7 @@ package eu.kanade.presentation.manga.components
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.graphics.res.animatedVectorResource import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.animation.graphics.vector.AnimatedImageVector
@@ -73,7 +74,7 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@@ -289,7 +290,8 @@ fun ExpandableMangaDescription(
modifier = Modifier modifier = Modifier
.padding(top = 8.dp) .padding(top = 8.dp)
.padding(vertical = 12.dp) .padding(vertical = 12.dp)
.animateContentSize(), .animateContentSize(animationSpec = spring())
.fillMaxWidth(),
) { ) {
var showMenu by remember { mutableStateOf(false) } var showMenu by remember { mutableStateOf(false) }
var tagSelected by remember { mutableStateOf("") } var tagSelected by remember { mutableStateOf("") }
@@ -25,14 +25,13 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.SubcomposeAsyncImage import coil3.compose.SubcomposeAsyncImage
import coil.compose.SubcomposeAsyncImageContent import coil3.compose.SubcomposeAsyncImageContent
import eu.kanade.domain.manga.model.PagePreview import eu.kanade.domain.manga.model.PagePreview
import eu.kanade.presentation.manga.MangaScreenItem import eu.kanade.presentation.manga.MangaScreenItem
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
@@ -102,6 +101,7 @@ fun PagePreviews(
pagePreviewState: PagePreviewState, pagePreviewState: PagePreviewState,
onOpenPage: (Int) -> Unit, onOpenPage: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit, onMorePreviewsClicked: () -> Unit,
rowCount: Int,
) { ) {
Column(Modifier.fillMaxWidth()) { Column(Modifier.fillMaxWidth()) {
var maxWidth by remember { var maxWidth by remember {
@@ -113,7 +113,7 @@ fun PagePreviews(
} }
pagePreviewState is PagePreviewState.Success -> { pagePreviewState is PagePreviewState.Success -> {
val itemPerRowCount = (maxWidth / 120.dp).floor() val itemPerRowCount = (maxWidth / 120.dp).floor()
pagePreviewState.pagePreviews.take(4 * itemPerRowCount).chunked(itemPerRowCount).forEach { pagePreviewState.pagePreviews.take(rowCount * itemPerRowCount).chunked(itemPerRowCount).forEach {
PagePreviewRow( PagePreviewRow(
onOpenPage = onOpenPage, onOpenPage = onOpenPage,
items = remember(it) { it.toImmutableList() } items = remember(it) { it.toImmutableList() }
@@ -132,7 +132,8 @@ fun LazyListScope.PagePreviewItems(
onOpenPage: (Int) -> Unit, onOpenPage: (Int) -> Unit,
onMorePreviewsClicked: () -> Unit, onMorePreviewsClicked: () -> Unit,
maxWidth: Dp, maxWidth: Dp,
setMaxWidth: (Dp) -> Unit setMaxWidth: (Dp) -> Unit,
rowCount: Int,
) { ) {
when { when {
pagePreviewState is PagePreviewState.Loading || maxWidth == Dp.Hairline -> { pagePreviewState is PagePreviewState.Loading || maxWidth == Dp.Hairline -> {
@@ -148,7 +149,7 @@ fun LazyListScope.PagePreviewItems(
items( items(
key = { "${MangaScreenItem.CHAPTER_PREVIEW_ROW}-$it" }, key = { "${MangaScreenItem.CHAPTER_PREVIEW_ROW}-$it" },
contentType = { MangaScreenItem.CHAPTER_PREVIEW_ROW }, contentType = { MangaScreenItem.CHAPTER_PREVIEW_ROW },
items = pagePreviewState.pagePreviews.take(4 * itemPerRowCount).chunked(itemPerRowCount), items = pagePreviewState.pagePreviews.take(rowCount * itemPerRowCount).chunked(itemPerRowCount),
) { ) {
PagePreviewRow( PagePreviewRow(
onOpenPage = onOpenPage, onOpenPage = onOpenPage,
@@ -111,8 +111,14 @@ fun ScanlatorFilterDialog(
} }
} else { } else {
FlowRow { FlowRow {
TextButton(onClick = mutableExcludedScanlators::clear) { if (mutableExcludedScanlators.isEmpty()) {
Text(text = stringResource(MR.strings.action_reset)) TextButton(onClick = { mutableExcludedScanlators.addAll(availableScanlators) }) {
Text(text = stringResource(MR.strings.action_select_all))
}
} else {
TextButton(onClick = mutableExcludedScanlators::clear) {
Text(text = stringResource(MR.strings.action_reset))
}
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) { TextButton(onClick = onDismissRequest) {
@@ -7,12 +7,12 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.InfoWidget import eu.kanade.presentation.more.settings.widget.InfoWidget
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
@@ -23,8 +23,6 @@ import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false } val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp } val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) { 56.dp }
@@ -156,16 +154,14 @@ internal fun PreferenceItem(
) )
} }
is Preference.PreferenceItem.TrackerPreference -> { is Preference.PreferenceItem.TrackerPreference -> {
val uName by Injekt.get<TrackPreferences>() val isLoggedIn by item.tracker.let { tracker ->
.trackUsername(item.tracker) tracker.isLoggedInFlow.collectAsState(tracker.isLoggedIn)
.collectAsState()
item.tracker.run {
TrackingPreferenceWidget(
tracker = this,
checked = uName.isNotEmpty(),
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
} }
TrackingPreferenceWidget(
tracker = item.tracker,
checked = isLoggedIn,
onClick = { if (isLoggedIn) item.logout() else item.login() },
)
} }
is Preference.PreferenceItem.InfoPreference -> { is Preference.PreferenceItem.InfoPreference -> {
InfoWidget(text = item.title) InfoWidget(text = item.title)
@@ -8,6 +8,8 @@ import android.provider.Settings
import android.webkit.WebStorage import android.webkit.WebStorage
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -60,7 +62,6 @@ import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import eu.kanade.tachiyomi.util.system.isShizukuInstalled import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import eu.kanade.tachiyomi.util.system.powerManager import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.setDefaultSettings
@@ -113,71 +114,54 @@ object SettingsAdvancedScreen : SearchableSettings {
val basePreferences = remember { Injekt.get<BasePreferences>() } val basePreferences = remember { Injekt.get<BasePreferences>() }
val networkPreferences = remember { Injekt.get<NetworkPreferences>() } val networkPreferences = remember { Injekt.get<NetworkPreferences>() }
return buildList { return listOf(
addAll( Preference.PreferenceItem.TextPreference(
listOf( title = stringResource(MR.strings.pref_dump_crash_logs),
/* SY --> Preference.PreferenceItem.SwitchPreference( subtitle = stringResource(MR.strings.pref_dump_crash_logs_summary),
pref = basePreferences.acraEnabled(), onClick = {
title = stringResource(MR.strings.pref_enable_acra), scope.launch {
subtitle = stringResource(MR.strings.pref_acra_summary), CrashLogUtil(context).dumpLogs()
enabled = isPreviewBuildType || isReleaseBuildType, }
), SY <-- */ },
Preference.PreferenceItem.TextPreference( ),
title = stringResource(MR.strings.pref_dump_crash_logs), /* SY --> Preference.PreferenceItem.SwitchPreference(
subtitle = stringResource(MR.strings.pref_dump_crash_logs_summary), pref = networkPreferences.verboseLogging(),
onClick = { title = stringResource(MR.strings.pref_verbose_logging),
scope.launch { subtitle = stringResource(MR.strings.pref_verbose_logging_summary),
CrashLogUtil(context).dumpLogs() onValueChanged = {
} context.toast(MR.strings.requires_app_restart)
}, true
), },
/* SY --> Preference.PreferenceItem.SwitchPreference( ), SY <-- */
pref = networkPreferences.verboseLogging(), Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_verbose_logging), title = stringResource(MR.strings.pref_debug_info),
subtitle = stringResource(MR.strings.pref_verbose_logging_summary), onClick = { navigator.push(DebugInfoScreen()) },
onValueChanged = { ),
context.toast(MR.strings.requires_app_restart) Preference.PreferenceItem.TextPreference(
true title = stringResource(MR.strings.pref_onboarding_guide),
}, onClick = { navigator.push(OnboardingScreen()) },
), SY <-- */ ),
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_debug_info), title = stringResource(MR.strings.pref_manage_notifications),
onClick = { navigator.push(DebugInfoScreen()) }, onClick = {
), val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
Preference.PreferenceItem.TextPreference( putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
title = stringResource(MR.strings.pref_onboarding_guide), }
onClick = { navigator.push(OnboardingScreen()) }, context.startActivity(intent)
), },
), ),
) getBackgroundActivityGroup(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { getDataGroup(),
add( getNetworkGroup(networkPreferences = networkPreferences),
Preference.PreferenceItem.TextPreference( getLibraryGroup(),
title = stringResource(MR.strings.pref_manage_notifications), getReaderGroup(basePreferences = basePreferences),
onClick = { getExtensionsGroup(basePreferences = basePreferences),
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { // SY -->
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) // getDownloaderGroup(),
} getDataSaverGroup(),
context.startActivity(intent) getDeveloperToolsGroup(),
}, // SY <--
), )
)
}
addAll(
listOf(
getBackgroundActivityGroup(),
getDataGroup(),
getNetworkGroup(networkPreferences = networkPreferences),
getLibraryGroup(),
getExtensionsGroup(basePreferences = basePreferences),
// SY -->
// getDownloaderGroup(),
getDataSaverGroup(),
getDeveloperToolsGroup(),
// SY <--
),
)
}
} }
@Composable @Composable
@@ -368,6 +352,34 @@ object SettingsAdvancedScreen : SearchableSettings {
) )
} }
@Composable
private fun getReaderGroup(
basePreferences: BasePreferences,
): Preference.PreferenceGroup {
val context = LocalContext.current
val chooseColorProfile = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
) { uri ->
uri?.let {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags)
basePreferences.displayProfile().set(uri.toString())
}
}
return Preference.PreferenceGroup(
title = stringResource(MR.strings.pref_category_reader),
preferenceItems = persistentListOf(
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.pref_display_profile),
subtitle = basePreferences.displayProfile().get(),
onClick = {
chooseColorProfile.launch(arrayOf("*/*"))
},
),
)
)
}
@Composable @Composable
private fun getExtensionsGroup( private fun getExtensionsGroup(
basePreferences: BasePreferences, basePreferences: BasePreferences,
@@ -23,11 +23,12 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.time.Instant import java.time.LocalDate
object SettingsAppearanceScreen : SearchableSettings { object SettingsAppearanceScreen : SearchableSettings {
@@ -106,7 +107,7 @@ object SettingsAppearanceScreen : SearchableSettings {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val now = remember { Instant.now().toEpochMilli() } val now = remember { LocalDate.now() }
val dateFormat by uiPreferences.dateFormat().collectAsState() val dateFormat by uiPreferences.dateFormat().collectAsState()
val formattedNow = remember(dateFormat) { val formattedNow = remember(dateFormat) {
@@ -157,6 +158,8 @@ object SettingsAppearanceScreen : SearchableSettings {
// SY --> // SY -->
@Composable @Composable
fun getForkGroup(uiPreferences: UiPreferences): Preference.PreferenceGroup { fun getForkGroup(uiPreferences: UiPreferences): Preference.PreferenceGroup {
val previewsRowCount by uiPreferences.previewsRowCount().collectAsState()
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
stringResource(SYMR.strings.pref_category_fork), stringResource(SYMR.strings.pref_category_fork),
preferenceItems = persistentListOf( preferenceItems = persistentListOf(
@@ -174,6 +177,21 @@ object SettingsAppearanceScreen : SearchableSettings {
title = stringResource(SYMR.strings.put_merge_in_overflow), title = stringResource(SYMR.strings.put_merge_in_overflow),
subtitle = stringResource(SYMR.strings.put_merge_in_overflow_summary), subtitle = stringResource(SYMR.strings.put_merge_in_overflow_summary),
), ),
Preference.PreferenceItem.SliderPreference(
value = previewsRowCount,
title = stringResource(SYMR.strings.pref_previews_row_count),
subtitle = if (previewsRowCount > 0) pluralStringResource(
SYMR.plurals.row_count,
previewsRowCount,
previewsRowCount,
) else stringResource(MR.strings.disabled),
min = 0,
max = 10,
onValueChanged = {
uiPreferences.previewsRowCount().set(it)
true
},
),
), ),
) )
} }
@@ -2,6 +2,7 @@ package eu.kanade.presentation.more.settings.screen
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -17,6 +18,7 @@ import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryScreen import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryScreen
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.domain.UnsortedPreferences import tachiyomi.domain.UnsortedPreferences
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -39,7 +41,9 @@ object SettingsBrowseScreen : SearchableSettings {
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val sourcePreferences = remember { Injekt.get<SourcePreferences>() } val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
val reposCount by sourcePreferences.extensionRepos().collectAsState() val getExtensionRepoCount = remember { Injekt.get<GetExtensionRepoCount>() }
val reposCount by getExtensionRepoCount.subscribe().collectAsState(0)
// SY --> // SY -->
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -104,7 +108,7 @@ object SettingsBrowseScreen : SearchableSettings {
), ),
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.label_extension_repos), title = stringResource(MR.strings.label_extension_repos),
subtitle = pluralStringResource(MR.plurals.num_repos, reposCount.size, reposCount.size), subtitle = pluralStringResource(MR.plurals.num_repos, reposCount, reposCount),
onClick = { onClick = {
navigator.push(ExtensionReposScreen()) navigator.push(ExtensionReposScreen())
}, },
@@ -15,16 +15,19 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MultiChoiceSegmentedButtonRow import androidx.compose.material3.MultiChoiceSegmentedButtonRow
import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -35,10 +38,13 @@ import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.domain.sync.SyncPreferences
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
import eu.kanade.presentation.more.settings.screen.data.StorageInfo import eu.kanade.presentation.more.settings.screen.data.StorageInfo
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
import eu.kanade.presentation.more.settings.screen.data.SyncTriggerOptionsScreen
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.presentation.util.relativeTimeSpanString
@@ -46,10 +52,15 @@ import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.cache.PagePreviewCache import eu.kanade.tachiyomi.data.cache.PagePreviewCache
import eu.kanade.tachiyomi.data.sync.SyncDataJob
import eu.kanade.tachiyomi.data.sync.SyncManager
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveService
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveSyncService
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.storage.displayablePath import tachiyomi.core.common.storage.displayablePath
@@ -91,13 +102,16 @@ object SettingsDataScreen : SearchableSettings {
val backupPreferences = Injekt.get<BackupPreferences>() val backupPreferences = Injekt.get<BackupPreferences>()
val storagePreferences = Injekt.get<StoragePreferences>() val storagePreferences = Injekt.get<StoragePreferences>()
val syncPreferences = remember { Injekt.get<SyncPreferences>() }
val syncService by syncPreferences.syncService().collectAsState()
return persistentListOf( return persistentListOf(
getStorageLocationPref(storagePreferences = storagePreferences), getStorageLocationPref(storagePreferences = storagePreferences),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)), Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.pref_storage_location_info)),
getBackupAndRestoreGroup(backupPreferences = backupPreferences), getBackupAndRestoreGroup(backupPreferences = backupPreferences),
getDataGroup(), getDataGroup(),
) ) + getSyncPreferences(syncPreferences = syncPreferences, syncService = syncService)
} }
@Composable @Composable
@@ -113,7 +127,17 @@ object SettingsDataScreen : SearchableSettings {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flags) // For some reason InkBook devices do not implement the SAF properly. Persistable URI grants do not
// work. However, simply retrieving the URI and using it works fine for these devices. Access is not
// revoked after the app is closed or the device is restarted.
// This also holds for some Samsung devices. Thus, we simply execute inside of a try-catch block and
// ignore the exception if it is thrown.
try {
context.contentResolver.takePersistableUriPermission(uri, flags)
} catch (e: SecurityException) {
logcat(LogPriority.ERROR, e)
context.toast(MR.strings.file_picker_uri_permission_unsupported)
}
UniFile.fromUri(context, uri)?.let { UniFile.fromUri(context, uri)?.let {
storageDirPref.set(it.uri.toString()) storageDirPref.set(it.uri.toString())
@@ -330,4 +354,225 @@ object SettingsDataScreen : SearchableSettings {
), ),
) )
} }
@Composable
private fun getSyncPreferences(syncPreferences: SyncPreferences, syncService: Int): List<Preference> {
return listOf(
Preference.PreferenceGroup(
title = stringResource(SYMR.strings.pref_sync_service_category),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
pref = syncPreferences.syncService(),
title = stringResource(SYMR.strings.pref_sync_service),
entries = persistentMapOf(
SyncManager.SyncService.NONE.value to stringResource(MR.strings.off),
SyncManager.SyncService.SYNCYOMI.value to stringResource(SYMR.strings.syncyomi),
SyncManager.SyncService.GOOGLE_DRIVE.value to stringResource(SYMR.strings.google_drive),
),
onValueChanged = { true },
),
),
),
) + getSyncServicePreferences(syncPreferences, syncService)
}
@Composable
private fun getSyncServicePreferences(syncPreferences: SyncPreferences, syncService: Int): List<Preference> {
val syncServiceType = SyncManager.SyncService.fromInt(syncService)
val basePreferences = getBasePreferences(syncServiceType, syncPreferences)
return if (syncServiceType != SyncManager.SyncService.NONE) {
basePreferences + getAdditionalPreferences(syncPreferences)
} else {
basePreferences
}
}
@Composable
private fun getBasePreferences(
syncServiceType: SyncManager.SyncService,
syncPreferences: SyncPreferences,
): List<Preference> {
return when (syncServiceType) {
SyncManager.SyncService.NONE -> emptyList()
SyncManager.SyncService.SYNCYOMI -> getSelfHostPreferences(syncPreferences)
SyncManager.SyncService.GOOGLE_DRIVE -> getGoogleDrivePreferences()
}
}
@Composable
private fun getAdditionalPreferences(syncPreferences: SyncPreferences): List<Preference> {
return listOf(getSyncNowPref(), getAutomaticSyncGroup(syncPreferences))
}
@Composable
private fun getGoogleDrivePreferences(): List<Preference> {
val context = LocalContext.current
val googleDriveSync = Injekt.get<GoogleDriveService>()
return listOf(
Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.pref_google_drive_sign_in),
onClick = {
val intent = googleDriveSync.getSignInIntent()
context.startActivity(intent)
},
),
getGoogleDrivePurge(),
)
}
@Composable
private fun getGoogleDrivePurge(): Preference.PreferenceItem.TextPreference {
val scope = rememberCoroutineScope()
val context = LocalContext.current
val googleDriveSync = remember { GoogleDriveSyncService(context) }
var showPurgeDialog by remember { mutableStateOf(false) }
if (showPurgeDialog) {
PurgeConfirmationDialog(
onConfirm = {
showPurgeDialog = false
scope.launch {
val result = googleDriveSync.deleteSyncDataFromGoogleDrive()
when (result) {
GoogleDriveSyncService.DeleteSyncDataStatus.NOT_INITIALIZED -> context.toast(
SYMR.strings.google_drive_not_signed_in,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.NO_FILES -> context.toast(
SYMR.strings.google_drive_sync_data_not_found,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.SUCCESS -> context.toast(
SYMR.strings.google_drive_sync_data_purged,
duration = 5000,
)
GoogleDriveSyncService.DeleteSyncDataStatus.ERROR -> context.toast(
SYMR.strings.google_drive_sync_data_purge_error,
duration = 10000,
)
}
}
},
onDismissRequest = { showPurgeDialog = false },
)
}
return Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.pref_google_drive_purge_sync_data),
onClick = { showPurgeDialog = true },
)
}
@Composable
private fun PurgeConfirmationDialog(
onConfirm: () -> Unit,
onDismissRequest: () -> Unit,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
title = { Text(text = stringResource(SYMR.strings.pref_purge_confirmation_title)) },
text = { Text(text = stringResource(SYMR.strings.pref_purge_confirmation_message)) },
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
confirmButton = {
TextButton(onClick = onConfirm) {
Text(text = stringResource(MR.strings.action_ok))
}
},
)
}
@Composable
private fun getSelfHostPreferences(syncPreferences: SyncPreferences): List<Preference> {
val scope = rememberCoroutineScope()
return listOf(
Preference.PreferenceItem.EditTextPreference(
title = stringResource(SYMR.strings.pref_sync_host),
subtitle = stringResource(SYMR.strings.pref_sync_host_summ),
pref = syncPreferences.clientHost(),
onValueChanged = { newValue ->
scope.launch {
// Trim spaces at the beginning and end, then remove trailing slash if present
val trimmedValue = newValue.trim()
val modifiedValue = trimmedValue.trimEnd { it == '/' }
syncPreferences.clientHost().set(modifiedValue)
}
true
},
),
Preference.PreferenceItem.EditTextPreference(
title = stringResource(SYMR.strings.pref_sync_api_key),
subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
pref = syncPreferences.clientAPIKey(),
),
)
}
@Composable
private fun getSyncNowPref(): Preference.PreferenceGroup {
val navigator = LocalNavigator.currentOrThrow
return Preference.PreferenceGroup(
title = stringResource(SYMR.strings.pref_sync_now_group_title),
preferenceItems = persistentListOf(
getSyncOptionsPref(),
Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.pref_sync_now),
subtitle = stringResource(SYMR.strings.pref_sync_now_subtitle),
onClick = {
navigator.push(SyncSettingsSelector())
},
),
),
)
}
@Composable
private fun getSyncOptionsPref(): Preference.PreferenceItem.TextPreference {
val navigator = LocalNavigator.currentOrThrow
return Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.pref_sync_options),
subtitle = stringResource(SYMR.strings.pref_sync_options_summ),
onClick = { navigator.push(SyncTriggerOptionsScreen()) },
)
}
@Composable
private fun getAutomaticSyncGroup(syncPreferences: SyncPreferences): Preference.PreferenceGroup {
val context = LocalContext.current
val syncIntervalPref = syncPreferences.syncInterval()
val lastSync by syncPreferences.lastSyncTimestamp().collectAsState()
return Preference.PreferenceGroup(
title = stringResource(SYMR.strings.pref_sync_automatic_category),
preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
pref = syncIntervalPref,
title = stringResource(SYMR.strings.pref_sync_interval),
entries = persistentMapOf(
0 to stringResource(MR.strings.off),
30 to stringResource(SYMR.strings.update_30min),
60 to stringResource(SYMR.strings.update_1hour),
180 to stringResource(SYMR.strings.update_3hour),
360 to stringResource(MR.strings.update_6hour),
720 to stringResource(MR.strings.update_12hour),
1440 to stringResource(MR.strings.update_24hour),
2880 to stringResource(MR.strings.update_48hour),
10080 to stringResource(MR.strings.update_weekly),
),
onValueChanged = {
SyncDataJob.setupTask(context, it)
true
},
),
Preference.PreferenceItem.InfoPreference(
stringResource(SYMR.strings.last_synchronization, relativeTimeSpanString(lastSync)),
),
),
)
}
} }
@@ -84,9 +84,6 @@ object SettingsLibraryScreen : SearchableSettings {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
val defaultCategory by libraryPreferences.defaultCategory().collectAsState()
val selectedCategory = allCategories.find { it.id == defaultCategory.toLong() }
// For default category // For default category
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) + val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
allCategories.fastMap { it.id.toInt() } allCategories.fastMap { it.id.toInt() }
@@ -108,7 +105,6 @@ object SettingsLibraryScreen : SearchableSettings {
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = libraryPreferences.defaultCategory(), pref = libraryPreferences.defaultCategory(),
title = stringResource(MR.strings.default_category), title = stringResource(MR.strings.default_category),
subtitle = selectedCategory?.visualName ?: stringResource(MR.strings.default_category_summary),
entries = ids.zip(labels).toMap().toImmutableMap(), entries = ids.zip(labels).toMap().toImmutableMap(),
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
@@ -235,11 +231,13 @@ object SettingsLibraryScreen : SearchableSettings {
pref = libraryPreferences.newShowUpdatesCount(), pref = libraryPreferences.newShowUpdatesCount(),
title = stringResource(MR.strings.pref_library_update_show_tab_badge), title = stringResource(MR.strings.pref_library_update_show_tab_badge),
), ),
// SY -->
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = libraryPreferences.libraryReadDuplicateChapters(), pref = libraryPreferences.libraryReadDuplicateChapters(),
title = stringResource(MR.strings.pref_library_mark_duplicate_chapters), title = stringResource(SYMR.strings.pref_library_mark_duplicate_chapters),
subtitle = stringResource(MR.strings.pref_library_mark_duplicate_chapters_summary), subtitle = stringResource(SYMR.strings.pref_library_mark_duplicate_chapters_summary),
), ),
// SY <--
), ),
) )
} }
@@ -17,6 +17,7 @@ import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@@ -35,6 +36,7 @@ object SettingsReaderScreen : SearchableSettings {
// SY --> // SY -->
val forceHorizontalSeekbar by readerPref.forceHorizontalSeekbar().collectAsState() val forceHorizontalSeekbar by readerPref.forceHorizontalSeekbar().collectAsState()
// SY <-- // SY <--
return listOf( return listOf(
Preference.PreferenceItem.ListPreference( Preference.PreferenceItem.ListPreference(
pref = readerPref.defaultReadingMode(), pref = readerPref.defaultReadingMode(),
@@ -81,24 +83,14 @@ object SettingsReaderScreen : SearchableSettings {
enabled = !forceHorizontalSeekbar, enabled = !forceHorizontalSeekbar,
), ),
// SY <-- // SY <--
Preference.PreferenceItem.SwitchPreference(
pref = readerPref.trueColor(),
title = stringResource(MR.strings.pref_true_color),
subtitle = stringResource(MR.strings.pref_true_color_summary),
enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O,
),
/* SY --> /* SY -->
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPref.pageTransitions(), pref = readerPref.pageTransitions(),
title = stringResource(MR.strings.pref_page_transitions), title = stringResource(MR.strings.pref_page_transitions),
), ),
SY <-- */ SY <-- */
Preference.PreferenceItem.SwitchPreference(
pref = readerPref.flashOnPageChange(),
title = stringResource(MR.strings.pref_flash_page),
subtitle = stringResource(MR.strings.pref_flash_page_summ),
),
getDisplayGroup(readerPreferences = readerPref), getDisplayGroup(readerPreferences = readerPref),
getEInkGroup(readerPreferences = readerPref),
getReadingGroup(readerPreferences = readerPref), getReadingGroup(readerPreferences = readerPref),
getPagedGroup(readerPreferences = readerPref), getPagedGroup(readerPreferences = readerPref),
getWebtoonGroup(readerPreferences = readerPref), getWebtoonGroup(readerPreferences = readerPref),
@@ -161,6 +153,65 @@ object SettingsReaderScreen : SearchableSettings {
) )
} }
@Composable
private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
val flashPageState by readerPreferences.flashOnPageChange().collectAsState()
val flashMillisPref = readerPreferences.flashDurationMillis()
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = readerPreferences.flashPageInterval()
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = readerPreferences.flashColor()
return Preference.PreferenceGroup(
title = "E-Ink",
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.flashOnPageChange(),
title = stringResource(MR.strings.pref_flash_page),
subtitle = stringResource(MR.strings.pref_flash_page_summ),
),
Preference.PreferenceItem.SliderPreference(
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
min = 1,
max = 15,
title = stringResource(MR.strings.pref_flash_duration),
subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
onValueChanged = {
flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
true
},
enabled = flashPageState,
),
Preference.PreferenceItem.SliderPreference(
value = flashInterval,
min = 1,
max = 10,
title = stringResource(MR.strings.pref_flash_page_interval),
subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
onValueChanged = {
flashIntervalPref.set(it)
true
},
enabled = flashPageState,
),
Preference.PreferenceItem.ListPreference(
pref = flashColorPref,
title = stringResource(MR.strings.pref_flash_with),
entries = persistentMapOf(
ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
ReaderPreferences.FlashColor.WHITE_BLACK
to stringResource(MR.strings.pref_flash_style_white_black),
),
enabled = flashPageState,
),
),
)
}
@Composable @Composable
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup { private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup( return Preference.PreferenceGroup(
@@ -178,6 +229,13 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPreferences.skipDupe(), pref = readerPreferences.skipDupe(),
title = stringResource(MR.strings.pref_skip_dupe_chapters), title = stringResource(MR.strings.pref_skip_dupe_chapters),
), ),
// SY -->
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.markReadDupe(),
title = stringResource(SYMR.strings.pref_mark_read_dupe_chapters),
subtitle = stringResource(SYMR.strings.pref_mark_read_dupe_chapters_summary),
),
// SY <--
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.alwaysShowChapterTransition(), pref = readerPreferences.alwaysShowChapterTransition(),
title = stringResource(MR.strings.pref_always_show_chapter_transition), title = stringResource(MR.strings.pref_always_show_chapter_transition),
@@ -382,17 +440,16 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.webtoonDoubleTapZoomEnabled(), pref = readerPreferences.webtoonDoubleTapZoomEnabled(),
title = stringResource(MR.strings.pref_double_tap_zoom), title = stringResource(MR.strings.pref_double_tap_zoom),
enabled = true, ),
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.webtoonDisableZoomOut(),
title = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
), ),
// SY --> // SY -->
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.pageTransitionsWebtoon(), pref = readerPreferences.pageTransitionsWebtoon(),
title = stringResource(MR.strings.pref_page_transitions), title = stringResource(MR.strings.pref_page_transitions),
), ),
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.webtoonEnableZoomOut(),
title = stringResource(SYMR.strings.enable_zoom_out),
),
// SY <-- // SY <--
), ),
) )
@@ -566,10 +623,14 @@ object SettingsReaderScreen : SearchableSettings {
.toMap() .toMap()
.toImmutableMap(), .toImmutableMap(),
), ),
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.ListPreference(
pref = readerPreferences.cacheArchiveMangaOnDisk(), pref = readerPreferences.archiveReaderMode(),
title = stringResource(SYMR.strings.cache_archived_manga_to_disk), title = stringResource(SYMR.strings.pref_archive_reader_mode),
subtitle = stringResource(SYMR.strings.cache_archived_manga_to_disk_subtitle), subtitle = stringResource(SYMR.strings.pref_archive_reader_mode_summary),
entries = ReaderPreferences.archiveModeTypes
.mapIndexed { index, it -> index to stringResource(it) }
.toMap()
.toImmutableMap(),
), ),
), ),
) )
@@ -56,10 +56,9 @@ import tachiyomi.presentation.core.icons.Reddit
import tachiyomi.presentation.core.icons.X import tachiyomi.presentation.core.icons.X
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.text.DateFormat import java.time.Instant
import java.text.SimpleDateFormat import java.time.LocalDateTime
import java.util.Locale import java.time.ZoneId
import java.util.TimeZone
object AboutScreen : Screen() { object AboutScreen : Screen() {
@@ -293,18 +292,15 @@ object AboutScreen : Screen() {
internal fun getFormattedBuildTime(): String { internal fun getFormattedBuildTime(): String {
return try { return try {
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) LocalDateTime.ofInstant(
inputDf.timeZone = TimeZone.getTimeZone("UTC") Instant.parse(BuildConfig.BUILD_TIME),
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME) ZoneId.systemDefault(),
val outputDf = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM,
DateFormat.SHORT,
Locale.getDefault(),
) )
outputDf.timeZone = TimeZone.getDefault() .toDateTimestampString(
UiPreferences.dateFormat(
buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get())) Injekt.get<UiPreferences>().dateFormat().get(),
),
)
} catch (e: Exception) { } catch (e: Exception) {
BuildConfig.BUILD_TIME BuildConfig.BUILD_TIME
} }
@@ -32,12 +32,13 @@ class OpenSourceLicensesScreen : Screen() {
.fillMaxSize(), .fillMaxSize(),
contentPadding = contentPadding, contentPadding = contentPadding,
onLibraryClick = { onLibraryClick = {
val libraryLicenseScreen = OpenSourceLibraryLicenseScreen( navigator.push(
name = it.library.name, OpenSourceLibraryLicenseScreen(
website = it.library.website, name = it.name,
license = it.library.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(), website = it.website,
license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
)
) )
navigator.push(libraryLicenseScreen)
}, },
) )
} }
@@ -8,11 +8,14 @@ import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoConflictDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog
import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionReposScreen import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionReposScreen
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
@@ -42,17 +45,19 @@ class ExtensionReposScreen(
ExtensionReposScreen( ExtensionReposScreen(
state = successState, state = successState,
onClickCreate = { screenModel.showDialog(RepoDialog.Create) }, onClickCreate = { screenModel.showDialog(RepoDialog.Create) },
onOpenWebsite = { context.openInBrowser(it.website) },
onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) }, onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) },
onClickRefresh = { screenModel.refreshRepos() },
navigateUp = navigator::pop, navigateUp = navigator::pop,
) )
when (val dialog = successState.dialog) { when (val dialog = successState.dialog) {
null -> {} null -> {}
RepoDialog.Create -> { is RepoDialog.Create -> {
ExtensionRepoCreateDialog( ExtensionRepoCreateDialog(
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onCreate = { screenModel.createRepo(it) }, onCreate = { screenModel.createRepo(it) },
repos = successState.repos, repoUrls = successState.repos.map { it.baseUrl }.toImmutableSet(),
) )
} }
is RepoDialog.Delete -> { is RepoDialog.Delete -> {
@@ -62,6 +67,15 @@ class ExtensionReposScreen(
repo = dialog.repo, repo = dialog.repo,
) )
} }
is RepoDialog.Conflict -> {
ExtensionRepoConflictDialog(
onDismissRequest = screenModel::dismissDialog,
onMigrate = { screenModel.replaceRepo(dialog.newRepo) },
oldRepo = dialog.oldRepo,
newRepo = dialog.newRepo,
)
}
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -4,24 +4,29 @@ import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.extension.interactor.CreateExtensionRepo
import eu.kanade.domain.extension.interactor.DeleteExtensionRepo
import eu.kanade.domain.extension.interactor.GetExtensionRepos
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
import mihon.domain.extensionrepo.model.ExtensionRepo
import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class ExtensionReposScreenModel( class ExtensionReposScreenModel(
private val getExtensionRepos: GetExtensionRepos = Injekt.get(), private val getExtensionRepo: GetExtensionRepo = Injekt.get(),
private val createExtensionRepo: CreateExtensionRepo = Injekt.get(), private val createExtensionRepo: CreateExtensionRepo = Injekt.get(),
private val deleteExtensionRepo: DeleteExtensionRepo = Injekt.get(), private val deleteExtensionRepo: DeleteExtensionRepo = Injekt.get(),
private val replaceExtensionRepo: ReplaceExtensionRepo = Injekt.get(),
private val updateExtensionRepo: UpdateExtensionRepo = Injekt.get(),
) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) { ) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) {
private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE) private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE)
@@ -29,7 +34,7 @@ class ExtensionReposScreenModel(
init { init {
screenModelScope.launchIO { screenModelScope.launchIO {
getExtensionRepos.subscribe() getExtensionRepo.subscribeAll()
.collectLatest { repos -> .collectLatest { repos ->
mutableState.update { mutableState.update {
RepoScreenState.Success( RepoScreenState.Success(
@@ -43,25 +48,51 @@ class ExtensionReposScreenModel(
/** /**
* Creates and adds a new repo to the database. * Creates and adds a new repo to the database.
* *
* @param name The name of the repo to create. * @param baseUrl The baseUrl of the repo to create.
*/ */
fun createRepo(name: String) { fun createRepo(baseUrl: String) {
screenModelScope.launchIO { screenModelScope.launchIO {
when (createExtensionRepo.await(name)) { when (val result = createExtensionRepo.await(baseUrl)) {
is CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl) CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
CreateExtensionRepo.Result.RepoAlreadyExists -> _events.send(RepoEvent.RepoAlreadyExists)
is CreateExtensionRepo.Result.DuplicateFingerprint -> {
showDialog(RepoDialog.Conflict(result.oldRepo, result.newRepo))
}
else -> {} else -> {}
} }
} }
} }
/** /**
* Deletes the given repo from the database. * Inserts a repo to the database, replace a matching repo with the same signing key fingerprint if found.
* *
* @param repo The repo to delete. * @param newRepo The repo to insert
*/ */
fun deleteRepo(repo: String) { fun replaceRepo(newRepo: ExtensionRepo) {
screenModelScope.launchIO { screenModelScope.launchIO {
deleteExtensionRepo.await(repo) replaceExtensionRepo.await(newRepo)
}
}
/**
* Refreshes information for each repository.
*/
fun refreshRepos() {
val status = state.value
if (status is RepoScreenState.Success) {
screenModelScope.launchIO {
updateExtensionRepo.awaitAll()
}
}
}
/**
* Deletes the given repo from the database
*/
fun deleteRepo(baseUrl: String) {
screenModelScope.launchIO {
deleteExtensionRepo.await(baseUrl)
} }
} }
@@ -87,11 +118,13 @@ class ExtensionReposScreenModel(
sealed class RepoEvent { sealed class RepoEvent {
sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent() sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent()
data object InvalidUrl : LocalizedMessage(MR.strings.invalid_repo_name) data object InvalidUrl : LocalizedMessage(MR.strings.invalid_repo_name)
data object RepoAlreadyExists : LocalizedMessage(MR.strings.error_repo_exists)
} }
sealed class RepoDialog { sealed class RepoDialog {
data object Create : RepoDialog() data object Create : RepoDialog()
data class Delete(val repo: String) : RepoDialog() data class Delete(val repo: String) : RepoDialog()
data class Conflict(val oldRepo: ExtensionRepo, val newRepo: ExtensionRepo) : RepoDialog()
} }
sealed class RepoScreenState { sealed class RepoScreenState {
@@ -101,7 +134,8 @@ sealed class RepoScreenState {
@Immutable @Immutable
data class Success( data class Success(
val repos: ImmutableSet<String>, val repos: ImmutableSet<ExtensionRepo>,
val oldRepos: ImmutableSet<String>? = null,
val dialog: RepoDialog? = null, val dialog: RepoDialog? = null,
) : RepoScreenState() { ) : RepoScreenState() {
@@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.automirrored.outlined.OpenInNew
import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
@@ -22,15 +23,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import mihon.domain.extensionrepo.model.ExtensionRepo
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@Composable @Composable
fun ExtensionReposContent( fun ExtensionReposContent(
repos: ImmutableSet<String>, repos: ImmutableSet<ExtensionRepo>,
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onOpenWebsite: (ExtensionRepo) -> Unit,
onClickDelete: (String) -> Unit, onClickDelete: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@@ -45,7 +48,8 @@ fun ExtensionReposContent(
ExtensionRepoListItem( ExtensionRepoListItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
repo = it, repo = it,
onDelete = { onClickDelete(it) }, onOpenWebsite = { onOpenWebsite(it) },
onDelete = { onClickDelete(it.baseUrl) },
) )
} }
} }
@@ -54,7 +58,8 @@ fun ExtensionReposContent(
@Composable @Composable
private fun ExtensionRepoListItem( private fun ExtensionRepoListItem(
repo: String, repo: ExtensionRepo,
onOpenWebsite: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@@ -74,16 +79,27 @@ private fun ExtensionRepoListItem(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null) Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium)) Text(
text = repo.name,
modifier = Modifier.padding(start = MaterialTheme.padding.medium),
style = MaterialTheme.typography.titleMedium,
)
} }
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End, horizontalArrangement = Arrangement.End,
) { ) {
IconButton(onClick = onOpenWebsite) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.OpenInNew,
contentDescription = stringResource(MR.strings.action_open_in_browser),
)
}
IconButton( IconButton(
onClick = { onClick = {
val url = "$repo/index.min.json" val url = "${repo.baseUrl}/index.min.json"
context.copyToClipboard(url, url) context.copyToClipboard(url, url)
}, },
) { ) {
@@ -1,6 +1,7 @@
package eu.kanade.presentation.more.settings.screen.browse.components package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -14,8 +15,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.text.input.KeyboardType
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import mihon.domain.extensionrepo.model.ExtensionRepo
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@@ -24,12 +27,12 @@ import kotlin.time.Duration.Companion.seconds
fun ExtensionRepoCreateDialog( fun ExtensionRepoCreateDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onCreate: (String) -> Unit, onCreate: (String) -> Unit,
repos: ImmutableSet<String>, repoUrls: ImmutableSet<String>,
) { ) {
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { repos.contains(name) } val nameAlreadyExists = remember(name) { repoUrls.contains(name) }
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -73,6 +76,7 @@ fun ExtensionRepoCreateDialog(
Text(text = stringResource(msgRes)) Text(text = stringResource(msgRes))
}, },
isError = name.isNotEmpty() && nameAlreadyExists, isError = name.isNotEmpty() && nameAlreadyExists,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
singleLine = true, singleLine = true,
) )
} }
@@ -115,3 +119,36 @@ fun ExtensionRepoDeleteDialog(
}, },
) )
} }
@Composable
fun ExtensionRepoConflictDialog(
oldRepo: ExtensionRepo,
newRepo: ExtensionRepo,
onDismissRequest: () -> Unit,
onMigrate: () -> Unit,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(
onClick = {
onMigrate()
onDismissRequest()
},
) {
Text(text = stringResource(MR.strings.action_replace_repo))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(MR.strings.action_cancel))
}
},
title = {
Text(text = stringResource(MR.strings.action_replace_repo_title))
},
text = {
Text(text = stringResource(MR.strings.action_replace_repo_message, newRepo.name, oldRepo.name))
},
)
}
@@ -5,12 +5,17 @@ package eu.kanade.presentation.more.settings.screen.browse.components
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState
import mihon.domain.extensionrepo.model.ExtensionRepo
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@@ -23,7 +28,9 @@ import tachiyomi.presentation.core.util.plus
fun ExtensionReposScreen( fun ExtensionReposScreen(
state: RepoScreenState.Success, state: RepoScreenState.Success,
onClickCreate: () -> Unit, onClickCreate: () -> Unit,
onOpenWebsite: (ExtensionRepo) -> Unit,
onClickDelete: (String) -> Unit, onClickDelete: (String) -> Unit,
onClickRefresh: () -> Unit,
navigateUp: () -> Unit, navigateUp: () -> Unit,
) { ) {
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
@@ -33,6 +40,14 @@ fun ExtensionReposScreen(
navigateUp = navigateUp, navigateUp = navigateUp,
title = stringResource(MR.strings.label_extension_repos), title = stringResource(MR.strings.label_extension_repos),
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
actions = {
IconButton(onClick = onClickRefresh) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(resource = MR.strings.action_webview_refresh),
)
}
},
) )
}, },
floatingActionButton = { floatingActionButton = {
@@ -55,6 +70,7 @@ fun ExtensionReposScreen(
lazyListState = lazyListState, lazyListState = lazyListState,
paddingValues = paddingValues + topSmallPaddingValues + paddingValues = paddingValues + topSmallPaddingValues +
PaddingValues(horizontal = MaterialTheme.padding.medium), PaddingValues(horizontal = MaterialTheme.padding.medium),
onOpenWebsite = onOpenWebsite,
onClickDelete = onClickDelete, onClickDelete = onClickDelete,
) )
} }
@@ -6,7 +6,6 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -69,7 +68,7 @@ class CreateBackupScreen : Screen() {
LazyColumnWithAction( LazyColumnWithAction(
contentPadding = contentPadding, contentPadding = contentPadding,
actionLabel = stringResource(MR.strings.action_create), actionLabel = stringResource(MR.strings.action_create),
actionEnabled = state.options.anyEnabled(), actionEnabled = state.options.canCreate(),
onClickAction = { onClickAction = {
if (!BackupCreateJob.isManualJobRunning(context)) { if (!BackupCreateJob.isManualJobRunning(context)) {
try { try {
@@ -104,7 +103,7 @@ class CreateBackupScreen : Screen() {
} }
@Composable @Composable
private fun ColumnScope.Options( private fun Options(
options: ImmutableList<BackupOptions.Entry>, options: ImmutableList<BackupOptions.Entry>,
state: CreateBackupScreenModel.State, state: CreateBackupScreenModel.State,
model: CreateBackupScreenModel, model: CreateBackupScreenModel,
@@ -63,7 +63,7 @@ class RestoreBackupScreen(
LazyColumnWithAction( LazyColumnWithAction(
contentPadding = contentPadding, contentPadding = contentPadding,
actionLabel = stringResource(MR.strings.action_restore), actionLabel = stringResource(MR.strings.action_restore),
actionEnabled = state.canRestore && state.options.anyEnabled(), actionEnabled = state.canRestore && state.options.canRestore(),
onClickAction = { onClickAction = {
model.startRestore() model.startRestore()
navigator.pop() navigator.pop()
@@ -0,0 +1,157 @@
package eu.kanade.presentation.more.settings.screen.data
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.sync.SyncPreferences
import eu.kanade.domain.sync.models.SyncSettings
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.sync.SyncDataJob
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.update
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.LazyColumnWithAction
import tachiyomi.presentation.core.components.SectionCard
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SyncSettingsSelector : Screen() {
@Composable
override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val model = rememberScreenModel { SyncSettingsSelectorModel() }
val state by model.state.collectAsState()
Scaffold(
topBar = {
AppBar(
title = stringResource(SYMR.strings.pref_choose_what_to_sync),
navigateUp = navigator::pop,
scrollBehavior = it,
)
},
) { contentPadding ->
LazyColumnWithAction(
contentPadding = contentPadding,
actionLabel = stringResource(SYMR.strings.label_sync),
actionEnabled = state.options.canCreate(),
onClickAction = {
if (!SyncDataJob.isRunning(context)) {
model.syncNow(context)
navigator.pop()
} else {
context.toast(SYMR.strings.sync_in_progress)
}
},
) {
item {
SectionCard(MR.strings.label_library) {
Options(BackupOptions.libraryOptions, state, model)
}
}
item {
SectionCard(MR.strings.label_settings) {
Options(BackupOptions.settingsOptions, state, model)
}
}
}
}
}
@Composable
private fun Options(
options: ImmutableList<BackupOptions.Entry>,
state: SyncSettingsSelectorModel.State,
model: SyncSettingsSelectorModel,
) {
options.forEach { option ->
LabeledCheckbox(
label = stringResource(option.label),
checked = option.getter(state.options),
onCheckedChange = {
model.toggle(option.setter, it)
},
enabled = option.enabled(state.options),
)
}
}
}
private class SyncSettingsSelectorModel(
val syncPreferences: SyncPreferences = Injekt.get(),
) : StateScreenModel<SyncSettingsSelectorModel.State>(
State(syncOptionsToBackupOptions(syncPreferences.getSyncSettings())),
) {
fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
mutableState.update {
val updatedOptions = setter(it.options, enabled)
syncPreferences.setSyncSettings(backupOptionsToSyncOptions(updatedOptions))
it.copy(options = updatedOptions)
}
}
fun syncNow(context: Context) {
SyncDataJob.startNow(context)
}
@Immutable
data class State(
val options: BackupOptions = BackupOptions(),
) companion object {
private fun syncOptionsToBackupOptions(syncSettings: SyncSettings): BackupOptions {
return BackupOptions(
libraryEntries = syncSettings.libraryEntries,
categories = syncSettings.categories,
chapters = syncSettings.chapters,
tracking = syncSettings.tracking,
history = syncSettings.history,
appSettings = syncSettings.appSettings,
extensionRepoSettings = syncSettings.extensionRepoSettings,
sourceSettings = syncSettings.sourceSettings,
privateSettings = syncSettings.privateSettings,
// SY -->
customInfo = syncSettings.customInfo,
readEntries = syncSettings.readEntries,
savedSearches = syncSettings.savedSearches,
// SY <--
)
}
private fun backupOptionsToSyncOptions(backupOptions: BackupOptions): SyncSettings {
return SyncSettings(
libraryEntries = backupOptions.libraryEntries,
categories = backupOptions.categories,
chapters = backupOptions.chapters,
tracking = backupOptions.tracking,
history = backupOptions.history,
appSettings = backupOptions.appSettings,
extensionRepoSettings = backupOptions.extensionRepoSettings,
sourceSettings = backupOptions.sourceSettings,
privateSettings = backupOptions.privateSettings,
// SY -->
customInfo = backupOptions.customInfo,
readEntries = backupOptions.readEntries,
savedSearches = backupOptions.savedSearches,
// SY <--
)
}
}
}
@@ -0,0 +1,102 @@
package eu.kanade.presentation.more.settings.screen.data
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.sync.SyncPreferences
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.data.sync.models.SyncTriggerOptions
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.update
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.LazyColumnWithAction
import tachiyomi.presentation.core.components.SectionCard
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SyncTriggerOptionsScreen : Screen() {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val model = rememberScreenModel { SyncOptionsScreenModel() }
val state by model.state.collectAsState()
Scaffold(
topBar = {
AppBar(
title = stringResource(SYMR.strings.pref_sync_options),
navigateUp = navigator::pop,
scrollBehavior = it,
)
},
) { contentPadding ->
LazyColumnWithAction(
contentPadding = contentPadding,
actionLabel = stringResource(MR.strings.action_save),
actionEnabled = true,
onClickAction = {
navigator.pop()
},
) {
item {
SectionCard(SYMR.strings.label_triggers) {
Options(SyncTriggerOptions.mainOptions, state, model)
}
}
}
}
}
@Composable
private fun Options(
options: ImmutableList<SyncTriggerOptions.Entry>,
state: SyncOptionsScreenModel.State,
model: SyncOptionsScreenModel,
) {
options.forEach { option ->
LabeledCheckbox(
label = stringResource(option.label),
checked = option.getter(state.options),
onCheckedChange = {
model.toggle(option.setter, it)
},
enabled = option.enabled(state.options),
)
}
}
}
private class SyncOptionsScreenModel(
val syncPreferences: SyncPreferences = Injekt.get(),
) : StateScreenModel<SyncOptionsScreenModel.State>(
State(
syncPreferences.getSyncTriggerOptions(),
),
) {
fun toggle(setter: (SyncTriggerOptions, Boolean) -> SyncTriggerOptions, enabled: Boolean) {
mutableState.update {
val updatedTriggerOptions = setter(it.options, enabled)
syncPreferences.setSyncTriggerOptions(updatedTriggerOptions)
it.copy(
options = updatedTriggerOptions,
)
}
}
@Immutable
data class State(
val options: SyncTriggerOptions = SyncTriggerOptions(),
)
}
@@ -42,7 +42,9 @@ import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.plus
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
class WorkerInfoScreen : Screen() { class WorkerInfoScreen : Screen() {
@@ -148,13 +150,16 @@ class WorkerInfoScreen : Screen() {
} }
appendLine("State: ${workInfo.state}") appendLine("State: ${workInfo.state}")
if (workInfo.state == WorkInfo.State.ENQUEUED) { if (workInfo.state == WorkInfo.State.ENQUEUED) {
appendLine( val timestamp = LocalDateTime.ofInstant(
"Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString( Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis),
ZoneId.systemDefault(),
)
.toDateTimestampString(
UiPreferences.dateFormat( UiPreferences.dateFormat(
Injekt.get<UiPreferences>().dateFormat().get(), Injekt.get<UiPreferences>().dateFormat().get(),
), ),
)}", )
) appendLine("Next scheduled run: $timestamp",)
appendLine("Attempt #${workInfo.runAttemptCount + 1}") appendLine("Attempt #${workInfo.runAttemptCount + 1}")
} }
appendLine() appendLine()
@@ -223,13 +223,12 @@ fun AppThemePreviewItem(
contentAlignment = Alignment.BottomCenter, contentAlignment = Alignment.BottomCenter,
) { ) {
Surface( Surface(
tonalElevation = 3.dp, color = MaterialTheme.colorScheme.surfaceContainer,
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.height(32.dp) .height(32.dp)
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(horizontal = 8.dp), .padding(horizontal = 8.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
@@ -22,7 +22,10 @@ import exh.source.isEhBasedManga
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import java.util.Date import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.ZonedDateTime
@Composable @Composable
fun ChapterListDialog( fun ChapterListDialog(
@@ -56,9 +59,13 @@ fun ChapterListDialog(
?.let { ?.let {
// SY --> // SY -->
if (manga?.isEhBasedManga() == true) { if (manga?.isEhBasedManga() == true) {
MetadataUtil.EX_DATE_FORMAT.format(Date(it)) MetadataUtil.EX_DATE_FORMAT
.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.systemDefault()))
} else { } else {
Date(it).toRelativeString(context, dateRelativeTime, chapterItem.dateFormat) LocalDate.ofInstant(
Instant.ofEpochMilli(it),
ZoneId.systemDefault(),
).toRelativeString(context, dateRelativeTime, chapterItem.dateFormat)
} }
// SY <-- // SY <--
}, },
@@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.time.Duration.Companion.milliseconds
@Stable @Stable
class DisplayRefreshHost { class DisplayRefreshHost {
internal var currentDisplayRefresh by mutableStateOf(false) internal var currentDisplayRefresh by mutableStateOf(false)
private val readerPreferences = Injekt.get<ReaderPreferences>()
internal val flashMillis = readerPreferences.flashDurationMillis()
internal val flashMode = readerPreferences.flashColor()
internal val flashIntervalPref = readerPreferences.flashPageInterval()
// Internal State for Flash
private var flashInterval = flashIntervalPref.get()
private var timesCalled = 0
fun flash() { fun flash() {
currentDisplayRefresh = true if (timesCalled % flashInterval == 0) {
currentDisplayRefresh = true
}
timesCalled += 1
}
fun setInterval(interval: Int) {
flashInterval = interval
timesCalled = 0
} }
} }
@@ -29,18 +52,39 @@ fun DisplayRefreshHost(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val currentDisplayRefresh = hostState.currentDisplayRefresh val currentDisplayRefresh = hostState.currentDisplayRefresh
val refreshDuration by hostState.flashMillis.collectAsState()
val flashMode by hostState.flashMode.collectAsState()
val flashInterval by hostState.flashIntervalPref.collectAsState()
var currentColor by remember { mutableStateOf<Color?>(null) }
LaunchedEffect(currentDisplayRefresh) { LaunchedEffect(currentDisplayRefresh) {
if (currentDisplayRefresh) { if (!currentDisplayRefresh) {
delay(1.5.seconds) currentColor = null
hostState.currentDisplayRefresh = false return@LaunchedEffect
} }
val refreshDurationHalf = refreshDuration.milliseconds / 2
currentColor = if (flashMode == ReaderPreferences.FlashColor.BLACK) {
Color.Black
} else {
Color.White
}
delay(refreshDurationHalf)
if (flashMode == ReaderPreferences.FlashColor.WHITE_BLACK) {
currentColor = Color.Black
}
delay(refreshDurationHalf)
hostState.currentDisplayRefresh = false
}
LaunchedEffect(flashInterval) {
hostState.setInterval(flashInterval)
} }
Canvas( Canvas(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
) { ) {
if (currentDisplayRefresh) { currentColor?.let { drawRect(it) }
drawRect(Color.Black)
}
} }
} }
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.Photo import androidx.compose.material.icons.outlined.Photo
import androidx.compose.material.icons.outlined.Save import androidx.compose.material.icons.outlined.Save
import androidx.compose.material.icons.outlined.Share import androidx.compose.material.icons.outlined.Share
@@ -31,9 +32,9 @@ fun ReaderPageActionsDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
// SY --> // SY -->
onSetAsCover: (useExtraPage: Boolean) -> Unit, onSetAsCover: (useExtraPage: Boolean) -> Unit,
onShare: (useExtraPage: Boolean) -> Unit, onShare: (copy: Boolean, useExtraPage: Boolean) -> Unit,
onSave: (useExtraPage: Boolean) -> Unit, onSave: (useExtraPage: Boolean) -> Unit,
onShareCombined: () -> Unit, onShareCombined: (copy: Boolean) -> Unit,
onSaveCombined: () -> Unit, onSaveCombined: () -> Unit,
hasExtraPage: Boolean, hasExtraPage: Boolean,
// SY <-- // SY <--
@@ -43,9 +44,7 @@ fun ReaderPageActionsDialog(
var useExtraPage by remember { mutableStateOf(false) } var useExtraPage by remember { mutableStateOf(false) }
// SY <-- // SY <--
AdaptiveSheet( AdaptiveSheet(onDismissRequest = onDismissRequest) {
onDismissRequest = onDismissRequest,
) {
Column(modifier = Modifier.padding(vertical = 16.dp)) { Column(modifier = Modifier.padding(vertical = 16.dp)) {
Row( Row(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
@@ -64,6 +63,25 @@ fun ReaderPageActionsDialog(
icon = Icons.Outlined.Photo, icon = Icons.Outlined.Photo,
onClick = { showSetCoverDialog = true }, onClick = { showSetCoverDialog = true },
) )
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(
// SY -->
if (hasExtraPage) {
SYMR.strings.action_copy_first_page
} else {
MR.strings.action_copy_to_clipboard
},
// SY <--
),
icon = Icons.Outlined.ContentCopy,
onClick = {
// SY -->
onShare(true, false)
// SY <--
onDismissRequest()
},
)
ActionButton( ActionButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
title = stringResource( title = stringResource(
@@ -78,7 +96,7 @@ fun ReaderPageActionsDialog(
icon = Icons.Outlined.Share, icon = Icons.Outlined.Share,
onClick = { onClick = {
// SY --> // SY -->
onShare(false) onShare(false, false)
// SY <-- // SY <--
onDismissRequest() onDismissRequest()
}, },
@@ -116,12 +134,21 @@ fun ReaderPageActionsDialog(
showSetCoverDialog = true showSetCoverDialog = true
}, },
) )
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(SYMR.strings.action_copy_second_page),
icon = Icons.Outlined.ContentCopy,
onClick = {
onShare(true, true)
onDismissRequest()
},
)
ActionButton( ActionButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
title = stringResource(SYMR.strings.action_share_second_page), title = stringResource(SYMR.strings.action_share_second_page),
icon = Icons.Outlined.Share, icon = Icons.Outlined.Share,
onClick = { onClick = {
onShare(true) onShare(false, true)
onDismissRequest() onDismissRequest()
}, },
) )
@@ -138,12 +165,21 @@ fun ReaderPageActionsDialog(
Row( Row(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) { ) {
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(SYMR.strings.action_copy_combined_page),
icon = Icons.Outlined.ContentCopy,
onClick = {
onShareCombined(true)
onDismissRequest()
},
)
ActionButton( ActionButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
title = stringResource(SYMR.strings.action_share_combined_page), title = stringResource(SYMR.strings.action_share_combined_page),
icon = Icons.Outlined.Share, icon = Icons.Outlined.Share,
onClick = { onClick = {
onShareCombined() onShareCombined(false)
onDismissRequest() onDismissRequest()
}, },
) )
@@ -5,11 +5,14 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.SettingsChipRow import tachiyomi.presentation.core.components.SettingsChipRow
import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.collectAsState
@@ -20,9 +23,27 @@ private val themes = listOf(
MR.strings.automatic_background to 3, MR.strings.automatic_background to 3,
) )
private val flashColors = listOf(
MR.strings.pref_flash_style_black to ReaderPreferences.FlashColor.BLACK,
MR.strings.pref_flash_style_white to ReaderPreferences.FlashColor.WHITE,
MR.strings.pref_flash_style_white_black to ReaderPreferences.FlashColor.WHITE_BLACK,
)
@Composable @Composable
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
val readerTheme by screenModel.preferences.readerTheme().collectAsState() val readerTheme by screenModel.preferences.readerTheme().collectAsState()
val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState()
val flashMillisPref = screenModel.preferences.flashDurationMillis()
val flashMillis by flashMillisPref.collectAsState()
val flashIntervalPref = screenModel.preferences.flashPageInterval()
val flashInterval by flashIntervalPref.collectAsState()
val flashColorPref = screenModel.preferences.flashColor()
val flashColor by flashColorPref.collectAsState()
SettingsChipRow(MR.strings.pref_reader_theme) { SettingsChipRow(MR.strings.pref_reader_theme) {
themes.map { (labelRes, value) -> themes.map { (labelRes, value) ->
FilterChip( FilterChip(
@@ -95,6 +116,35 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
label = stringResource(MR.strings.pref_flash_page), label = stringResource(MR.strings.pref_flash_page),
pref = screenModel.preferences.flashOnPageChange(), pref = screenModel.preferences.flashOnPageChange(),
) )
if (flashPageState) {
SliderItem(
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
label = stringResource(MR.strings.pref_flash_duration),
valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
min = 1,
max = 15,
)
SliderItem(
value = flashInterval,
label = stringResource(MR.strings.pref_flash_page_interval),
valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
onChange = {
flashIntervalPref.set(it)
},
min = 1,
max = 10,
)
SettingsChipRow(MR.strings.pref_flash_with) {
flashColors.map { (labelRes, value) ->
FilterChip(
selected = flashColor == value,
onClick = { flashColorPref.set(value) },
label = { Text(stringResource(labelRes)) },
)
}
}
}
// SY --> // SY -->
CheckboxItem( CheckboxItem(
@@ -217,11 +217,6 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM
label = stringResource(MR.strings.pref_page_transitions), label = stringResource(MR.strings.pref_page_transitions),
pref = screenModel.preferences.pageTransitionsWebtoon(), pref = screenModel.preferences.pageTransitionsWebtoon(),
) )
CheckboxItem(
label = stringResource(SYMR.strings.enable_zoom_out),
pref = screenModel.preferences.webtoonEnableZoomOut(),
)
// SY <-- // SY <--
val dualPageSplitWebtoon by screenModel.preferences.dualPageSplitWebtoon().collectAsState() val dualPageSplitWebtoon by screenModel.preferences.dualPageSplitWebtoon().collectAsState()
@@ -254,6 +249,10 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM
label = stringResource(MR.strings.pref_double_tap_zoom), label = stringResource(MR.strings.pref_double_tap_zoom),
pref = screenModel.preferences.webtoonDoubleTapZoomEnabled(), pref = screenModel.preferences.webtoonDoubleTapZoomEnabled(),
) )
CheckboxItem(
label = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
pref = screenModel.preferences.webtoonDisableZoomOut(),
)
} }
// SY --> // SY -->
@@ -8,6 +8,7 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.presentation.theme.colorscheme.BaseColorScheme
import eu.kanade.presentation.theme.colorscheme.GreenAppleColorScheme import eu.kanade.presentation.theme.colorscheme.GreenAppleColorScheme
import eu.kanade.presentation.theme.colorscheme.LavenderColorScheme import eu.kanade.presentation.theme.colorscheme.LavenderColorScheme
import eu.kanade.presentation.theme.colorscheme.MidnightDuskColorScheme import eu.kanade.presentation.theme.colorscheme.MidnightDuskColorScheme
@@ -62,23 +63,27 @@ private fun getThemeColorScheme(
appTheme: AppTheme, appTheme: AppTheme,
isAmoled: Boolean, isAmoled: Boolean,
): ColorScheme { ): ColorScheme {
val colorScheme = when (appTheme) { val colorScheme = if (appTheme == AppTheme.MONET) {
AppTheme.DEFAULT -> TachiyomiColorScheme MonetColorScheme(LocalContext.current)
AppTheme.MONET -> MonetColorScheme(LocalContext.current) } else {
AppTheme.GREEN_APPLE -> GreenAppleColorScheme colorSchemes.getOrDefault(appTheme, TachiyomiColorScheme)
AppTheme.LAVENDER -> LavenderColorScheme
AppTheme.MIDNIGHT_DUSK -> MidnightDuskColorScheme
AppTheme.NORD -> NordColorScheme
AppTheme.STRAWBERRY_DAIQUIRI -> StrawberryColorScheme
AppTheme.TAKO -> TakoColorScheme
AppTheme.TEALTURQUOISE -> TealTurqoiseColorScheme
AppTheme.TIDAL_WAVE -> TidalWaveColorScheme
AppTheme.YINYANG -> YinYangColorScheme
AppTheme.YOTSUBA -> YotsubaColorScheme
else -> TachiyomiColorScheme
} }
return colorScheme.getColorScheme( return colorScheme.getColorScheme(
isSystemInDarkTheme(), isSystemInDarkTheme(),
isAmoled, isAmoled,
) )
} }
private val colorSchemes: Map<AppTheme, BaseColorScheme> = mapOf(
AppTheme.DEFAULT to TachiyomiColorScheme,
AppTheme.GREEN_APPLE to GreenAppleColorScheme,
AppTheme.LAVENDER to LavenderColorScheme,
AppTheme.MIDNIGHT_DUSK to MidnightDuskColorScheme,
AppTheme.NORD to NordColorScheme,
AppTheme.STRAWBERRY_DAIQUIRI to StrawberryColorScheme,
AppTheme.TAKO to TakoColorScheme,
AppTheme.TEALTURQUOISE to TealTurqoiseColorScheme,
AppTheme.TIDAL_WAVE to TidalWaveColorScheme,
AppTheme.YINYANG to YinYangColorScheme,
AppTheme.YOTSUBA to YotsubaColorScheme,
)
@@ -8,19 +8,28 @@ internal abstract class BaseColorScheme {
abstract val darkScheme: ColorScheme abstract val darkScheme: ColorScheme
abstract val lightScheme: ColorScheme abstract val lightScheme: ColorScheme
// Cannot be pure black as there's content scrolling behind it
// https://m3.material.io/components/navigation-bar/guidelines#90615a71-607e-485e-9e09-778bfc080563
private val surfaceContainer = Color(0xFF0C0C0C)
private val surfaceContainerHigh = Color(0xFF131313)
private val surfaceContainerHighest = Color(0xFF1B1B1B)
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme { fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
return (if (isDark) darkScheme else lightScheme) if (!isDark) return lightScheme
.let {
if (isDark && isAmoled) { if (!isAmoled) return darkScheme
it.copy(
background = Color.Black, return darkScheme.copy(
onBackground = Color.White, background = Color.Black,
surface = Color.Black, onBackground = Color.White,
onSurface = Color.White, surface = Color.Black,
) onSurface = Color.White,
} else { surfaceVariant = surfaceContainer, // Navigation bar background (ThemePrefWidget)
it surfaceContainerLowest = surfaceContainer,
} surfaceContainerLow = surfaceContainer,
} surfaceContainer = surfaceContainer, // Navigation bar background
surfaceContainerHigh = surfaceContainerHigh,
surfaceContainerHighest = surfaceContainerHighest,
)
} }
} }
@@ -19,53 +19,77 @@ internal object GreenAppleColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme( override val darkScheme = darkColorScheme(
primary = Color(0xFF7ADB8F), primary = Color(0xFF7ADB8F),
onPrimary = Color(0xFF003915), onPrimary = Color(0xFF003917),
primaryContainer = Color(0xFF005322), primaryContainer = Color(0xFF017737),
onPrimaryContainer = Color(0xFF96F8A9), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFF006D2F), secondary = Color(0xFF7ADB8F), // Unread badge
secondary = Color(0xFF7ADB8F), onSecondary = Color(0xFF003917), // Unread badge text
onSecondary = Color(0xFF003915), secondaryContainer = Color(0xFF017737), // Navigation bar selector pill & progress indicator (remaining)
secondaryContainer = Color(0xFF005322), onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selected icon
onSecondaryContainer = Color(0xFF96F8A9), tertiary = Color(0xFFFFB3AC), // Downloaded badge
tertiary = Color(0xFFFFB3AA), onTertiary = Color(0xFF680008), // Downloaded badge text
onTertiary = Color(0xFF680006), tertiaryContainer = Color(0xFFC7282A),
tertiaryContainer = Color(0xFF93000D), onTertiaryContainer = Color(0xFFFFFFFF),
onTertiaryContainer = Color(0xFFFFDAD5), error = Color(0xFFFFB4AB),
background = Color(0xFF1A1C19), onError = Color(0xFF690005),
onBackground = Color(0xFFE1E3DD), errorContainer = Color(0xFF93000A),
surface = Color(0xFF1A1C19), onErrorContainer = Color(0xFFFFDAD6),
onSurface = Color(0xFFE1E3DD), background = Color(0xFF0F1510),
surfaceVariant = Color(0xFF414941), onBackground = Color(0xFFDFE4DB),
onSurfaceVariant = Color(0xFFC1C8BE), surface = Color(0xFF0F1510),
surfaceTint = Color(0xFF7ADB8F), onSurface = Color(0xFFDFE4DB),
inverseSurface = Color(0xFFE1E3DD), surfaceVariant = Color(0xFF3F493F), // Navigation bar background (ThemePrefWidget)
inverseOnSurface = Color(0xFF1A1C19), onSurfaceVariant = Color(0xFFBECABC),
outline = Color(0xFF8B9389), outline = Color(0xFF889487),
outlineVariant = Color(0xFF3F493F),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFFDFE4DB),
inverseOnSurface = Color(0xFF2C322C),
inversePrimary = Color(0xFF006D32),
surfaceDim = Color(0xFF0F1510),
surfaceBright = Color(0xFF353B35),
surfaceContainerLowest = Color(0xFF0A0F0B),
surfaceContainerLow = Color(0xFF181D18),
surfaceContainer = Color(0xFF1C211C), // Navigation bar background
surfaceContainerHigh = Color(0xFF262B26),
surfaceContainerHighest = Color(0xFF313630),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
primary = Color(0xFF006D2F), primary = Color(0xFF005927),
onPrimary = Color(0xFFFFFFFF), onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFF96F8A9), primaryContainer = Color(0xFF188140),
onPrimaryContainer = Color(0xFF002109), onPrimaryContainer = Color(0xFFFFFFFF),
secondary = Color(0xFF005927), // Unread badge
onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFF97f7a9), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF000000), // Navigation bar selected icon
tertiary = Color(0xFF9D0012), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFFD33131),
onTertiaryContainer = Color(0xFFFFFFFF),
error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
background = Color(0xFFF6FBF2),
onBackground = Color(0xFF181D18),
surface = Color(0xFFF6FBF2),
onSurface = Color(0xFF181D18),
surfaceVariant = Color(0xFFDAE6D7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF3F493F),
outline = Color(0xFF6F7A6E),
outlineVariant = Color(0xFFBECABC),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFF2C322C),
inverseOnSurface = Color(0xFFEDF2E9),
inversePrimary = Color(0xFF7ADB8F), inversePrimary = Color(0xFF7ADB8F),
secondary = Color(0xFF006D2F), surfaceDim = Color(0xFFD6DCD3),
onSecondary = Color(0xFFFFFFFF), surfaceBright = Color(0xFFF6FBF2),
secondaryContainer = Color(0xFF96F8A9), surfaceContainerLowest = Color(0xFFFFFFFF),
onSecondaryContainer = Color(0xFF002109), surfaceContainerLow = Color(0xFFF0F5EC),
tertiary = Color(0xFFB91D22), surfaceContainer = Color(0xFFEAEFE6), // Navigation bar background
onTertiary = Color(0xFFFFFFFF), surfaceContainerHigh = Color(0xFFE4EAE1),
tertiaryContainer = Color(0xFFFFDAD5), surfaceContainerHighest = Color(0xFFDFE4DB),
onTertiaryContainer = Color(0xFF410003),
background = Color(0xFFFBFDF7),
onBackground = Color(0xFF1A1C19),
surface = Color(0xFFFBFDF7),
onSurface = Color(0xFF1A1C19),
surfaceVariant = Color(0xFFDDE5DA),
onSurfaceVariant = Color(0xFF414941),
surfaceTint = Color(0xFF006D2F),
inverseSurface = Color(0xFF2F312E),
inverseOnSurface = Color(0xFFF0F2EC),
outline = Color(0xFF717970),
) )
} }
@@ -18,53 +18,77 @@ internal object LavenderColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme( override val darkScheme = darkColorScheme(
primary = Color(0xFFA177FF), primary = Color(0xFFA177FF),
onPrimary = Color(0xFF111129), onPrimary = Color(0xFF3D0090),
primaryContainer = Color(0xFFA177FF), primaryContainer = Color(0xFFA177FF),
onPrimaryContainer = Color(0xFF111129), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFF006D2F), secondary = Color(0xFFA177FF), // Unread badge
secondary = Color(0xFFA177FF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
onSecondary = Color(0xFF111129), secondaryContainer = Color(0xFF423271), // Navigation bar selector pill & progress indicator (remaining)
secondaryContainer = Color(0xFFA177FF), onSecondaryContainer = Color(0xFFA177FF), // Navigation bar selected icon
onSecondaryContainer = Color(0xFF111129), tertiary = Color(0xFFCDBDFF), // Downloaded badge
tertiary = Color(0xFF5E25E1), onTertiary = Color(0xFF360096), // Downloaded badge text
onTertiary = Color(0xFFE8E8E8), tertiaryContainer = Color(0xFF5512D8),
tertiaryContainer = Color(0xFF111129), onTertiaryContainer = Color(0xFFEFE6FF),
onTertiaryContainer = Color(0xFFDEE8FF), error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF111129), background = Color(0xFF111129),
onBackground = Color(0xFFDEE8FF), onBackground = Color(0xFFE7E0EC),
surface = Color(0xFF111129), surface = Color(0xFF111129),
onSurface = Color(0xFFDEE8FF), onSurface = Color(0xFFE7E0EC),
surfaceVariant = Color(0x2CB6B6B6), surfaceVariant = Color(0xFF3D2F6B), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFE8E8E8), onSurfaceVariant = Color(0xFFCBC3D6),
surfaceTint = Color(0xFFA177FF), outline = Color(0xFF958E9F),
inverseSurface = Color(0xFF221247), outlineVariant = Color(0xFF4A4453),
inverseOnSurface = Color(0xFFDEE8FF), scrim = Color(0xFF000000),
outline = Color(0xA8905FFF), inverseSurface = Color(0xFFE7E0EC),
inverseOnSurface = Color(0xFF322F38),
inversePrimary = Color(0xFF6D41C8),
surfaceDim = Color(0xFF111129),
surfaceBright = Color(0xFF3B3841),
surfaceContainerLowest = Color(0xFF15132d),
surfaceContainerLow = Color(0xFF171531),
surfaceContainer = Color(0xFF1D193B), // Navigation bar background
surfaceContainerHigh = Color(0xFF241f41),
surfaceContainerHighest = Color(0xFF282446),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
primary = Color(0xFF7B46AF), primary = Color(0xFF6D41C8),
onPrimary = Color(0xFFEDE2FF), onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFF7B46AF), primaryContainer = Color(0xFF7B46AF),
onPrimaryContainer = Color(0xFFEDE2FF), onPrimaryContainer = Color(0xFF130038),
inversePrimary = Color(0xFFD6BAFF), secondary = Color(0xFF7B46AF), // Unread badge
secondary = Color(0xFF7B46AF), onSecondary = Color(0xFFEDE2FF), // Unread badge text
onSecondary = Color(0xFFEDE2FF), secondaryContainer = Color(0xFFC9B0E6), // Navigation bar selector pill & progress indicator (remaining)
secondaryContainer = Color(0xFF7B46AF), onSecondaryContainer = Color(0xFF7B46AF), // Navigation bar selector icon
onSecondaryContainer = Color(0xFFEDE2FF), tertiary = Color(0xFFEDE2FF), // Downloaded badge
tertiary = Color(0xFFEDE2FF), onTertiary = Color(0xFF7B46AF), // Downloaded badge text
onTertiary = Color(0xFF7B46AF), tertiaryContainer = Color(0xFF6D3BF0),
tertiaryContainer = Color(0xFFEDE2FF), onTertiaryContainer = Color(0xFFFFFFFF),
onTertiaryContainer = Color(0xFF7B46AF), error = Color(0xFFBA1A1A),
onError = Color(0xFFFFFFFF),
errorContainer = Color(0xFFFFDAD6),
onErrorContainer = Color(0xFF410002),
background = Color(0xFFEDE2FF), background = Color(0xFFEDE2FF),
onBackground = Color(0xFF1B1B22), onBackground = Color(0xFF1D1A22),
surface = Color(0xFFEDE2FF), surface = Color(0xFFEDE2FF),
onSurface = Color(0xFF1B1B22), onSurface = Color(0xFF1D1A22),
surfaceVariant = Color(0xFFB9B0CC), surfaceVariant = Color(0xFFE4D5F8), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xD849454E), onSurfaceVariant = Color(0xFF4A4453),
surfaceTint = Color(0xFF7B46AF), outline = Color(0xFF7B7485),
inverseSurface = Color(0xFF313033), outlineVariant = Color(0xFFCBC3D6),
inverseOnSurface = Color(0xFFF3EFF4), scrim = Color(0xFF000000),
outline = Color(0xFF7B46AF), inverseSurface = Color(0xFF322F38),
inverseOnSurface = Color(0xFFF5EEFA),
inversePrimary = Color(0xFFA177FF),
surfaceDim = Color(0xFFDED7E3),
surfaceBright = Color(0xFFEDE2FF),
surfaceContainerLowest = Color(0xFFDACCEC),
surfaceContainerLow = Color(0xFFDED0F1),
surfaceContainer = Color(0xFFE4D5F8), // Navigation bar background
surfaceContainerHigh = Color(0xFFEADCFD),
surfaceContainerHighest = Color(0xFFEEE2FF),
) )
} }
@@ -23,24 +23,29 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFBD1C5C), primaryContainer = Color(0xFFBD1C5C),
onPrimaryContainer = Color(0xFFFFFFFF), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFF02475), inversePrimary = Color(0xFFF02475),
secondary = Color(0xFFF02475), secondary = Color(0xFFF02475), // Unread badge
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFF16151D), // Unread badge text
secondaryContainer = Color(0xFFF02475), secondaryContainer = Color(0xFF66183C), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFFFFFFF), onSecondaryContainer = Color(0xFFF02475), // Navigation bar selector icon
tertiary = Color(0xFF55971C), tertiary = Color(0xFF55971C), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFF16151D), // Downloaded badge text
tertiaryContainer = Color(0xFF386412), tertiaryContainer = Color(0xFF386412),
onTertiaryContainer = Color(0xFFE5E1E5), onTertiaryContainer = Color(0xFFE5E1E5),
background = Color(0xFF16151D), background = Color(0xFF16151D),
onBackground = Color(0xFFE5E1E5), onBackground = Color(0xFFE5E1E5),
surface = Color(0xFF16151D), surface = Color(0xFF16151D),
onSurface = Color(0xFFE5E1E5), onSurface = Color(0xFFE5E1E5),
surfaceVariant = Color(0xFF524346), surfaceVariant = Color(0xFF281624), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD6C1C4), onSurfaceVariant = Color(0xFFD6C1C4),
surfaceTint = Color(0xFFF02475), surfaceTint = Color(0xFFF02475),
inverseSurface = Color(0xFF333043), inverseSurface = Color(0xFF333043),
inverseOnSurface = Color(0xFFFFFFFF), inverseOnSurface = Color(0xFFFFFFFF),
outline = Color(0xFF9F8C8F), outline = Color(0xFF9F8C8F),
surfaceContainerLowest = Color(0xFF221320),
surfaceContainerLow = Color(0xFF251522),
surfaceContainer = Color(0xFF281624), // Navigation bar background
surfaceContainerHigh = Color(0xFF2D1C2A),
surfaceContainerHighest = Color(0xFF2F1F2C),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object MidnightDuskColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFD9E1), primaryContainer = Color(0xFFFFD9E1),
onPrimaryContainer = Color(0xFF3F0017), onPrimaryContainer = Color(0xFF3F0017),
inversePrimary = Color(0xFFFFB1C4), inversePrimary = Color(0xFFFFB1C4),
secondary = Color(0xFFBB0054), secondary = Color(0xFFBB0054), // Unread badge
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFFFD9E1), secondaryContainer = Color(0xFFEFBAD4), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF3F0017), onSecondaryContainer = Color(0xFFD1377C), // Navigation bar selector icon
tertiary = Color(0xFF006638), tertiary = Color(0xFF006638), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00894b), tertiaryContainer = Color(0xFF00894b),
onTertiaryContainer = Color(0xFF2D1600), onTertiaryContainer = Color(0xFF2D1600),
background = Color(0xFFFFFBFF), background = Color(0xFFFFFBFF),
onBackground = Color(0xFF1C1B1F), onBackground = Color(0xFF1C1B1F),
surface = Color(0xFFFFFBFF), surface = Color(0xFFFFFBFF),
onSurface = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F),
surfaceVariant = Color(0xFFF3DDE0), surfaceVariant = Color(0xFFF9E6F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF524346), onSurfaceVariant = Color(0xFF524346),
surfaceTint = Color(0xFFBB0054), surfaceTint = Color(0xFFBB0054),
inverseSurface = Color(0xFF313033), inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF4F0F4), inverseOnSurface = Color(0xFFF4F0F4),
outline = Color(0xFF847376), outline = Color(0xFF847376),
surfaceContainerLowest = Color(0xFFDAC0CD),
surfaceContainerLow = Color(0xFFE8D1DD),
surfaceContainer = Color(0xFFF9E6F1), // Navigation bar background
surfaceContainerHigh = Color(0xFFFCF3F8),
surfaceContainerHighest = Color(0xFFFEF9FC),
) )
} }
@@ -17,19 +17,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF88C0D0), primaryContainer = Color(0xFF88C0D0),
onPrimaryContainer = Color(0xFF2E3440), onPrimaryContainer = Color(0xFF2E3440),
inversePrimary = Color(0xFF397E91), inversePrimary = Color(0xFF397E91),
secondary = Color(0xFF81A1C1), secondary = Color(0xFF81A1C1), // Unread badge
onSecondary = Color(0xFF2E3440), onSecondary = Color(0xFF2E3440), // Unread badge text
secondaryContainer = Color(0xFF81A1C1), secondaryContainer = Color(0xFF506275), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF2E3440), onSecondaryContainer = Color(0xFF88C0D0), // Navigation bar selector icon
tertiary = Color(0xFF5E81AC), tertiary = Color(0xFF5E81AC), // Downloaded badge
onTertiary = Color(0xFF000000), onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF5E81AC), tertiaryContainer = Color(0xFF5E81AC),
onTertiaryContainer = Color(0xFF000000), onTertiaryContainer = Color(0xFF000000),
background = Color(0xFF2E3440), background = Color(0xFF2E3440),
onBackground = Color(0xFFECEFF4), onBackground = Color(0xFFECEFF4),
surface = Color(0xFF3B4252), surface = Color(0xFF2E3440),
onSurface = Color(0xFFECEFF4), onSurface = Color(0xFFECEFF4),
surfaceVariant = Color(0xFF2E3440), surfaceVariant = Color(0xFF414C5C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFECEFF4), onSurfaceVariant = Color(0xFFECEFF4),
surfaceTint = Color(0xFF88C0D0), surfaceTint = Color(0xFF88C0D0),
inverseSurface = Color(0xFFD8DEE9), inverseSurface = Color(0xFFD8DEE9),
@@ -39,6 +39,11 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFF2E3440), onError = Color(0xFF2E3440),
errorContainer = Color(0xFFBF616A), errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000), onErrorContainer = Color(0xFF000000),
surfaceContainerLowest = Color(0xFF373F4D),
surfaceContainerLow = Color(0xFF3E4756),
surfaceContainer = Color(0xFF414C5C),
surfaceContainerHigh = Color(0xFF4E5766),
surfaceContainerHighest = Color(0xFF505968), // Navigation bar background
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -47,19 +52,19 @@ internal object NordColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF5E81AC), primaryContainer = Color(0xFF5E81AC),
onPrimaryContainer = Color(0xFF000000), onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF8CA8CD), inversePrimary = Color(0xFF8CA8CD),
secondary = Color(0xFF81A1C1), secondary = Color(0xFF81A1C1), // Unread badge
onSecondary = Color(0xFF2E3440), onSecondary = Color(0xFF2E3440), // Unread badge text
secondaryContainer = Color(0xFF81A1C1), secondaryContainer = Color(0xFF91B4D7), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF2E3440), onSecondaryContainer = Color(0xFF2E3440), // Navigation bar selector icon
tertiary = Color(0xFF88C0D0), tertiary = Color(0xFF88C0D0), // Downloaded badge
onTertiary = Color(0xFF2E3440), onTertiary = Color(0xFF2E3440), // Downloaded badge text
tertiaryContainer = Color(0xFF88C0D0), tertiaryContainer = Color(0xFF88C0D0),
onTertiaryContainer = Color(0xFF2E3440), onTertiaryContainer = Color(0xFF2E3440),
background = Color(0xFFECEFF4), background = Color(0xFFECEFF4),
onBackground = Color(0xFF2E3440), onBackground = Color(0xFF2E3440),
surface = Color(0xFFE5E9F0), surface = Color(0xFFE5E9F0),
onSurface = Color(0xFF2E3440), onSurface = Color(0xFF2E3440),
surfaceVariant = Color(0xFFffffff), surfaceVariant = Color(0xFFDAE0EA), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF2E3440), onSurfaceVariant = Color(0xFF2E3440),
surfaceTint = Color(0xFF5E81AC), surfaceTint = Color(0xFF5E81AC),
inverseSurface = Color(0xFF3B4252), inverseSurface = Color(0xFF3B4252),
@@ -68,5 +73,10 @@ internal object NordColorScheme : BaseColorScheme() {
onError = Color(0xFFECEFF4), onError = Color(0xFFECEFF4),
errorContainer = Color(0xFFBF616A), errorContainer = Color(0xFFBF616A),
onErrorContainer = Color(0xFF000000), onErrorContainer = Color(0xFF000000),
surfaceContainerLowest = Color(0xFFD1D7E0),
surfaceContainerLow = Color(0xFFD6DCE6),
surfaceContainer = Color(0xFFDAE0EA), // Navigation bar background
surfaceContainerHigh = Color(0xFFE9EDF3),
surfaceContainerHighest = Color(0xFFF2F4F8),
) )
} }
@@ -18,54 +18,78 @@ import androidx.compose.ui.graphics.Color
internal object StrawberryColorScheme : BaseColorScheme() { internal object StrawberryColorScheme : BaseColorScheme() {
override val darkScheme = darkColorScheme( override val darkScheme = darkColorScheme(
primary = Color(0xFFFFB2B9), primary = Color(0xFFFFB2B8),
onPrimary = Color(0xFF67001B), onPrimary = Color(0xFF67001D),
primaryContainer = Color(0xFF91002A), primaryContainer = Color(0xFFD53855),
onPrimaryContainer = Color(0xFFFFDADD), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFB61E40), secondary = Color(0xFFED4A65), // Unread badge
secondary = Color(0xFFFFB2B9), onSecondary = Color(0xFF201A1A), // Unread badge text
onSecondary = Color(0xFF67001B), secondaryContainer = Color(0xFF91002A), // Navigation bar selector pill & progress indicator (remaining)
secondaryContainer = Color(0xFF91002A), onSecondaryContainer = Color(0xFFFFFFFF), // Navigation bar selector icon
onSecondaryContainer = Color(0xFFFFDADD), tertiary = Color(0xFFE8C08E), // Downloaded badge
tertiary = Color(0xFFE8C08E), onTertiary = Color(0xFF201A1A), // Downloaded badge text
onTertiary = Color(0xFF432C06), tertiaryContainer = Color(0xFF775930),
tertiaryContainer = Color(0xFF5D421B), onTertiaryContainer = Color(0xFFFFF7F1),
onTertiaryContainer = Color(0xFFFFDDB1), error = Color(0xFFFFB4AB),
onError = Color(0xFF690005),
errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF201A1A), background = Color(0xFF201A1A),
onBackground = Color(0xFFECDFDF), onBackground = Color(0xFFF7DCDD),
surface = Color(0xFF201A1A), surface = Color(0xFF201A1A),
onSurface = Color(0xFFECDFDF), onSurface = Color(0xFFF7DCDD),
surfaceVariant = Color(0xFF534344), surfaceVariant = Color(0xFF322727), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD7C1C2), onSurfaceVariant = Color(0xFFE1BEC0),
surfaceTint = Color(0xFFFFB2B9), outline = Color(0xFFA9898B),
inverseSurface = Color(0xFFECDFDF), outlineVariant = Color(0xFF594042),
inverseOnSurface = Color(0xFF201A1A), scrim = Color(0xFF000000),
outline = Color(0xFFA08C8D), inverseSurface = Color(0xFFF7DCDD),
inverseOnSurface = Color(0xFF3D2C2D),
inversePrimary = Color(0xFFB61F40),
surfaceDim = Color(0xFF1D1011),
surfaceBright = Color(0xFF463536),
surfaceContainerLowest = Color(0xFF2C2222),
surfaceContainerLow = Color(0xFF302525),
surfaceContainer = Color(0xFF322727), // Navigation bar background
surfaceContainerHigh = Color(0xFF3C2F2F),
surfaceContainerHighest = Color(0xFF463737),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
primary = Color(0xFFB61E40), primary = Color(0xFFA10833),
onPrimary = Color(0xFFFFFFFF), onPrimary = Color(0xFFFFFFFF),
primaryContainer = Color(0xFFFFDADD), primaryContainer = Color(0xFFD53855),
onPrimaryContainer = Color(0xFF40000D), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFFFB2B9), secondary = Color(0xFFA10833), // Unread badge
secondary = Color(0xFFB61E40), onSecondary = Color(0xFFFFFFFF), // Unread badge text
onSecondary = Color(0xFFFFFFFF), secondaryContainer = Color(0xFFD53855), // Navigation bar selector pill & progress indicator (remaining)
secondaryContainer = Color(0xFFFFDADD), onSecondaryContainer = Color(0xFFF6EAED), // Navigation bar selector icon
onSecondaryContainer = Color(0xFF40000D), tertiary = Color(0xFF5F441D), // Downloaded badge
tertiary = Color(0xFF775930), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
onTertiary = Color(0xFFFFFFFF), tertiaryContainer = Color(0xFF87683D),
tertiaryContainer = Color(0xFFFFDDB1), onTertiaryContainer = Color(0xFFFFFFFF),
onTertiaryContainer = Color(0xFF2A1800), error = Color(0xFFBA1A1A),
background = Color(0xFFFCFCFC), onError = Color(0xFFFFFFFF),
onBackground = Color(0xFF201A1A), errorContainer = Color(0xFFFFDAD6),
surface = Color(0xFFFCFCFC), onErrorContainer = Color(0xFF410002),
onSurface = Color(0xFF201A1A), background = Color(0xFFFAFAFA),
surfaceVariant = Color(0xFFF4DDDD), onBackground = Color(0xFF261819),
onSurfaceVariant = Color(0xFF534344), surface = Color(0xFFFAFAFA),
surfaceTint = Color(0xFFB61E40), onSurface = Color(0xFF261819),
inverseSurface = Color(0xFF362F2F), surfaceVariant = Color(0xFFF6EAED), // Navigation bar background (ThemePrefWidget)
inverseOnSurface = Color(0xFFFBEDED), onSurfaceVariant = Color(0xFF594042),
outline = Color(0xFF857374), outline = Color(0xFF8D7071),
outlineVariant = Color(0xFFE1BEC0),
scrim = Color(0xFF000000),
inverseSurface = Color(0xFF3D2C2D),
inverseOnSurface = Color(0xFFFFECED),
inversePrimary = Color(0xFFFFB2B8),
surfaceDim = Color(0xFFEED4D5),
surfaceBright = Color(0xFFFFF8F7),
surfaceContainerLowest = Color(0xFFF7DCDD),
surfaceContainerLow = Color(0xFFFDE2E3),
surfaceContainer = Color(0xFFF6EAED), // Navigation bar background
surfaceContainerHigh = Color(0xFFFFF0F0),
surfaceContainerHighest = Color(0xFFFFFFFF),
) )
} }
@@ -22,19 +22,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF00429B), primaryContainer = Color(0xFF00429B),
onPrimaryContainer = Color(0xFFD9E2FF), onPrimaryContainer = Color(0xFFD9E2FF),
inversePrimary = Color(0xFF0058CA), inversePrimary = Color(0xFF0058CA),
secondary = Color(0xFFB0C6FF), secondary = Color(0xFFB0C6FF), // Unread badge
onSecondary = Color(0xFF002D6E), onSecondary = Color(0xFF002D6E), // Unread badge text
secondaryContainer = Color(0xFF00429B), secondaryContainer = Color(0xFF00429B), // Navigation bar selector pill & pro
onSecondaryContainer = Color(0xFFD9E2FF), onSecondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector icon
tertiary = Color(0xFF7ADC77), tertiary = Color(0xFF7ADC77), // Downloaded badge
onTertiary = Color(0xFF003909), onTertiary = Color(0xFF003909), // Downloaded badge text
tertiaryContainer = Color(0xFF005312), tertiaryContainer = Color(0xFF005312),
onTertiaryContainer = Color(0xFF95F990), onTertiaryContainer = Color(0xFF95F990),
background = Color(0xFF1B1B1F), background = Color(0xFF1B1B1F),
onBackground = Color(0xFFE3E2E6), onBackground = Color(0xFFE3E2E6),
surface = Color(0xFF1B1B1F), surface = Color(0xFF1B1B1F),
onSurface = Color(0xFFE3E2E6), onSurface = Color(0xFFE3E2E6),
surfaceVariant = Color(0xFF44464F), surfaceVariant = Color(0xFF211F26), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFC5C6D0), onSurfaceVariant = Color(0xFFC5C6D0),
surfaceTint = Color(0xFFB0C6FF), surfaceTint = Color(0xFFB0C6FF),
inverseSurface = Color(0xFFE3E2E6), inverseSurface = Color(0xFFE3E2E6),
@@ -45,6 +45,11 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFFFFDAD6), onErrorContainer = Color(0xFFFFDAD6),
outline = Color(0xFF8F9099), outline = Color(0xFF8F9099),
outlineVariant = Color(0xFF44464F), outlineVariant = Color(0xFF44464F),
surfaceContainerLowest = Color(0xFF1A181D),
surfaceContainerLow = Color(0xFF1E1C22),
surfaceContainer = Color(0xFF211F26), // Navigation bar background
surfaceContainerHigh = Color(0xFF292730),
surfaceContainerHighest = Color(0xFF302E38),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -53,19 +58,19 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFD9E2FF), primaryContainer = Color(0xFFD9E2FF),
onPrimaryContainer = Color(0xFF001945), onPrimaryContainer = Color(0xFF001945),
inversePrimary = Color(0xFFB0C6FF), inversePrimary = Color(0xFFB0C6FF),
secondary = Color(0xFF0058CA), secondary = Color(0xFF0058CA), // Unread badge
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFD9E2FF), secondaryContainer = Color(0xFFD9E2FF), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF001945), onSecondaryContainer = Color(0xFF001945), // Navigation bar selector icon
tertiary = Color(0xFF006E1B), tertiary = Color(0xFF006E1B), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF95F990), tertiaryContainer = Color(0xFF95F990),
onTertiaryContainer = Color(0xFF002203), onTertiaryContainer = Color(0xFF002203),
background = Color(0xFFFEFBFF), background = Color(0xFFFEFBFF),
onBackground = Color(0xFF1B1B1F), onBackground = Color(0xFF1B1B1F),
surface = Color(0xFFFEFBFF), surface = Color(0xFFFEFBFF),
onSurface = Color(0xFF1B1B1F), onSurface = Color(0xFF1B1B1F),
surfaceVariant = Color(0xFFE1E2EC), surfaceVariant = Color(0xFFF3EDF7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF44464F), onSurfaceVariant = Color(0xFF44464F),
surfaceTint = Color(0xFF0058CA), surfaceTint = Color(0xFF0058CA),
inverseSurface = Color(0xFF303034), inverseSurface = Color(0xFF303034),
@@ -76,5 +81,10 @@ internal object TachiyomiColorScheme : BaseColorScheme() {
onErrorContainer = Color(0xFF410002), onErrorContainer = Color(0xFF410002),
outline = Color(0xFF757780), outline = Color(0xFF757780),
outlineVariant = Color(0xFFC5C6D0), outlineVariant = Color(0xFFC5C6D0),
surfaceContainerLowest = Color(0xFFF5F1F8),
surfaceContainerLow = Color(0xFFF7F2FA),
surfaceContainer = Color(0xFFF3EDF7), // Navigation bar background
surfaceContainerHigh = Color(0xFFFCF7FF),
surfaceContainerHighest = Color(0xFFFCF7FF),
) )
} }
@@ -23,24 +23,29 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFF3B375), primaryContainer = Color(0xFFF3B375),
onPrimaryContainer = Color(0xFF38294E), onPrimaryContainer = Color(0xFF38294E),
inversePrimary = Color(0xFF84531E), inversePrimary = Color(0xFF84531E),
secondary = Color(0xFFF3B375), secondary = Color(0xFFF3B375), // Unread badge
onSecondary = Color(0xFF38294E), onSecondary = Color(0xFF38294E), // Unread badge text
secondaryContainer = Color(0xFFF3B375), secondaryContainer = Color(0xFF5C4D4B), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF38294E), onSecondaryContainer = Color(0xFFF3B375), // Navigation bar selector icon
tertiary = Color(0xFF66577E), tertiary = Color(0xFF66577E), // Downloaded badge
onTertiary = Color(0xFFF3B375), onTertiary = Color(0xFFF3B375), // Downloaded badge text
tertiaryContainer = Color(0xFF4E4065), tertiaryContainer = Color(0xFF4E4065),
onTertiaryContainer = Color(0xFFEDDCFF), onTertiaryContainer = Color(0xFFEDDCFF),
background = Color(0xFF21212E), background = Color(0xFF21212E),
onBackground = Color(0xFFE3E0F2), onBackground = Color(0xFFE3E0F2),
surface = Color(0xFF21212E), surface = Color(0xFF21212E),
onSurface = Color(0xFFE3E0F2), onSurface = Color(0xFFE3E0F2),
surfaceVariant = Color(0xFF49454E), surfaceVariant = Color(0xFF2A2A3C), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFCBC4CE), onSurfaceVariant = Color(0xFFCBC4CE),
surfaceTint = Color(0xFF66577E), surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFFE5E1E6), inverseSurface = Color(0xFFE5E1E6),
inverseOnSurface = Color(0xFF1B1B1E), inverseOnSurface = Color(0xFF1B1B1E),
outline = Color(0xFF958F99), outline = Color(0xFF958F99),
surfaceContainerLowest = Color(0xFF20202E),
surfaceContainerLow = Color(0xFF262636),
surfaceContainer = Color(0xFF2A2A3C), // Navigation bar background
surfaceContainerHigh = Color(0xFF303044),
surfaceContainerHighest = Color(0xFF36364D),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object TakoColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF66577E), primaryContainer = Color(0xFF66577E),
onPrimaryContainer = Color(0xFFF3B375), onPrimaryContainer = Color(0xFFF3B375),
inversePrimary = Color(0xFFD6BAFF), inversePrimary = Color(0xFFD6BAFF),
secondary = Color(0xFF66577E), secondary = Color(0xFF66577E), // Unread badge
onSecondary = Color(0xFFF3B375), onSecondary = Color(0xFFF3B375), // Unread badge text
secondaryContainer = Color(0xFF66577E), secondaryContainer = Color(0xFFC8BED0), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFF3B375), onSecondaryContainer = Color(0xFF66577E), // Navigation bar selector icon
tertiary = Color(0xFFF3B375), tertiary = Color(0xFFF3B375), // Downloaded badge
onTertiary = Color(0xFF574360), onTertiary = Color(0xFF574360), // Downloaded badge text
tertiaryContainer = Color(0xFFFDD6B0), tertiaryContainer = Color(0xFFFDD6B0),
onTertiaryContainer = Color(0xFF221437), onTertiaryContainer = Color(0xFF221437),
background = Color(0xFFF7F5FF), background = Color(0xFFF7F5FF),
onBackground = Color(0xFF1B1B22), onBackground = Color(0xFF1B1B22),
surface = Color(0xFFF7F5FF), surface = Color(0xFFF7F5FF),
onSurface = Color(0xFF1B1B22), onSurface = Color(0xFF1B1B22),
surfaceVariant = Color(0xFFE8E0EB), surfaceVariant = Color(0xFFE8E0EB), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF49454E), onSurfaceVariant = Color(0xFF49454E),
surfaceTint = Color(0xFF66577E), surfaceTint = Color(0xFF66577E),
inverseSurface = Color(0xFF313033), inverseSurface = Color(0xFF313033),
inverseOnSurface = Color(0xFFF3EFF4), inverseOnSurface = Color(0xFFF3EFF4),
outline = Color(0xFF7A757E), outline = Color(0xFF7A757E),
surfaceContainerLowest = Color(0xFFD7D0DA),
surfaceContainerLow = Color(0xFFDFD8E2),
surfaceContainer = Color(0xFFE8E0EB), // Navigation bar background
surfaceContainerHigh = Color(0xFFEEE6F1),
surfaceContainerHighest = Color(0xFFF7EEFA),
) )
} }
@@ -15,24 +15,29 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF40E0D0), primaryContainer = Color(0xFF40E0D0),
onPrimaryContainer = Color(0xFF000000), onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFF008080), inversePrimary = Color(0xFF008080),
secondary = Color(0xFF40E0D0), secondary = Color(0xFF40E0D0), // Unread badge
onSecondary = Color(0xFF000000), onSecondary = Color(0xFF000000), // Unread badge text
secondaryContainer = Color(0xFF18544E), secondaryContainer = Color(0xFF18544E), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF40E0D0), onSecondaryContainer = Color(0xFF40E0D0), // Navigation bar selector icon
tertiary = Color(0xFFBF1F2F), tertiary = Color(0xFFBF1F2F), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF200508), tertiaryContainer = Color(0xFF200508),
onTertiaryContainer = Color(0xFFBF1F2F), onTertiaryContainer = Color(0xFFBF1F2F),
background = Color(0xFF202125), background = Color(0xFF202125),
onBackground = Color(0xFFDFDEDA), onBackground = Color(0xFFDFDEDA),
surface = Color(0xFF202125), surface = Color(0xFF202125),
onSurface = Color(0xFFDFDEDA), onSurface = Color(0xFFDFDEDA),
surfaceVariant = Color(0xFF3F4947), surfaceVariant = Color(0xFF233133), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFDFDEDA), onSurfaceVariant = Color(0xFFDFDEDA),
surfaceTint = Color(0xFF40E0D0), surfaceTint = Color(0xFF40E0D0),
inverseSurface = Color(0xFFDFDEDA), inverseSurface = Color(0xFFDFDEDA),
inverseOnSurface = Color(0xFF202125), inverseOnSurface = Color(0xFF202125),
outline = Color(0xFF899391), outline = Color(0xFF899391),
surfaceContainerLowest = Color(0xFF202C2E),
surfaceContainerLow = Color(0xFF222F31),
surfaceContainer = Color(0xFF233133), // Navigation bar background
surfaceContainerHigh = Color(0xFF28383A),
surfaceContainerHighest = Color(0xFF2F4244),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -41,23 +46,28 @@ internal object TealTurqoiseColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF008080), primaryContainer = Color(0xFF008080),
onPrimaryContainer = Color(0xFFFFFFFF), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFF40E0D0), inversePrimary = Color(0xFF40E0D0),
secondary = Color(0xFF008080), secondary = Color(0xFF008080), // Unread badge text
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFBFDFDF), secondaryContainer = Color(0xFFCFE5E4), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF008080), onSecondaryContainer = Color(0xFF008080), // Navigation bar selector icon
tertiary = Color(0xFFFF7F7F), tertiary = Color(0xFFFF7F7F), // Downloaded badge
onTertiary = Color(0xFF000000), onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFF2A1616), tertiaryContainer = Color(0xFF2A1616),
onTertiaryContainer = Color(0xFFFF7F7F), onTertiaryContainer = Color(0xFFFF7F7F),
background = Color(0xFFFAFAFA), background = Color(0xFFFAFAFA),
onBackground = Color(0xFF050505), onBackground = Color(0xFF050505),
surface = Color(0xFFFAFAFA), surface = Color(0xFFFAFAFA),
onSurface = Color(0xFF050505), onSurface = Color(0xFF050505),
surfaceVariant = Color(0xFFDAE5E2), surfaceVariant = Color(0xFFEBF3F1), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF050505), onSurfaceVariant = Color(0xFF050505),
surfaceTint = Color(0xFFBFDFDF), surfaceTint = Color(0xFFBFDFDF),
inverseSurface = Color(0xFF050505), inverseSurface = Color(0xFF050505),
inverseOnSurface = Color(0xFFFAFAFA), inverseOnSurface = Color(0xFFFAFAFA),
outline = Color(0xFF6F7977), outline = Color(0xFF6F7977),
surfaceContainerLowest = Color(0xFFE1E9E7),
surfaceContainerLow = Color(0xFFE6EEEC),
surfaceContainer = Color(0xFFEBF3F1), // Navigation bar background
surfaceContainerHigh = Color(0xFFF0F8F6),
surfaceContainerHighest = Color(0xFFF7FFFD),
) )
} }
@@ -22,24 +22,29 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF004d61), primaryContainer = Color(0xFF004d61),
onPrimaryContainer = Color(0xFFb8eaff), onPrimaryContainer = Color(0xFFb8eaff),
inversePrimary = Color(0xFFa12b03), inversePrimary = Color(0xFFa12b03),
secondary = Color(0xFF5ed4fc), secondary = Color(0xFF5ed4fc), // Unread badge
onSecondary = Color(0xFF003544), onSecondary = Color(0xFF003544), // Unread badge text
secondaryContainer = Color(0xFF004d61), secondaryContainer = Color(0xFF004d61), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFb8eaff), onSecondaryContainer = Color(0xFFb8eaff), // Navigation bar selector icon
tertiary = Color(0xFF92f7bc), tertiary = Color(0xFF92f7bc), // Downloaded badge
onTertiary = Color(0xFF001c3b), onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada), tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6), onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFF001c3b), background = Color(0xFF001c3b),
onBackground = Color(0xFFd5e3ff), onBackground = Color(0xFFd5e3ff),
surface = Color(0xFF001c3b), surface = Color(0xFF001c3b),
onSurface = Color(0xFFd5e3ff), onSurface = Color(0xFFd5e3ff),
surfaceVariant = Color(0xFF40484c), surfaceVariant = Color(0xFF082b4b), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFbfc8cc), onSurfaceVariant = Color(0xFFbfc8cc),
surfaceTint = Color(0xFF5ed4fc), surfaceTint = Color(0xFF5ed4fc),
inverseSurface = Color(0xFFffe3c4), inverseSurface = Color(0xFFffe3c4),
inverseOnSurface = Color(0xFF001c3b), inverseOnSurface = Color(0xFF001c3b),
outline = Color(0xFF8a9296), outline = Color(0xFF8a9296),
surfaceContainerLowest = Color(0xFF072642),
surfaceContainerLow = Color(0xFF072947),
surfaceContainer = Color(0xFF082b4b), // Navigation bar background
surfaceContainerHigh = Color(0xFF093257),
surfaceContainerHighest = Color(0xFF0A3861),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -48,23 +53,28 @@ internal object TidalWaveColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFB4D4DF), primaryContainer = Color(0xFFB4D4DF),
onPrimaryContainer = Color(0xFF001f28), onPrimaryContainer = Color(0xFF001f28),
inversePrimary = Color(0xFFff987f), inversePrimary = Color(0xFFff987f),
secondary = Color(0xFF006780), secondary = Color(0xFF006780), // Unread badge
onSecondary = Color(0xFFffffff), onSecondary = Color(0xFFffffff), // Unread badge text
secondaryContainer = Color(0xFFb8eaff), secondaryContainer = Color(0xFF9AE1FF), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF001f28), onSecondaryContainer = Color(0xFF001f28), // Navigation bar selector icon
tertiary = Color(0xFF92f7bc), tertiary = Color(0xFF92f7bc), // Downloaded badge
onTertiary = Color(0xFF001c3b), onTertiary = Color(0xFF001c3b), // Downloaded badge text
tertiaryContainer = Color(0xFFc3fada), tertiaryContainer = Color(0xFFc3fada),
onTertiaryContainer = Color(0xFF78ffd6), onTertiaryContainer = Color(0xFF78ffd6),
background = Color(0xFFfdfbff), background = Color(0xFFfdfbff),
onBackground = Color(0xFF001c3b), onBackground = Color(0xFF001c3b),
surface = Color(0xFFfdfbff), surface = Color(0xFFfdfbff),
onSurface = Color(0xFF001c3b), onSurface = Color(0xFF001c3b),
surfaceVariant = Color(0xFFdce4e8), surfaceVariant = Color(0xFFe8eff5), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF40484c), onSurfaceVariant = Color(0xFF40484c),
surfaceTint = Color(0xFF006780), surfaceTint = Color(0xFF006780),
inverseSurface = Color(0xFF020400), inverseSurface = Color(0xFF020400),
inverseOnSurface = Color(0xFFffe3c4), inverseOnSurface = Color(0xFFffe3c4),
outline = Color(0xFF70787c), outline = Color(0xFF70787c),
surfaceContainerLowest = Color(0xFFe2e8ec),
surfaceContainerLow = Color(0xFFe5ecf1),
surfaceContainer = Color(0xFFe8eff5), // Navigation bar background
surfaceContainerHigh = Color(0xFFedf4fA),
surfaceContainerHighest = Color(0xFFf5faff),
) )
} }
@@ -17,24 +17,29 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFFFFF), primaryContainer = Color(0xFFFFFFFF),
onPrimaryContainer = Color(0xFF000000), onPrimaryContainer = Color(0xFF000000),
inversePrimary = Color(0xFFCECECE), inversePrimary = Color(0xFFCECECE),
secondary = Color(0xFFFFFFFF), secondary = Color(0xFFFFFFFF), // Unread badge
onSecondary = Color(0xFF5A5A5A), onSecondary = Color(0xFF5A5A5A), // Unread badge text
secondaryContainer = Color(0xFF717171), secondaryContainer = Color(0xFF717171), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFE4E4E4), onSecondaryContainer = Color(0xFFE4E4E4), // Navigation bar selector icon
tertiary = Color(0xFF000000), tertiary = Color(0xFF000000), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFF00419E), tertiaryContainer = Color(0xFF00419E),
onTertiaryContainer = Color(0xFFD8E2FF), onTertiaryContainer = Color(0xFFD8E2FF),
background = Color(0xFF1E1E1E), background = Color(0xFF1E1E1E),
onBackground = Color(0xFFE6E6E6), onBackground = Color(0xFFE6E6E6),
surface = Color(0xFF1E1E1E), surface = Color(0xFF1E1E1E),
onSurface = Color(0xFFE6E6E6), onSurface = Color(0xFFE6E6E6),
surfaceVariant = Color(0xFF4E4E4E), surfaceVariant = Color(0xFF313131), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD1D1D1), onSurfaceVariant = Color(0xFFD1D1D1),
surfaceTint = Color(0xFFFFFFFF), surfaceTint = Color(0xFFFFFFFF),
inverseSurface = Color(0xFFE6E6E6), inverseSurface = Color(0xFFE6E6E6),
inverseOnSurface = Color(0xFF1E1E1E), inverseOnSurface = Color(0xFF1E1E1E),
outline = Color(0xFF999999), outline = Color(0xFF999999),
surfaceContainerLowest = Color(0xFF2A2A2A),
surfaceContainerLow = Color(0xFF2D2D2D),
surfaceContainer = Color(0xFF313131), // Navigation bar background
surfaceContainerHigh = Color(0xFF383838),
surfaceContainerHighest = Color(0xFF3F3F3F),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -43,23 +48,28 @@ internal object YinYangColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF000000), primaryContainer = Color(0xFF000000),
onPrimaryContainer = Color(0xFFFFFFFF), onPrimaryContainer = Color(0xFFFFFFFF),
inversePrimary = Color(0xFFA6A6A6), inversePrimary = Color(0xFFA6A6A6),
secondary = Color(0xFF000000), secondary = Color(0xFF000000), // Unread badge
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFDDDDDD), secondaryContainer = Color(0xFFDDDDDD), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF0C0C0C), onSecondaryContainer = Color(0xFF0C0C0C), // Navigation bar selector icon
tertiary = Color(0xFFFFFFFF), tertiary = Color(0xFFFFFFFF), // Downloaded badge
onTertiary = Color(0xFF000000), onTertiary = Color(0xFF000000), // Downloaded badge text
tertiaryContainer = Color(0xFFD8E2FF), tertiaryContainer = Color(0xFFD8E2FF),
onTertiaryContainer = Color(0xFF001947), onTertiaryContainer = Color(0xFF001947),
background = Color(0xFFFDFDFD), background = Color(0xFFFDFDFD),
onBackground = Color(0xFF222222), onBackground = Color(0xFF222222),
surface = Color(0xFFFDFDFD), surface = Color(0xFFFDFDFD),
onSurface = Color(0xFF222222), onSurface = Color(0xFF222222),
surfaceVariant = Color(0xFFEDEDED), surfaceVariant = Color(0xFFE8E8E8), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF515151), onSurfaceVariant = Color(0xFF515151),
surfaceTint = Color(0xFF000000), surfaceTint = Color(0xFF000000),
inverseSurface = Color(0xFF333333), inverseSurface = Color(0xFF333333),
inverseOnSurface = Color(0xFFF4F4F4), inverseOnSurface = Color(0xFFF4F4F4),
outline = Color(0xFF838383), outline = Color(0xFF838383),
surfaceContainerLowest = Color(0xFFCFCFCF),
surfaceContainerLow = Color(0xFFDADADA),
surfaceContainer = Color(0xFFE8E8E8), // Navigation bar background
surfaceContainerHigh = Color(0xFFECECEC),
surfaceContainerHighest = Color(0xFFEFEFEF),
) )
} }
@@ -23,24 +23,29 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFF862200), primaryContainer = Color(0xFF862200),
onPrimaryContainer = Color(0xFFFFDBCF), onPrimaryContainer = Color(0xFFFFDBCF),
inversePrimary = Color(0xFFAE3200), inversePrimary = Color(0xFFAE3200),
secondary = Color(0xFFFFB59D), secondary = Color(0xFFFFB59D), // Unread badge
onSecondary = Color(0xFF5F1600), onSecondary = Color(0xFF5F1600), // Unread badge text
secondaryContainer = Color(0xFF862200), secondaryContainer = Color(0xFF862200), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFFFFDBCF), onSecondaryContainer = Color(0xFFFFDBCF), // Navigation bar selector icon
tertiary = Color(0xFFD7C68D), tertiary = Color(0xFFD7C68D), // Downloaded badge
onTertiary = Color(0xFF3A2F05), onTertiary = Color(0xFF3A2F05), // Downloaded badge text
tertiaryContainer = Color(0xFF524619), tertiaryContainer = Color(0xFF524619),
onTertiaryContainer = Color(0xFFF5E2A7), onTertiaryContainer = Color(0xFFF5E2A7),
background = Color(0xFF211A18), background = Color(0xFF211A18),
onBackground = Color(0xFFEDE0DD), onBackground = Color(0xFFEDE0DD),
surface = Color(0xFF211A18), surface = Color(0xFF211A18),
onSurface = Color(0xFFEDE0DD), onSurface = Color(0xFFEDE0DD),
surfaceVariant = Color(0xFF53433F), surfaceVariant = Color(0xFF332723), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFFD8C2BC), onSurfaceVariant = Color(0xFFD8C2BC),
surfaceTint = Color(0xFFFFB59D), surfaceTint = Color(0xFFFFB59D),
inverseSurface = Color(0xFFEDE0DD), inverseSurface = Color(0xFFEDE0DD),
inverseOnSurface = Color(0xFF211A18), inverseOnSurface = Color(0xFF211A18),
outline = Color(0xFFA08C87), outline = Color(0xFFA08C87),
surfaceContainerLowest = Color(0xFF2E221F),
surfaceContainerLow = Color(0xFF312521),
surfaceContainer = Color(0xFF332723), // Navigation bar background
surfaceContainerHigh = Color(0xFF413531),
surfaceContainerHighest = Color(0xFF4C403D),
) )
override val lightScheme = lightColorScheme( override val lightScheme = lightColorScheme(
@@ -49,23 +54,28 @@ internal object YotsubaColorScheme : BaseColorScheme() {
primaryContainer = Color(0xFFFFDBCF), primaryContainer = Color(0xFFFFDBCF),
onPrimaryContainer = Color(0xFF3B0A00), onPrimaryContainer = Color(0xFF3B0A00),
inversePrimary = Color(0xFFFFB59D), inversePrimary = Color(0xFFFFB59D),
secondary = Color(0xFFAE3200), secondary = Color(0xFFAE3200), // Unread badge
onSecondary = Color(0xFFFFFFFF), onSecondary = Color(0xFFFFFFFF), // Unread badge text
secondaryContainer = Color(0xFFFFDBCF), secondaryContainer = Color(0xFFEBCDC2), // Navigation bar selector pill & progress indicator (remaining)
onSecondaryContainer = Color(0xFF3B0A00), onSecondaryContainer = Color(0xFF3B0A00), // Navigation bar selector icon
tertiary = Color(0xFF6B5E2F), tertiary = Color(0xFF6B5E2F), // Downloaded badge
onTertiary = Color(0xFFFFFFFF), onTertiary = Color(0xFFFFFFFF), // Downloaded badge text
tertiaryContainer = Color(0xFFF5E2A7), tertiaryContainer = Color(0xFFF5E2A7),
onTertiaryContainer = Color(0xFF231B00), onTertiaryContainer = Color(0xFF231B00),
background = Color(0xFFFCFCFC), background = Color(0xFFFCFCFC),
onBackground = Color(0xFF211A18), onBackground = Color(0xFF211A18),
surface = Color(0xFFFCFCFC), surface = Color(0xFFFCFCFC),
onSurface = Color(0xFF211A18), onSurface = Color(0xFF211A18),
surfaceVariant = Color(0xFFF5DED8), surfaceVariant = Color(0xFFF6EBE7), // Navigation bar background (ThemePrefWidget)
onSurfaceVariant = Color(0xFF53433F), onSurfaceVariant = Color(0xFF53433F),
surfaceTint = Color(0xFFAE3200), surfaceTint = Color(0xFFAE3200),
inverseSurface = Color(0xFF362F2D), inverseSurface = Color(0xFF362F2D),
inverseOnSurface = Color(0xFFFBEEEB), inverseOnSurface = Color(0xFFFBEEEB),
outline = Color(0xFF85736E), outline = Color(0xFF85736E),
surfaceContainerLowest = Color(0xFFECE3E0),
surfaceContainerLow = Color(0xFFF1E7E4),
surfaceContainer = Color(0xFFF6EBE7), // Navigation bar background
surfaceContainerHigh = Color(0xFFFAF4F2),
surfaceContainerHighest = Color(0xFFFBF6F4),
) )
} }
@@ -52,17 +52,18 @@ import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.presentation.track.components.TrackLogoIcon import eu.kanade.presentation.track.components.TrackLogoIcon
import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.util.lang.toLocalDate
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import java.text.DateFormat import java.time.format.DateTimeFormatter
private const val UnsetStatusTextAlpha = 0.5F private const val UnsetStatusTextAlpha = 0.5F
@Composable @Composable
fun TrackInfoDialogHome( fun TrackInfoDialogHome(
trackItems: List<TrackItem>, trackItems: List<TrackItem>,
dateFormat: DateFormat, dateFormat: DateTimeFormatter,
onStatusClick: (TrackItem) -> Unit, onStatusClick: (TrackItem) -> Unit,
onChapterClick: (TrackItem) -> Unit, onChapterClick: (TrackItem) -> Unit,
onScoreClick: (TrackItem) -> Unit, onScoreClick: (TrackItem) -> Unit,
@@ -71,6 +72,7 @@ fun TrackInfoDialogHome(
onNewSearch: (TrackItem) -> Unit, onNewSearch: (TrackItem) -> Unit,
onOpenInBrowser: (TrackItem) -> Unit, onOpenInBrowser: (TrackItem) -> Unit,
onRemoved: (TrackItem) -> Unit, onRemoved: (TrackItem) -> Unit,
onCopyLink: (TrackItem) -> Unit,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -104,17 +106,18 @@ fun TrackInfoDialogHome(
.takeIf { supportsScoring && item.track.score != 0.0 }, .takeIf { supportsScoring && item.track.score != 0.0 },
onScoreClick = { onScoreClick(item) } onScoreClick = { onScoreClick(item) }
.takeIf { supportsScoring }, .takeIf { supportsScoring },
startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) } startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate.toLocalDate()) }
.takeIf { supportsReadingDates && item.track.startDate != 0L }, .takeIf { supportsReadingDates && item.track.startDate != 0L },
onStartDateClick = { onStartDateEdit(item) } // TODO onStartDateClick = { onStartDateEdit(item) } // TODO
.takeIf { supportsReadingDates }, .takeIf { supportsReadingDates },
endDate = dateFormat.format(item.track.finishDate) endDate = dateFormat.format(item.track.finishDate.toLocalDate())
.takeIf { supportsReadingDates && item.track.finishDate != 0L }, .takeIf { supportsReadingDates && item.track.finishDate != 0L },
onEndDateClick = { onEndDateEdit(item) } onEndDateClick = { onEndDateEdit(item) }
.takeIf { supportsReadingDates }, .takeIf { supportsReadingDates },
onNewSearch = { onNewSearch(item) }, onNewSearch = { onNewSearch(item) },
onOpenInBrowser = { onOpenInBrowser(item) }, onOpenInBrowser = { onOpenInBrowser(item) },
onRemoved = { onRemoved(item) }, onRemoved = { onRemoved(item) },
onCopyLink = { onCopyLink(item) },
) )
} else { } else {
TrackInfoItemEmpty( TrackInfoItemEmpty(
@@ -143,6 +146,7 @@ private fun TrackInfoItem(
onNewSearch: () -> Unit, onNewSearch: () -> Unit,
onOpenInBrowser: () -> Unit, onOpenInBrowser: () -> Unit,
onRemoved: () -> Unit, onRemoved: () -> Unit,
onCopyLink: () -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
Column { Column {
@@ -152,6 +156,7 @@ private fun TrackInfoItem(
TrackLogoIcon( TrackLogoIcon(
tracker = tracker, tracker = tracker,
onClick = onOpenInBrowser, onClick = onOpenInBrowser,
onLongClick = onCopyLink,
) )
Box( Box(
modifier = Modifier modifier = Modifier
@@ -178,6 +183,7 @@ private fun TrackInfoItem(
TrackInfoItemMenu( TrackInfoItemMenu(
onOpenInBrowser = onOpenInBrowser, onOpenInBrowser = onOpenInBrowser,
onRemoved = onRemoved, onRemoved = onRemoved,
onCopyLink = onCopyLink,
) )
} }
@@ -185,7 +191,7 @@ private fun TrackInfoItem(
modifier = Modifier modifier = Modifier
.padding(top = 12.dp) .padding(top = 12.dp)
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.padding(8.dp) .padding(8.dp)
.clip(RoundedCornerShape(6.dp)), .clip(RoundedCornerShape(6.dp)),
) { ) {
@@ -286,6 +292,7 @@ private fun TrackInfoItemEmpty(
private fun TrackInfoItemMenu( private fun TrackInfoItemMenu(
onOpenInBrowser: () -> Unit, onOpenInBrowser: () -> Unit,
onRemoved: () -> Unit, onRemoved: () -> Unit,
onCopyLink: () -> Unit,
) { ) {
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) { Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) {
@@ -306,6 +313,13 @@ private fun TrackInfoItemMenu(
expanded = false expanded = false
}, },
) )
DropdownMenuItem(
text = { Text(stringResource(MR.strings.action_copy_link)) },
onClick = {
onCopyLink()
expanded = false
},
)
DropdownMenuItem( DropdownMenuItem(
text = { Text(stringResource(MR.strings.action_remove)) }, text = { Text(stringResource(MR.strings.action_remove)) },
onClick = { onClick = {
@@ -5,7 +5,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.test.DummyTracker import eu.kanade.test.DummyTracker
import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.model.Track
import java.text.DateFormat import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
internal class TrackInfoDialogHomePreviewProvider : internal class TrackInfoDialogHomePreviewProvider :
PreviewParameterProvider<@Composable () -> Unit> { PreviewParameterProvider<@Composable () -> Unit> {
@@ -46,7 +47,7 @@ internal class TrackInfoDialogHomePreviewProvider :
trackItemWithoutTrack, trackItemWithoutTrack,
trackItemWithTrack, trackItemWithTrack,
), ),
dateFormat = DateFormat.getDateInstance(), dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
onStatusClick = {}, onStatusClick = {},
onChapterClick = {}, onChapterClick = {},
onScoreClick = {}, onScoreClick = {},
@@ -55,13 +56,14 @@ internal class TrackInfoDialogHomePreviewProvider :
onNewSearch = {}, onNewSearch = {},
onOpenInBrowser = {}, onOpenInBrowser = {},
onRemoved = {}, onRemoved = {},
onCopyLink = {},
) )
} }
private val noTrackers = @Composable { private val noTrackers = @Composable {
TrackInfoDialogHome( TrackInfoDialogHome(
trackItems = listOf(), trackItems = listOf(),
dateFormat = DateFormat.getDateInstance(), dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
onStatusClick = {}, onStatusClick = {},
onChapterClick = {}, onChapterClick = {},
onScoreClick = {}, onScoreClick = {},
@@ -70,6 +72,7 @@ internal class TrackInfoDialogHomePreviewProvider :
onNewSearch = {}, onNewSearch = {},
onOpenInBrowser = {}, onOpenInBrowser = {},
onRemoved = {}, onRemoved = {},
onCopyLink = {},
) )
} }
@@ -22,9 +22,10 @@ import tachiyomi.presentation.core.util.clickableNoIndication
fun TrackLogoIcon( fun TrackLogoIcon(
tracker: Tracker, tracker: Tracker,
onClick: (() -> Unit)? = null, onClick: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
) { ) {
val modifier = if (onClick != null) { val modifier = if (onClick != null) {
Modifier.clickableNoIndication(onClick = onClick) Modifier.clickableNoIndication(onClick = onClick, onLongClick = onLongClick)
} else { } else {
Modifier Modifier
} }
@@ -53,6 +54,7 @@ private fun TrackLogoIconPreviews(
TrackLogoIcon( TrackLogoIcon(
tracker = tracker, tracker = tracker,
onClick = null, onClick = null,
onLongClick = null,
) )
} }
} }
@@ -4,6 +4,7 @@ import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material.icons.outlined.FlipToBack import androidx.compose.material.icons.outlined.FlipToBack
import androidx.compose.material.icons.outlined.Refresh import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material.icons.outlined.SelectAll import androidx.compose.material.icons.outlined.SelectAll
@@ -36,7 +37,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
import java.util.Date import java.time.LocalDate
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@Composable @Composable
@@ -50,6 +51,7 @@ fun UpdateScreen(
onClickCover: (UpdatesItem) -> Unit, onClickCover: (UpdatesItem) -> Unit,
onSelectAll: (Boolean) -> Unit, onSelectAll: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
onCalendarClicked: () -> Unit,
onUpdateLibrary: () -> Boolean, onUpdateLibrary: () -> Boolean,
onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit, onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit,
onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit, onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit,
@@ -63,6 +65,7 @@ fun UpdateScreen(
Scaffold( Scaffold(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
UpdatesAppBar( UpdatesAppBar(
onCalendarClicked = { onCalendarClicked() },
onUpdateLibrary = { onUpdateLibrary() }, onUpdateLibrary = { onUpdateLibrary() },
actionModeCounter = state.selected.size, actionModeCounter = state.selected.size,
onSelectAll = { onSelectAll(true) }, onSelectAll = { onSelectAll(true) },
@@ -132,6 +135,7 @@ fun UpdateScreen(
@Composable @Composable
private fun UpdatesAppBar( private fun UpdatesAppBar(
onCalendarClicked: () -> Unit,
onUpdateLibrary: () -> Unit, onUpdateLibrary: () -> Unit,
// For action mode // For action mode
actionModeCounter: Int, actionModeCounter: Int,
@@ -147,6 +151,11 @@ private fun UpdatesAppBar(
actions = { actions = {
AppBarActions( AppBarActions(
persistentListOf( persistentListOf(
AppBar.Action(
title = stringResource(MR.strings.action_view_upcoming),
icon = Icons.Outlined.CalendarMonth,
onClick = onCalendarClicked,
),
AppBar.Action( AppBar.Action(
title = stringResource(MR.strings.action_update_library), title = stringResource(MR.strings.action_update_library),
icon = Icons.Outlined.Refresh, icon = Icons.Outlined.Refresh,
@@ -212,6 +221,6 @@ private fun UpdatesBottomBar(
} }
sealed interface UpdatesUiModel { sealed interface UpdatesUiModel {
data class Header(val date: Date) : UpdatesUiModel data class Header(val date: LocalDate) : UpdatesUiModel
data class Item(val item: UpdatesItem) : UpdatesUiModel data class Item(val item: UpdatesItem) : UpdatesUiModel
} }
@@ -30,12 +30,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import com.google.accompanist.web.AccompanistWebViewClient import com.kevinnzou.web.AccompanistWebViewClient
import com.google.accompanist.web.LoadingState import com.kevinnzou.web.LoadingState
import com.google.accompanist.web.WebContent import com.kevinnzou.web.WebContent
import com.google.accompanist.web.WebView import com.kevinnzou.web.WebView
import com.google.accompanist.web.rememberWebViewNavigator import com.kevinnzou.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState import com.kevinnzou.web.rememberWebViewState
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.util.system.setDefaultSettings import eu.kanade.tachiyomi.util.system.setDefaultSettings
@@ -28,11 +28,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.accompanist.web.AccompanistWebViewClient import com.kevinnzou.web.AccompanistWebViewClient
import com.google.accompanist.web.LoadingState import com.kevinnzou.web.LoadingState
import com.google.accompanist.web.WebView import com.kevinnzou.web.WebView
import com.google.accompanist.web.rememberWebViewNavigator import com.kevinnzou.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState import com.kevinnzou.web.rememberWebViewState
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.components.WarningBanner
+73 -46
View File
@@ -15,12 +15,12 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import coil.ImageLoader import coil3.ImageLoader
import coil.ImageLoaderFactory import coil3.SingletonImageLoader
import coil.decode.GifDecoder import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil.decode.ImageDecoderDecoder import coil3.request.allowRgb565
import coil.disk.DiskCache import coil3.request.crossfade
import coil.util.DebugLogger import coil3.util.DebugLogger
import com.elvishew.xlog.LogConfiguration import com.elvishew.xlog.LogConfiguration
import com.elvishew.xlog.LogLevel import com.elvishew.xlog.LogLevel
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
@@ -33,10 +33,12 @@ import com.google.firebase.ktx.Firebase
import eu.kanade.domain.DomainModule import eu.kanade.domain.DomainModule
import eu.kanade.domain.SYDomainModule import eu.kanade.domain.SYDomainModule
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.sync.SyncPreferences
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
import eu.kanade.tachiyomi.crash.CrashActivity import eu.kanade.tachiyomi.crash.CrashActivity
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
import eu.kanade.tachiyomi.data.coil.MangaKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer
@@ -44,6 +46,7 @@ import eu.kanade.tachiyomi.data.coil.PagePreviewFetcher
import eu.kanade.tachiyomi.data.coil.PagePreviewKeyer import eu.kanade.tachiyomi.data.coil.PagePreviewKeyer
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.sync.SyncDataJob
import eu.kanade.tachiyomi.di.AppModule import eu.kanade.tachiyomi.di.AppModule
import eu.kanade.tachiyomi.di.PreferenceModule import eu.kanade.tachiyomi.di.PreferenceModule
import eu.kanade.tachiyomi.di.SYPreferenceModule import eu.kanade.tachiyomi.di.SYPreferenceModule
@@ -68,8 +71,12 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import logcat.LogPriority import logcat.LogPriority
import logcat.LogcatLogger import logcat.LogcatLogger
import mihon.core.migration.Migrator
import mihon.core.migration.migrations.migrations
import org.conscrypt.Conscrypt import org.conscrypt.Conscrypt
import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.storage.service.StorageManager import tachiyomi.domain.storage.service.StorageManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -81,7 +88,7 @@ import java.security.Security
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
private val basePreferences: BasePreferences by injectLazy() private val basePreferences: BasePreferences by injectLazy()
private val networkPreferences: NetworkPreferences by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy()
@@ -164,43 +171,78 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
/*if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) { /*if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) {
LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE)) LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE))
}*/ }*/
val syncPreferences: SyncPreferences = Injekt.get()
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppStart
) {
SyncDataJob.startNow(this@App)
}
initializeMigrator()
} }
override fun newImageLoader(): ImageLoader { private fun initializeMigrator() {
val preferenceStore = Injekt.get<PreferenceStore>()
// SY -->
val preference = preferenceStore.getInt(Preference.appStateKey("eh_last_version_code"), 0)
// SY <--
logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" }
Migrator.initialize(
old = preference.get(),
new = BuildConfig.VERSION_CODE,
migrations = migrations,
onMigrationComplete = {
logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" }
preference.set(BuildConfig.VERSION_CODE)
},
)
}
@Suppress("MagicNumber")
override fun newImageLoader(context: Context): ImageLoader {
return ImageLoader.Builder(this).apply { return ImageLoader.Builder(this).apply {
val callFactoryInit = { Injekt.get<NetworkHelper>().client } val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
val diskCacheInit = { CoilDiskCache.get(this@App) }
components { components {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { // NetworkFetcher.Factory
add(ImageDecoderDecoder.Factory()) add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
} else { // Decoder.Factory
add(GifDecoder.Factory())
}
add(TachiyomiImageDecoder.Factory()) add(TachiyomiImageDecoder.Factory())
add(MangaCoverFetcher.MangaFactory(lazy(callFactoryInit), lazy(diskCacheInit))) // Fetcher.Factory
add(MangaCoverFetcher.MangaCoverFactory(lazy(callFactoryInit), lazy(diskCacheInit))) add(BufferedSourceFetcher.Factory())
add(MangaKeyer()) add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
// SY -->
add(PagePreviewFetcher.Factory(callFactoryLazy))
// SY <--
// Keyer
add(MangaCoverKeyer()) add(MangaCoverKeyer())
add(MangaKeyer())
// SY --> // SY -->
add(PagePreviewKeyer()) add(PagePreviewKeyer())
add(PagePreviewFetcher.Factory(lazy(callFactoryInit), lazy(diskCacheInit)))
// SY <-- // SY <--
} }
callFactory(callFactoryInit)
diskCache(diskCacheInit)
crossfade((300 * this@App.animatorDurationScale).toInt()) crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(DeviceUtil.isLowRamDevice(this@App)) allowRgb565(DeviceUtil.isLowRamDevice(this@App))
if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
// Coil spawns a new thread for every image load by default // Coil spawns a new thread for every image load by default
fetcherDispatcher(Dispatchers.IO.limitedParallelism(8)) fetcherCoroutineContext(Dispatchers.IO.limitedParallelism(8))
decoderDispatcher(Dispatchers.IO.limitedParallelism(2)) decoderCoroutineContext(Dispatchers.IO.limitedParallelism(3))
transformationDispatcher(Dispatchers.IO.limitedParallelism(2)) }
}.build() .build()
} }
override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) {
SecureActivityDelegate.onApplicationStart() SecureActivityDelegate.onApplicationStart()
val syncPreferences: SyncPreferences = Injekt.get()
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppResume
) {
SyncDataJob.startNow(this@App)
}
} }
override fun onStop(owner: LifecycleOwner) { override fun onStop(owner: LifecycleOwner) {
@@ -234,6 +276,12 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to modify notification channels" } logcat(LogPriority.ERROR, e) { "Failed to modify notification channels" }
} }
val syncPreferences: SyncPreferences = Injekt.get()
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppStart) {
SyncDataJob.startNow(this@App)
}
} }
// EXH // EXH
@@ -331,24 +379,3 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
} }
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE" private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"
/**
* Direct copy of Coil's internal SingletonDiskCache so that [MangaCoverFetcher] can access it.
*/
private object CoilDiskCache {
private const val FOLDER_NAME = "image_cache"
private var instance: DiskCache? = null
@Synchronized
fun get(context: Context): DiskCache {
return instance ?: run {
val safeCacheDir = context.cacheDir.apply { mkdirs() }
// Create the singleton disk cache instance.
DiskCache.Builder()
.directory(safeCacheDir.resolve(FOLDER_NAME))
.build()
.also { instance = it }
}
}
}

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