Compare commits

...

796 commits

Author SHA1 Message Date
17879ddc5a
chore: Bump version code
Some checks failed
Build app / Build app (push) Has been cancelled
Mirror Repository / mirror (push) Has been cancelled
2025-06-02 13:52:46 +07:00
7f83c117be
chore: Sync project [skip ci] 2025-06-02 10:04:20 +07:00
abbe606473
revert: "refactor: Replace Requery's SQLite with AndroidX's new KMP SQLite"
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
This reverts commit f604e4e256.
2025-06-01 18:05:35 +07:00
7ac42d5545
docs: Sync changelog 2025-06-01 16:44:45 +07:00
renovate[bot]
f90e2a1425
fix(deps): Update dependency io.github.pdvrieze.xmlutil:core-android to v0.91.1 (#440)
* fix(deps): Update dependency io.github.pdvrieze.xmlutil:core-android to v0.91.1

* fix: Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2025-06-01 16:43:59 +07:00
f604e4e256
refactor: Replace Requery's SQLite with AndroidX's new KMP SQLite
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
2025-06-01 14:55:19 +07:00
a04ea9f5ea
chore(deps): Also grab dependency from sonatype just in case
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
2025-05-31 11:11:21 +07:00
43d4d5404d
docs: Sync changelog 2025-05-31 07:36:21 +07:00
renovate[bot]
54df9436b8
fix(deps): Update serialization to v1.8.1 (#446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:33:22 +07:00
renovate[bot]
cd5cdbe746
fix(deps): Update dependency io.github.fornewid:material-motion-compose-core to v1.2.1 (#439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:32:55 +07:00
renovate[bot]
41662979fe
fix(deps): Update lifecycle to v2.9.0 (#445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:26:24 +07:00
renovate[bot]
f68e9df74d
fix(deps): Update dependency org.jsoup:jsoup to v1.20.1 (#443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:26:05 +07:00
renovate[bot]
a77d315922
fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.4.0 (#442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:25:49 +07:00
renovate[bot]
4e2c4aef8a
fix(deps): Update dependency io.mockk:mockk to v1.14.2 (#441)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:25:31 +07:00
renovate[bot]
d80b53ba78
fix(deps): Update dependency io.coil-kt.coil3:coil-bom to v3.2.0 (#438)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:25:16 +07:00
renovate[bot]
a1f6eb6524
fix(deps): Update dependency com.squareup.okio:okio to v3.12.0 (#437)
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:24:58 +07:00
renovate[bot]
9f256bb8c6
fix(deps): Update dependency com.google.firebase:firebase-bom to v33.14.0 (#436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:24:40 +07:00
renovate[bot]
1d49d65961
fix(deps): Update dependency com.google.accompanist:accompanist-themeadapter-material3 to v0.36.0 (#435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:24:24 +07:00
renovate[bot]
9ccdd36c46
fix(deps): Update dependency com.github.requery:sqlite-android to v3.49.0 (#434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:24:04 +07:00
renovate[bot]
48938f02dd
fix(deps): Update dependency com.getkeepsafe.taptargetview:taptargetview to v1.15.0 (#432)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:21:18 +07:00
renovate[bot]
63435b933a
fix(deps): Update dependency androidx.window:window to v1.4.0 (#431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:20:59 +07:00
renovate[bot]
ef49bf3321
fix(deps): Update dependency androidx.webkit:webkit to v1.13.0 (#430)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:20:44 +07:00
renovate[bot]
c05ba1a8fb
fix(deps): Update dependency androidx.sqlite:sqlite-ktx to v2.5.1 (#429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:16:58 +07:00
renovate[bot]
764d52a729
fix(deps): Update dependency androidx.sqlite:sqlite to v2.5.1 (#428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:16:45 +07:00
renovate[bot]
af2be0d2d0
fix(deps): Update dependency androidx.recyclerview:recyclerview to v1.4.0 (#427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:14:07 +07:00
renovate[bot]
733fcbba4a
fix(deps): Update dependency androidx.core:core-ktx to v1.16.0 (#426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:13:50 +07:00
renovate[bot]
b6e1cabc59
fix(deps): Update dependency androidx.compose:compose-bom to v2025.05.01 (#425)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:13:35 +07:00
renovate[bot]
3867aabff1
fix(deps): Update aboutlibraries to v11.6.3 (#424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:13:20 +07:00
renovate[bot]
ca6bb95b84
chore(deps): Update plugin kotlinter to v5.1.0 (#423)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:12:59 +07:00
renovate[bot]
4b7564e410
chore(deps): Update plugin gradle-versions to v0.52.0 (#422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:11:36 +07:00
renovate[bot]
8d3cfffa66
fix(deps): Update okhttp monorepo to v5.0.0-alpha.16 (#421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2025-05-31 07:11:10 +07:00
renovate[bot]
97339689c6
fix(deps): Update moko to v0.24.5 (#420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:10:01 +07:00
renovate[bot]
9453c3e808
fix(deps): Update kotlin monorepo to v2.1.21 (#419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:09:41 +07:00
renovate[bot]
6f03935c17
fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.2 (#418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:09:25 +07:00
renovate[bot]
f1597bd95c
fix(deps): Update dependency me.zhanghai.android.libarchive:library to v1.1.5 (#417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:09:09 +07:00
renovate[bot]
903a37e390
fix(deps): Update dependency io.insert-koin:koin-bom to v4.0.4 (#416)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:08:54 +07:00
renovate[bot]
1655540a16
fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.5 (#415)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:08:40 +07:00
renovate[bot]
6a7b386127
fix(deps): Update dependency androidx.work:work-runtime-ktx to v2.10.1 (#414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:08:23 +07:00
renovate[bot]
75191dde05
fix(deps): Update dependency androidx.constraintlayout:constraintlayout to v2.2.1 (#413)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:08:04 +07:00
renovate[bot]
db0af71901
chore(deps): Update plugin firebase-crashlytics to v3.0.3 (#412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 07:07:50 +07:00
renovate[bot]
89c5e997cc
chore(deps): Update null2264/actions digest to 363cb9c (#411)
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
* chore(deps): Update null2264/actions digest to 363cb9c

* fix: Action was renamed

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2025-05-30 08:49:47 +07:00
e22559b2df
fix(renovate): Ignore more jitpack packages [skip ci] 2025-05-30 08:33:35 +07:00
AwkwardPeak7
370bb62ef9
refactor: Change Page.State to sealed interface
Some checks failed
Build app / Build app (push) Has been cancelled
Mirror Repository / mirror (push) Has been cancelled
2025-05-28 09:19:42 +07:00
AntsyLich
18528fbd92
fix: Fix mark existing duplicate read chapters as read option not working in some cases 2025-05-28 09:05:14 +07:00
7964ac87c6
fix: Wrong pref to bound
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
I should really stop coding in the middle of the night
2025-05-27 22:25:19 +07:00
850151720b
feat: Mark duplicate read chapters as read
Some checks are pending
Build app / Build app (push) Waiting to run
Mirror Repository / mirror (push) Waiting to run
This also refactor how chapters progress are saved. Chapters' progress now save when user "flipped" the page.

Closes GH-409

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2025-05-27 21:47:41 +07:00
d3050d5799
fix: Fix weblate [skip ci] 2025-05-26 08:15:39 +07:00
c7d2ff0970
chore: Ignore weblate config [skip ci] 2025-05-26 08:00:32 +07:00
524c00fd44
docs(README): Mirror the project to my personal git hosting [skip ci]
Just in case
2025-05-26 07:40:38 +07:00
93f819c236
docs(README): Mirror the project to my personal git hosting [skip ci]
Just in case
2025-05-26 07:37:00 +07:00
1c73b925b1
chore(github): Everyone gets to vote [skip ci] 2025-05-23 08:07:50 +07:00
Weblate (bot)
ea179979b1
chore(i18n): Translations update from Hosted Weblate (#390)
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/es/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fi/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fil/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fr/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/hi/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hans/
Translation: Yōkai/Yōkai

Co-authored-by: Hiirbaf <thefgc8@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: MARTINAT Noah <noahmartinat@gmail.com>
Co-authored-by: Ricky Tigg <ricky.tigg@gmail.com>
Co-authored-by: Thibault <tib2935@gmail.com>
Co-authored-by: UnTamed Fury <prince.16.tamoli@gmail.com>
Co-authored-by: zhongfly <11155705+zhongfly@users.noreply.github.com>
2025-05-14 10:51:39 +07:00
71c26b77fc
chore: Sync project [skip ci] 2025-05-14 04:37:33 +07:00
43a5e8edd8
fix: Disable MAL's custom user-agent 2025-05-14 04:13:41 +07:00
Hiirbaf
33d7c3cd2b
feat: update user agent (#401)
* Switch default user agent to Android Chrome

* Emparejamiento con mihon

* Update User-Agent
2025-05-14 03:44:48 +07:00
0e0e865cb9
docs(CHANGELOG): Add missing credit [skip ci] 2025-05-11 19:54:26 +07:00
AwkwardPeak7
2b2e0491e8
fix: staggered grid cover being squashed for local source (#398)
* fix: cover ratio NaN for local source

* unused

* Update CHANGELOG.md
2025-05-11 19:52:43 +07:00
f035454150
chore: Sync project [skip ci] 2025-05-09 11:18:55 +07:00
96f88d5e90
docs: Sync changelog [skip ci] 2025-05-09 11:17:58 +07:00
f362b0bda0
fix: Fix saved search not restoring Filter.Group properly
This happened when Filter.Group ordering doesn't match the old ordering.
2025-05-09 10:44:30 +07:00
f74662c0f3
docs: Add missing credits [skip ci] 2025-05-05 07:01:51 +07:00
65639391b7
docs: Sync changelog 2025-05-05 06:49:29 +07:00
Hiirbaf
94c314559b
feat: Enable/Disable Sources Swipe (#396)
* Update SourceItem.kt

* Update SettingsBrowseController.kt 1

* Update SettingsBrowseController.kt

* Update SettingsBrowseController.kt

* Update strings.xml

* Update SettingsBrowseController.kt

* Update UiPreferences.kt

* Update SettingsBrowseController.kt

* Update SourceItem.kt
2025-05-05 06:46:29 +07:00
66241774dc chore(deps): Update dependency gradle to v8.12 2025-05-05 06:05:45 +07:00
ea634a5ce3
chore: Bump version to v1.10.0 [skip ci]
Changing the version scheme to:
- Major -> Basically a rewrite
- Minor -> Feature releases / big changes
- Macro -> Bug fixes
2025-04-18 12:07:46 +07:00
271e440014
fix: Prevent potential "Comparison method violates its general contract!" crash 2025-04-17 17:12:31 +07:00
Lee Shuen Fei
8be33e0f81
feat: Display the number of filtered manga in each category's
header when searching in library (#387)
2025-04-15 07:05:15 +07:00
f13f98f19a
feat: Add the ability to save search queries
I got tired of putting the same tag over and over, so...
2025-04-14 21:02:12 +07:00
7a08ca294a
fix: Fix source filter buttom sheet unable to be fully scrolled to the bottom
This bug has been annoying me for a long time, classic RecyclerView moment
2025-04-13 18:21:30 +07:00
4faa641739
docs: Sync changelog [skip ci] 2025-04-12 07:18:19 +07:00
Weblate (bot)
ebd891fa75
chore(i18n): Translations update from Hosted Weblate (#330)
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai-plurals/fi/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/es/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fi/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fil/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fr/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/id/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/ja/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/ru/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/tr/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hans/
Translation: Yōkai/Yōkai
Translation: Yōkai/Yōkai Plurals

Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
Co-authored-by: Alexander Sergeev <hiyajo.maho@rambler.ru>
Co-authored-by: Hiirbaf <thefgc8@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: MARTINAT Noah <noahmartinat@gmail.com>
Co-authored-by: Ricky Tigg <ricky.tigg@gmail.com>
Co-authored-by: Tachibana Saza <tachibanasaza@proton.me>
Co-authored-by: zhongfly <11155705+zhongfly@users.noreply.github.com>
2025-04-12 07:17:22 +07:00
Hiirbaf
d46f5fb73e
fix: Temporarily disable file log (#380)
Temporary solution to the problem of stopping working in the background
2025-03-29 09:52:00 +07:00
AntsyLich
d6c5a9a7c2
chore: Tweak .editorconfig [skip ci] 2025-03-10 06:53:32 +07:00
MajorTanya
2208a81013
fix: Add Infinix system app to list of invalid browsers
`com.transsion.resolver` being picked by the system as a suitable
browser caused a Mihon user with an Infinix device to be unable to
open any links in browsers, including tracker login and opening a
WebView page in a real browser.
2025-02-05 07:54:26 +07:00
0bf55a8ca0
ci: Tweak GitHub Actions and switch from Adopt to Temurin JDK 2025-02-02 07:02:23 +07:00
4dd8aece0c
fix: Temporarily hide the experimental compose library switch for nightly 2025-01-24 18:54:06 +07:00
ece849b008
fix(AppBar): Use maxLayoutHeight instead of constraints.maxHeight
Also revert padding changes
2025-01-17 10:16:57 +07:00
d2ddf7dfb0
fix(AppBar): Also adjust the other placeables' padding 2025-01-17 09:35:32 +07:00
8b53e5ad10
fix(AppBar): Adjust title padding 2025-01-17 09:26:22 +07:00
63cdf247b4
chore: Sync AppBar code with upstream 2025-01-17 09:14:35 +07:00
9ed12ef07c
fix: Forgor to put ! 2025-01-16 17:50:21 +07:00
86b01a297f
fix: Can't set stable id like that 2025-01-14 03:59:47 +07:00
d6ffbe15ee
fix: Title is only lateinit on SMangaImpl 2025-01-13 13:46:29 +07:00
915ce20904
fix: Fix build 2025-01-13 13:30:11 +07:00
9e5d13f261
fix: Fix lateinit error 2025-01-13 13:22:19 +07:00
33fa77d527
fix: Fix some NPE crashes 2025-01-13 12:39:18 +07:00
baaa841278
fix: Explicitly disable stable ids 2025-01-13 12:23:18 +07:00
453ea32bc9
chore: Hide compose library on prod build 2025-01-13 11:33:23 +07:00
f37e657a9b
fix: Prevent lateinit crash 2025-01-13 11:29:07 +07:00
258708b038
fix: Fix build 2025-01-10 11:10:28 +07:00
c6da3325b3
fix(AppBar): Wrap scroll behaviour with remember { } 2025-01-10 11:06:11 +07:00
c9a90f6847
chore(library/compose): View<->Compose Interop
REF: https://developer.android.com/develop/ui/compose/touch-input/pointer-input/scroll#parent-view-child-compose
2025-01-09 07:22:21 +07:00
9cf1fbb118
fix(i18n): Use behavior instead of behaviour
The base should use US English not British English... I'm too used to
British English :^)
2025-01-08 07:55:00 +07:00
f01ace94be
chore(library/compose): LibraryItem data classes 2025-01-08 05:54:27 +07:00
ad22250265
fix(library/compose): Don't left the title empty 2025-01-07 19:05:51 +07:00
48c2ad9b33
refactor(library/compose): StateCoroutinePresenter 2025-01-07 18:59:08 +07:00
7d9c0faf86
fix: Compose library is not rendering anything 2025-01-07 10:08:40 +07:00
6614bd3ed8
docs(template): Wrong name [skip ci] 2025-01-07 08:23:02 +07:00
c6c40ffb71
docs(template): Replace broken link and instruct people how to find
extension repo maintainer [skip ci]
2025-01-07 08:20:57 +07:00
d0d322fd67
refactor(extension/repo): Use ScreenModel instead of ViewModel 2025-01-07 07:56:43 +07:00
d655c3e09a
chore: WIP compose library page 2025-01-07 07:36:00 +07:00
6a680facd5
refactor(extension): Installer abstraction 2025-01-07 05:45:50 +07:00
0565fc2665
fix: Selected icon for Random should be a reload icon 2025-01-06 08:02:00 +07:00
568859891a
fix: Forgor about the bubble 2025-01-05 19:27:29 +07:00
7fc95e3029
feat: Random sort 2025-01-05 19:19:28 +07:00
eebc3dc822
docs: Sync changelog 2025-01-05 18:20:08 +07:00
968639a59b
chore: Fix typo 2025-01-05 18:16:21 +07:00
Ahmad Ansori Palembani
cae0332ef9
refactor(library): Store sectioned library instead of flatten version of it (#336)
* refactor(library): Store sectioned first before flattening it out

* fix: Fix build, also rename some variables and move stuff around

* chore: Replace findCurrentCategory with currentCategory getter

* fix: Empty category can't be collapsed

* chore: Disable file log for debug build

* fix: Entry always displayed on default category

* refactor: Specify id, source, and url directly from MangaImpl constructor

* refactor: Make LibraryManga not extend MangaImpl

* refactor: Separate placeholder from LibraryManga

* fix: Default category should always be at the very beginning

* fix: Accidentally made the entries invisible

* fix: Default category disappear everytime a new category is added
2025-01-05 18:15:34 +07:00
e415fd4ef2
chore(about): Link weblate 2025-01-03 11:08:29 +07:00
a3672be728
fix(myanimelist): Fix nullability and fallback to medium cover if large
cover is null
2025-01-03 10:44:38 +07:00
e06b28a60e
fix: Handle version check for AboutController 2025-01-03 09:26:49 +07:00
eba5aa1d2e
fix: Make the markdown text scrollable 2025-01-03 08:40:46 +07:00
a554c079fb
fix: Handle update checker separately for AboutController for noe 2025-01-02 22:01:37 +07:00
49b10c1b4f
refactor: Rework Dialog 2025-01-02 21:42:53 +07:00
1a16d84e61
refactor(archive): Turn timespec function to extension method 2025-01-02 10:05:26 +07:00
fc87410d46
fix(download): Making sure archive tmp file is created properly 2025-01-02 09:05:41 +07:00
84d2924a82
chore(archive): Mark as internal or private 2025-01-02 08:29:30 +07:00
03e1953c9f
sync: Merge remote-tracking branch 'weblate/master' 2025-01-01 19:13:08 +07:00
Hosted Weblate
02c2b370b4
i18n: Translations update from Hosted Weblate
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: zhongfly <icesshadows@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai-plurals/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai-plurals/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fil/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/id/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hant/
Translation: Yōkai/Yōkai
Translation: Yōkai/Yōkai Plurals
2025-01-01 19:05:27 +07:00
fa84ce8fe8
refactor: Reduce dependant towards RecentsPresenter 2025-01-01 14:02:57 +07:00
42dd857d94
docs: Sync changelog 2025-01-01 10:33:37 +07:00
2cf2fcfc4f
refactor: WIP delegated source refactor
J2K only handles deep link, which was disabled when I forked it as Yokai... Might gonna re-introduce it for some sources I used later (mainly Cubari tbh)
2025-01-01 10:28:00 +07:00
b4377a4609
refactor: Move archive related code to core.archive module 2025-01-01 09:26:15 +07:00
54a3059730
chore: Move core module to core.main 2025-01-01 08:21:55 +07:00
672d364f43
chore: Remove stdlib 2025-01-01 08:03:30 +07:00
b19480de1a
chore: Remove cmake arguments 2025-01-01 07:58:43 +07:00
3c00a249c3
fix: Resolve deprecated gradle function 2025-01-01 07:54:15 +07:00
Hosted Weblate
012407aede
Translations update from Hosted Weblate
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: zhongfly <icesshadows@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai-plurals/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai-plurals/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/fil/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/id/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/yokai/yokai/zh_Hant/
Translation: Yōkai/Yōkai
Translation: Yōkai/Yōkai Plurals
2025-01-01 01:44:31 +01:00
1461f048dd
fix: Resolve deprecated gradle function 2025-01-01 07:44:09 +07:00
85af94d810
fix: Fix build 2025-01-01 07:35:55 +07:00
d02f1bdd11
refactor: Don't use context receiver
Deprecated on Kotlin 2.x, scheduled for removal in v2.1.x, will be replaced with context parameters

REF: https://github.com/Kotlin/KEEP/issues/259#issuecomment-2278319746
REF: https://youtrack.jetbrains.com/issue/KT-67119/Migration-warning-from-context-receivers-to-context-parameters
REF: https://github.com/Kotlin/KEEP/issues/367
2025-01-01 07:10:30 +07:00
1b92ae2e5f
chore: License and FIXME note 2024-12-31 12:53:37 +07:00
ac0d2e9fc0
revert: "fix: Emit scrolled event from onPreScroll" 2024-12-31 12:46:33 +07:00
8a28d1d484
fix: Emit scrolled event from onPreScroll 2024-12-31 12:24:45 +07:00
3399d6a326
docs: Remove Translation section [skip ci]
Handled by Weblate
2024-12-30 20:30:49 +07:00
6747795690
refactor: Use fast*() util functions 2024-12-30 20:28:44 +07:00
e554513392
style: Scrollbar
And add fast scroller component for later
2024-12-30 20:05:28 +07:00
f7e5abba59
ci: Ignore i18n changes except for base strings 2024-12-29 10:40:09 +07:00
a7874f2f29
docs: Update README [skip ci] 2024-12-29 10:36:09 +07:00
2f2ccac8e7
docs: Weblate [skip ci] 2024-12-29 10:08:20 +07:00
dd6a2f377a
docs: Sync changelog [skip ci] 2024-12-29 09:58:50 +07:00
f8807f81b1
chore: Remove unused translation strings [skip ci] 2024-12-29 09:48:00 +07:00
7ee9c7a746
docs(i18n): Add README.md [skip ci] 2024-12-29 09:27:44 +07:00
67c4500cce
style(about): Use CrossfadeTransition
It's basically the same as J2K's .withFadeTransaction
2024-12-28 12:15:08 +07:00
cd4079aa4b
fix: Crashes caused by cab40214d2
Screen arguments need to be parcelable
2024-12-28 11:23:34 +07:00
c6a86d773b
fix: Copy paste moment, it's Build time not Version 2024-12-28 11:08:48 +07:00
cab40214d2
refactor: Use Compose for About page 2024-12-28 10:53:01 +07:00
37f1f0e330
chore: Remove unused util function 2024-12-28 08:56:27 +07:00
310e90beb5
fix: Make status bar transparent 2024-12-27 10:26:23 +07:00
9bb869111d
fix: Don't hardcode status bar color 2024-12-27 08:55:03 +07:00
8a9d8166af
style(AppBar): Partially revert 71dcb2ab but adjust the color to
primaryContainer
2024-12-27 07:54:23 +07:00
71dcb2ab85
fix(AppBar): Adjust scrolled container color 2024-12-27 07:09:59 +07:00
60ef9482c8
style(AppBar): Scrolled container color 2024-12-26 13:55:43 +07:00
1cc8abb599
chore: Adjust aspect ratio to 2:3
For easier migration to Compose
2024-12-26 08:23:59 +07:00
96348dbf7d
fix(browse): Disable stable id
This reverts commit 8ac8187977.
2024-12-26 08:06:37 +07:00
23d4fb1fdd
fix(AppBar): Actions aren't aligned properly 2024-12-26 07:56:15 +07:00
f78d4e9e6a
fix(AppBar): Sizing issue when user flick too hard 2024-12-25 21:08:23 +07:00
120d2cfb96
feat(AppBar): EnterAlwaysCollapsed for Compose
Google left out EnterAlwaysCollapsed for some reason
2024-12-25 16:19:59 +07:00
dec1a70091
fix(AppBar): Re-introduce snap but only do it to the top bar 2024-12-25 16:12:11 +07:00
448c93365a
refactor: Try to mimic ExpandedAppBarLayout for Compose 2024-12-25 13:21:42 +07:00
55fad67223
fix(EmptyScreen): Align center the message if it's not tablet UI 2024-12-25 12:02:39 +07:00
c09c4045e2
refactor: Rework buildSrc
Co-authored-by: AntsyLich <59261191+antsylich@users.noreply.github.com>
2024-12-25 10:37:47 +07:00
b201e410a3
chore: Remove deprecated constants
They're moved to Constants object
2024-12-25 07:24:22 +07:00
915debd41b
docs: Sync changelog [skip ci] 2024-12-24 14:00:19 +07:00
6d2ae386a2
fix(reader): Use LocalContentColor's value 2024-12-24 13:41:37 +07:00
a853deae4a
fix(reader): Use it directly 2024-12-24 12:58:39 +07:00
f9bb2b96cb
fix(reader): Fix build 2024-12-24 12:43:30 +07:00
031e30e227
refactor: Use Compose for reader chapter transition
Co-authored-by: arkon <arkon@users.noreply.github.com>
2024-12-24 12:19:24 +07:00
0049653355
chore: Remove ic_local_library_24dp
No longer used, replaced by ImageVector
2024-12-24 08:50:53 +07:00
677d96eed5
docs: Sync changelog 2024-12-24 08:22:59 +07:00
640feb69ac
fix(recents): Only set list on queue state change when it's not empty 2024-12-24 08:07:10 +07:00
f3cac7cac8
refactor: Transform LocalSource icon from XML vector to ImageVector 2024-12-24 07:05:01 +07:00
99bec41056
style(EmptyScreen): Adjust spacing 2024-12-23 21:29:56 +07:00
afb7e79ea4
fix(EmptyScreen): Align buttons center 2024-12-23 21:17:45 +07:00
76af51a319
docs: Sync changelog 2024-12-23 21:06:20 +07:00
5824ac81c2
refactor: Use Compose for EmptyView 2024-12-23 20:58:30 +07:00
b1665eaedf
chore(i18n): Rephrasing 2024-12-23 09:26:45 +07:00
2b953a53d8
docs: Sync changelog 2024-12-23 09:12:53 +07:00
37f47ae2f5
fix(onboarding): Allow user to skip onboarding if Shizuku is installed
Fixes GH-322
2024-12-23 08:57:44 +07:00
3fa475c1cf
refactor: Move isShizukuInstalled check 2024-12-23 08:57:43 +07:00
renovate[bot]
bcf21858ee
fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.1 (#321)
* fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.1

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-12-23 08:29:38 +07:00
90f5dfc55a
fix(library): Don't show Default category if it's empty 2024-12-23 07:33:23 +07:00
Ahmad Ansori Palembani
365d259e94
refactor(library): Utilise flow even more (#272)
* revert: "revert: "refactor(library): Some adjustments""

This reverts commit 2b639d0630.

* fix: Don't use emptyFlow

* fix: Fix build

* fix: Don't overwrite allCategories inside `combine {}`

* fix: Fix build
2024-12-23 07:07:11 +07:00
09bd1a9f78
chore: Bump version to v1.9.8 [skip ci] 2024-12-23 06:26:24 +07:00
f8e7002a9b
chore(release): v1.9.7 [skip ci] 2024-12-23 06:24:25 +07:00
4caefff11d
refactor(manga): Turn getHistory() to a suspend func 2024-12-23 05:54:47 +07:00
07f9e921f1
docs: Sync changelog 2024-12-22 13:31:56 +07:00
Meokjeng
4256e86e97
chore(i18n): Update korean translation (#318)
I have corrected translations that were grammatically incorrect or had different meanings.
2024-12-22 13:30:53 +07:00
fdb69a1c7d
docs: Sync changelog 2024-12-22 10:44:26 +07:00
e65497baef
fix: Don't log everything to log files unless verbose logging is enabled 2024-12-22 10:42:08 +07:00
acafe931f1
fix(base): Log with .v 2024-12-22 10:40:46 +07:00
bafd9e54aa
chore: Disable verbose logging by default on nightly releases 2024-12-22 10:39:23 +07:00
a6e5b41d7d
chore: Disable FlexibleAdapter verbose log 2024-12-22 10:16:55 +07:00
629f1891f6
refactor(download): Singleton cache and provider 2024-12-21 19:42:37 +07:00
bfbbd1b4f3
fix: Wrong function 2024-12-21 12:00:55 +07:00
33a84f7e39
fix: Dismiss failed download notification before retrying 2024-12-21 11:51:43 +07:00
fe666b614f
docs: Sync changelog
Also reformat the code slightly
2024-12-21 06:26:40 +07:00
f240fe0dd4
fix: Don't use .hasQueue(), check queueSize directly 2024-12-21 06:13:54 +07:00
3787845893
fix: Download queue count not updating
Also, don't use raw integer for download badge colour
2024-12-21 06:03:23 +07:00
3606f67dba
fix(browse): Unsubscribe before restarting pager 2024-12-20 20:53:11 +07:00
9e5262140e
docs: Sync changelog
Also slight code adjustment
2024-12-20 20:32:03 +07:00
c73d0f843f
fix(browse): A different approach 2024-12-20 20:12:09 +07:00
b974eff320
debug: Verbose logging for FlexibleAdapter 2024-12-20 13:19:36 +07:00
8ac8187977
fix(browse): Enable stable id 2024-12-20 09:29:05 +07:00
778bd05e21
ci: Remove upload to artifact
No longer needed
2024-12-20 08:47:25 +07:00
3d2e2b2774
fix(library): Don't use double-bang 2024-12-20 08:46:18 +07:00
c1cb7a2066
fix(source/local): Don't use double-bang 2024-12-20 08:37:00 +07:00
2299aaac63
fix(manga): Explicitly check if tablet header is not null to avoid NPE 2024-12-20 08:31:25 +07:00
f985ad6daa
fix(download): Don't delay pause notification 2024-12-20 08:06:20 +07:00
12002f62cd
ci: Fix upload to artifact 2024-12-19 11:09:26 +07:00
eea87eede4
chore: Bump version to v1.9.7 for beta and nightly 2024-12-19 10:59:00 +07:00
2466d3a493
ci: Upload APK to artifact 2024-12-19 10:56:29 +07:00
bf8eccc186
ci: Update Android SDK tools to v35.0.0 2024-12-19 10:43:02 +07:00
1571678ddb
chore(deps): Update NDK to v27.2.12479018 2024-12-19 10:35:08 +07:00
cd8ff6f898
refactor(downloader): Extract ensureSuccessfulDownload
Also double-bang archiveChapter, since exceptions already being catch by
`downloadChapter`
2024-12-19 08:08:59 +07:00
985ac6d7a8
refactor(download): Move .show() inside with block 2024-12-19 07:56:41 +07:00
17eec5f6aa
revert: "refactor(archive): Move stuff around"
This reverts commit e19d048bb1.
2024-12-19 07:18:14 +07:00
33332110f1
revert: "fix(browse): Recycle on adapter clear"
Still unclickable
2024-12-18 12:07:03 +07:00
659ba89218
fix(browse): Recycle on adapter clear
Take 2, but this time don't override onDetachFromRecyclerView
2024-12-18 11:37:30 +07:00
02296985d6
chore(chapter): No longer set last_update as newest chapter's
date_upload

Grab this info from SQL instead
2024-12-18 11:29:55 +07:00
b3e69bdb28
fix(library): Sort by latest chapter is not working properly
`last_update` is now when entry chapter list is changed instead of when is
the latest entry uploaded. It is now replaced by LibraryManga's
latestUpdate

Fixes GH-309
2024-12-18 11:25:33 +07:00
6c40fe92be
chore(release): v1.9.6 2024-12-18 06:04:15 +07:00
18c5a68981
fix(manga): Fix crashes 2024-12-18 06:00:27 +07:00
cba97eb94d
revert: "fix(browse): Recycle on adapter clear"
Nvm, that caused the item to be unclickable :^)
2024-12-17 20:13:09 +07:00
0d205f4a6d
fix(browse): Recycle on adapter clear 2024-12-17 19:34:35 +07:00
fcaff92db1
refactor(browse): Setup flow from presenter 2024-12-17 19:18:17 +07:00
5fad2c0154
chore: Bump version to 1.9.6 for nightly and beta 2024-12-17 11:43:30 +07:00
bbfa8c8bc4
chore(release): v1.9.5 2024-12-17 11:32:46 +07:00
8020f6591c
docs: Remove todo note
Doesn't seem to have any leaks...
2024-12-17 11:28:22 +07:00
a404eb2c83
docs: Sync changelog 2024-12-17 09:06:00 +07:00
8c9208c3b6
chore(manga): Always try to refresh if it's a local entry 2024-12-17 09:03:44 +07:00
2dfc1e3451
refactor(manga): Split refreshAll to fetchMangaFromSource and fetchChaptersFromSource 2024-12-17 08:47:42 +07:00
4ffb0ad8ee
revert: Revert flow usage commits
Too much headache...
2024-12-17 08:19:23 +07:00
4a0f578211
fix(manga): Group currentManga setup together 2024-12-17 08:09:09 +07:00
60fe907cc0
fix(manga): Loading state 2024-12-17 07:56:36 +07:00
f81be429df
refactor(manga): Slowly using flow attempt 2 2024-12-17 07:33:24 +07:00
f8d74a6b2f
revert: "refactor(manga): Slowly using flow"
I'll just redo this in the morning
2024-12-16 20:55:15 +07:00
2ef1195a90
refactor(manga): Slowly using flow 2024-12-16 20:43:37 +07:00
50cad86c0c
chore(globalsearch): Save initial mangaId just in case 2024-12-16 12:22:21 +07:00
8ec29bf755
fix(globalsearch): Update favorite state in real time 2024-12-16 11:45:08 +07:00
3651c2a853
fix(browse): Update favorite state in real time 2024-12-16 11:17:29 +07:00
2461b93af5
docs: Sync changelog 2024-12-16 09:44:01 +07:00
301acb9f4d
fix(history): Remove unnecessary JOIN statement 2024-12-16 09:12:49 +07:00
4fc18b4913
fix(recents): Missing ON statement 2024-12-16 09:00:40 +07:00
e5b8ed9e9d
fix(recents): Find unread from SQL instead of code 2024-12-16 08:27:17 +07:00
263603616e
fix: Don't target iOS
We're not doing this anytime soon
2024-12-15 21:22:46 +07:00
6c1d8d5011
revert "refactor: Some multiplatform bs" 2024-12-15 21:19:28 +07:00
247ed3bca7
revert: "revert: "revert: "fix(sql): Use UNION ALL instead of UNION"""
That only made it worse...
2024-12-15 20:24:26 +07:00
c7b6e8ee00
revert: "revert: "fix(sql): Use UNION ALL instead of UNION""
This reverts commit 37535d3bcf.
2024-12-15 19:53:43 +07:00
d99476f9bf
fix(chapter): Check if url already managed or not 2024-12-15 10:29:01 +07:00
834958a819
fix(chapter): Distinct by url again 2024-12-15 09:22:16 +07:00
1bbfd97b30
fix: Downgrade dependency me.zhanghai.android.libarchive:library to v1.1.2
Causing segmentation fault on some devices
2024-12-15 09:16:17 +07:00
59a8bfc7aa
revert: "chore: Partial revert"
This reverts commit abcf06b921.
2024-12-14 20:05:55 +07:00
93e5effaaf
chore: Bump version to 1.9.5 for nightly and beta 2024-12-14 19:55:09 +07:00
0f54d5e59d
sync: Sync project 2024-12-14 19:54:43 +07:00
316bc87a5c
chore(release): v1.9.4 2024-12-14 19:45:47 +07:00
82b73bce76
fix(chapter): A mishap 2024-12-14 19:43:22 +07:00
71a9e2493b
fix(chapter): A mishap 2024-12-14 19:39:13 +07:00
abcf06b921
chore: Partial revert 2024-12-14 19:20:23 +07:00
e604c951ed
refactor: ifnull no longer needed 2024-12-14 19:02:10 +07:00
62e26d1f38
refactor: Simplify code and move stuff around 2024-12-14 18:46:49 +07:00
ee2acf8b98
fix: Delete duplicate chapters 2024-12-14 14:37:34 +07:00
8c5b54df5f
fix(library): Handle multiple header 2024-12-14 13:29:18 +07:00
renovate[bot]
00aa93d189
chore(deps): Update kotlin monorepo to v2.1.0 (#291)
* chore(deps): Update kotlin monorepo to v2.1.0

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-12-14 11:49:47 +07:00
bfee1de3b1
docs: Sync changelog 2024-12-14 11:06:50 +07:00
renovate[bot]
fbe9760616
chore(deps): Update plugin gradle-versions to v0.51.0 (#303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-14 11:06:18 +07:00
renovate[bot]
86f5e743e1
chore(deps): Update plugin kotlinter to v5 (#304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-14 11:05:26 +07:00
8f45148a9e
docs: Sync changelog 2024-12-14 10:43:53 +07:00
renovate[bot]
09621111bf
fix(deps): Update dependency androidx.compose:compose-bom to v2024.12.01 (#296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-14 10:43:32 +07:00
5fe72d4cb5
chore: Bump version to 1.9.4 for nightly and beta releases [skip ci] 2024-12-14 10:26:52 +07:00
5b637fae8f
chore(release): v1.9.3 2024-12-14 09:56:49 +07:00
336579bd35
docs: Fix changelog 2024-12-14 09:51:43 +07:00
d61052485f
fix: Use collect instead of collectLatest 2024-12-14 09:33:24 +07:00
Ahmad Ansori Palembani
16316d810b
refactor: Replace DownloadQueue with Flow (#301)
* refactor: Replace DownloadQueue with Flow

* fix: Remove DownloadQueue leftover

* fix: Download progress not progressing

* chore: Remove unnecessary downloadStatusChange trigger

Already handled by MainActivity

* fix: Chapter download state stuck in CHECKED

* fix: Chapter download state stuck in QUEUE on deletion

* fix: A regression, download progress not progressing

* refactor: Remove rx usage

* docs: Sync changelog
2024-12-14 09:17:50 +07:00
37535d3bcf
revert: "fix(sql): Use UNION ALL instead of UNION"
This reverts commit 39775ea308.
2024-12-12 21:53:53 +07:00
39775ea308
fix(sql): Use UNION ALL instead of UNION 2024-12-12 21:40:33 +07:00
1c996f9a59
docs: Sync changelog 2024-12-12 11:20:23 +07:00
ea04968581
refactor(ChapterSourceSync): Simplify code and insert new chapters in
bulk
2024-12-12 11:00:46 +07:00
365875590f
chore: Bump version to v1.9.3 for beta and nightly 2024-12-11 20:23:42 +07:00
1bc107f26b
chore(release): v1.9.2 2024-12-11 20:17:14 +07:00
eeb572740a
style(AppUpdateNotifier): Fix consistency 2024-12-11 19:53:34 +07:00
dbf5a7efcd
fix(reader): Some desync issue causing download's remove after read to
not work properly
2024-12-11 19:33:18 +07:00
b4e3dcfdda
refactor(reader): Replace rx with kotlin coroutine 2024-12-11 19:21:41 +07:00
8e7f5d8897
style(chapter): Adjust contrast 2024-12-11 11:22:42 +07:00
87c13b44ab
chore: Bump version to v1.9.2 for beta and nightly [skip ci] 2024-12-10 13:44:23 +07:00
8c8b2f9634
chore(release): v1.9.1 2024-12-10 13:31:03 +07:00
88959d956f
fix(cover): Recycle bitmap after compression 2024-12-10 12:52:51 +07:00
53f8a37c8e
chore: Bump version to v1.9.1 for beta and nightly 2024-12-10 12:14:36 +07:00
05b20e00e0
fix(deps): Downgrade dependency org.conscrypt:conscrypt-android to v2.5.2
REF: https://github.com/google/conscrypt/issues/1268
2024-12-10 12:05:55 +07:00
158049d4f4
docs: Sync changelog 2024-12-10 10:31:09 +07:00
119b1c64b2
fix(source/local): Don't crash trying to get manga language
XML is pain
2024-12-10 10:29:28 +07:00
1cb635999e
docs(sql): Documentate this insanity of a query [skip ci] 2024-12-10 09:34:36 +07:00
160a7109da
ci: Don't leave changelog empty 2024-12-10 09:30:07 +07:00
618109c80e
docs: Sync changelog 2024-12-10 08:10:07 +07:00
renovate[bot]
9918c407c8
fix(deps): Update fast.adapter to v5.7.0 (#287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:09:55 +07:00
renovate[bot]
d780d6ceb1
fix(deps): Update dependency androidx.glance:glance-appwidget to v1.1.1 (#285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:09:32 +07:00
renovate[bot]
ee52e6ecf7
fix(deps): Update dependency com.google.firebase:firebase-bom to v33.7.0 (#286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:08:45 +07:00
renovate[bot]
4cdcf62351
fix(deps): Update dependency androidx.constraintlayout:constraintlayout to v2.2.0 (#284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:08:05 +07:00
renovate[bot]
1504d94f52
fix(deps): Update dependency androidx.annotation:annotation to v1.9.1 (#283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:07:42 +07:00
renovate[bot]
e1bf13f1d9
fix(deps): Update voyager to v1.1.0-beta03 (#282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:07:13 +07:00
renovate[bot]
c6f6718d30
fix(deps): Update dependency org.jsoup:jsoup to v1.18.3 (#281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-10 08:06:56 +07:00
renovate[bot]
41319660f6
fix(deps): Update dependency io.github.kevinnzou:compose-webview to v0.33.6 (#280)
* fix(deps): Update dependency io.github.kevinnzou:compose-webview to v0.33.6

* fix: Namespace change

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-12-10 08:06:32 +07:00
6935ae545c
chore(deps): Uncomment leakcanary on debug build 2024-12-10 07:55:06 +07:00
fe59d7f4ec
chore(deps): Add leakcanary to debug memleaks 2024-12-10 07:43:12 +07:00
e45baf6ab4
revert(recents): Filter scanlator
I forgot to turn this back on
2024-12-09 22:16:35 +07:00
d3c98fb897
fix(recents): Can't open chapters from Grouped and All
id column name for mangas and chapter is both _id causing it conflict when doing 'Rn.*'. In fact, 'Rn.*' is not even needed for union, it just needs to be on the same order, same type, and have the same number of columns.
2024-12-09 21:57:17 +07:00
22978ab8bf
refactor(recents): Some adjustments 2024-12-09 20:20:12 +07:00
07ed81454f
refactor(reader): Remove runBlocking usage from ViewModel 2024-12-09 19:03:25 +07:00
6c111a1247
docs(changelog): Fix typo [skip ci] 2024-12-09 08:34:27 +07:00
c357f6f658
chore: Sync project 2024-12-09 08:31:26 +07:00
06c7cc7d17
chore(release): Sync changelog for v1.9.0 release 2024-12-09 08:10:33 +07:00
4c30881e91
docs: Sync changelog 2024-12-09 06:30:08 +07:00
renovate[bot]
6bd2f0ab9a
chore(deps): Update moko to v0.24.4 (#203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 06:29:56 +07:00
2649425259
chore(coil): Remove commented code [skip ci]
It's used to compress custom cover instead
2024-12-08 13:46:10 +07:00
1d200f426c
fix(manga): Compress custom cover to not exceed 4092px 2024-12-08 12:24:51 +07:00
349b9c181a
fix(library): Include default category in allCategories 2024-12-08 09:37:10 +07:00
d7c3aa6b45
fix(manga): Move more stuff to UI thread 2024-12-08 08:28:20 +07:00
823860a56f
chore(manga): Try not to use GlobalScope as much as possible 2024-12-08 08:09:37 +07:00
28cbf0b988
fix(manga): Missing "withUIContext" 2024-12-08 08:07:26 +07:00
2f8ae26a83
refactor(manga): Removing runBlocking 2024-12-08 07:53:06 +07:00
f114320123
fix: Missing import 2024-12-08 07:03:13 +07:00
a4ab7f11e2
fix(reader): Prevent potential NPE 2024-12-08 06:47:02 +07:00
8bc22fec28
revert: "revert(reader): Revert setMaxTileSize to use GL's max texture size"
This reverts commit c9eb3023ed.
2024-12-07 15:21:35 +07:00
c9eb3023ed
revert(reader): Revert setMaxTileSize to use GL's max texture size 2024-12-07 15:15:48 +07:00
0d264026cf
docs: Sync changelog and add fixme note 2024-12-07 13:36:01 +07:00
b3fbc0bf39
refactor: Port upstream's download cache system 2024-12-07 13:13:08 +07:00
MajorTanya
c66bf9b280
fix: Always use software bitmap on certain devices 2024-12-07 07:04:27 +07:00
2c36be8b8f
fix: Actually keep 5 log files 2024-12-06 18:51:46 +07:00
5396b0408e
chore: Only keep 5 log files and 5 rolled log files 2024-12-06 07:51:51 +07:00
332f3f7ee6
refactor: Separate isHardwareThresholdExceeded from isMaxTextureSizeExceeded 2024-12-05 14:49:52 +07:00
23db3244ce
docs: Sync changelog 2024-12-05 14:12:00 +07:00
renovate[bot]
4c74e4e78b
chore(deps): Update agp to v8.7.3 (#276)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 14:11:45 +07:00
renovate[bot]
ffac6293f0
fix(deps): Update dependency org.conscrypt:conscrypt-android to v2.5.3 (#275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 14:11:25 +07:00
6bb2f5f94e
ci: Adjust concurrency 2024-12-05 12:06:44 +07:00
3b9eb8a30a
ci: Always exit 0 2024-12-05 12:05:25 +07:00
0406160452
ci: Push to mirror repo with github actions 2024-12-05 12:03:18 +07:00
cd978388b2
chore: Adjust fixme note [skip ci]
Now that I think about it, moving to AndroidX paging is useless, we need to move to Compose to fully fix the desync issue
2024-12-04 18:00:29 +07:00
2e12817735
refactor(browse): Move stuff around 2024-12-04 17:30:02 +07:00
cb265d2225
fix(browse): Restart pager from controller
Hopefully fix desync issue after opening an entry
2024-12-04 16:58:24 +07:00
39d891aa88
docs: FIXME note 2024-12-04 12:07:16 +07:00
106737371f
chore: Flow version of firstOrNull 2024-12-04 12:05:19 +07:00
19ea7cbebd
fix(reader): Fix potential NPE 2024-12-04 06:01:14 +07:00
6df9e4f745
fix: Set max bitmap size for covers 2024-12-04 05:24:45 +07:00
b4c6820ca4
chore(deps): Update dependency io.coil-kt.coil3:coil-bom to v3.0.4 2024-12-04 05:17:30 +07:00
11ef447321
fix(library): Only retry if it's NPE 2024-12-03 17:09:47 +07:00
ca982d93d1
fix: Missing import 2024-12-03 17:02:22 +07:00
50fecd5350
fix(library): Workaround NPE when querying library by retrying once
REF: https://github.com/sqldelight/sqldelight/issues/4194#issuecomment-2417466333
2024-12-03 17:00:41 +07:00
31773876b4
docs: Sync changelog 2024-12-03 16:42:38 +07:00
30d7b389a5
fix: Fix Komga unread count (again) 2024-12-03 16:22:44 +07:00
723abbe520
fix(CategoryPresenter): Run setCategories on Main thread
Fixes GH-273
2024-12-03 15:00:05 +07:00
8c96e8d4b6
docs: Sync changelog 2024-12-03 13:46:01 +07:00
d2fdbf8717
fix(DownloadQueue): Use CopyOnWriteArrayList to further prevent ConcurrentModificationException 2024-12-03 08:40:36 +07:00
ea9407b49d
fix(DownloadQueue): Fix ConcurrentModificationException 2024-12-03 07:33:03 +07:00
b9f7d18d3d
fix(library): NPE 2024-12-03 07:01:07 +07:00
fbb4112eac
fix(ExtensionInstallerJob): Don't crash when request list is empty 2024-12-03 06:56:00 +07:00
7f05d16039
fix(sqldelight): Custom query function
Basically the same as "executeAsOneOrNull" but without the result count
check
2024-12-02 13:20:55 +07:00
2fd6146d32
sync: Sync project 2024-12-02 09:25:28 +07:00
737681173c
test: Test if major and minor version bump is checked properly 2024-12-02 09:25:01 +07:00
29aa80104d
refactor(version): Better version comparator 2024-12-02 09:05:27 +07:00
d7e3a970d8
chore: Bump version to v1.9.0
Preparing for release
2024-12-02 07:36:56 +07:00
bd16db8823
chore: FIXME note [skip ci] 2024-12-01 09:58:40 +07:00
2b639d0630
revert: "refactor(library): Some adjustments"
This reverts commit 9276382c12.
2024-12-01 09:21:13 +07:00
1738dfb510
fix(library): Force update library on create 2024-12-01 09:06:18 +07:00
ef8b81409e
chore: Bump versionCode to prepare v1.9.0 release 2024-12-01 08:53:15 +07:00
60c5e3f5c1
chore: Hide danger zone on prod build 2024-12-01 08:42:16 +07:00
6c8ed6dc57
docs: Sync changelog 2024-12-01 08:29:10 +07:00
AntsyLich
d800f183e7
chore: Bump default user agent 2024-12-01 08:27:22 +07:00
f7450b8d17
fix(library): Re-introduce force UI update on certain actions
Those actions are not yet handled via flow
2024-12-01 08:18:18 +07:00
9276382c12
refactor(library): Some adjustments
- fetchLibrary -> forceUpdateEvent
- getLibrary -> updateLibrary
- updateManga -> updateLibrary
- Add some todo notes
- Remove old todo notes
- Update UI on categories collapsed instead of relying on forceUpdateEvent
- Remove unnecessary force UI update
2024-12-01 08:11:33 +07:00
598c4918f1
fix: Revert to use fetchLibrary flow 2024-12-01 06:19:19 +07:00
efea0103fe
fix(library): Category collapse should trigger UI update
Fixes GH-271
2024-12-01 05:48:51 +07:00
19b1ba76c7
chore: Some clean up 2024-11-30 17:46:29 +07:00
Ahmad Ansori Palembani
ec36c9faf7
refactor(library): Use flow to retrieve library (#159)
* refactor(library): Use flow to retrieve library

* fix(library): Use fetchLibrary in getLibraryFlow
2024-11-30 17:30:19 +07:00
a940722f2b
chore(db): Remove StorIO related stuff 2024-11-30 17:27:20 +07:00
844a83b4e1
docs: Sync changelog
Now I just need to remove all the StorIO junk from the codebase...
2024-11-30 09:10:18 +07:00
bfbc1f6742
chore(db): Clean up unused StorIO relics 2024-11-30 09:08:43 +07:00
7a20537cf9
fix: Category data is not migrated 2024-11-30 08:40:15 +07:00
07c1e7e67f
refactor(db): Fully migrated from StorIO to SQLDelight 2024-11-30 08:22:16 +07:00
a832e4d6dc
fix: current is not supposed to be there 2024-11-29 20:17:07 +07:00
393ccb822e
fix: Another potential NPE 2024-11-29 19:40:35 +07:00
42b3dce0ef
fix: NPE 2024-11-29 19:24:39 +07:00
e8054855ac
refactor(db): Migrate mangaCategory queries (that can be migrated) to SQLDelight 2024-11-29 19:11:29 +07:00
726613e6d7
refactor(db): Migrate track queries (that can be migrated) to SQLDelight 2024-11-29 18:02:45 +07:00
93ec13f324
refactor(db): Migrate last manga queries (that can be migrated) to SQLDelight 2024-11-29 17:30:43 +07:00
17b07cd836
refactor(db): Migrate last history queries (that can be migrated) to SQLDelight 2024-11-29 17:05:59 +07:00
8cef33d9c6
refactor(stats): Remove some runBlocking usage 2024-11-29 15:21:10 +07:00
e25f330118
refactor(db): Migrate getChapters query to SQLDelight 2024-11-29 15:08:42 +07:00
83cb898068
refactor(db): Migrate deleteTrack query to SQLDelight 2024-11-29 14:44:13 +07:00
333a7eea68
refactor(db): Migrate insertManga queries to SQLDelight 2024-11-29 14:35:41 +07:00
aae9a68c8b
refactor(db): Migrate getManga queries to SQLDelight 2024-11-29 14:21:16 +07:00
312f9e197b
chore(db): Disable search_metadata 2024-11-29 13:58:17 +07:00
da1f60c5c5
refactor(db): Migrate clear database queries to SQLDelight 2024-11-29 13:46:37 +07:00
cf06ebdb8b
fix(library): Accidentally swap global open random manga with non-global 2024-11-29 13:13:21 +07:00
a4a7f8fb6d
refactor(db): Migrate last category queries (that can be migrated) to SQLDelight 2024-11-29 12:08:17 +07:00
663360c283
refactor(db): Migrate some more category queries to SQLDelight part 2 2024-11-29 12:02:18 +07:00
ca41e02fe1
refactor(db): Migrate some more category queries to SQLDelight 2024-11-29 11:28:38 +07:00
9c40aadca2
fix: Fix build 2024-11-29 10:29:12 +07:00
fa846d68e2
refactor(db): Migrate most getCategoriesForManga queries to SQLDelight 2024-11-29 10:24:03 +07:00
87bd36d025
refactor(db): Migrate GetCategories to SQLDelight 2024-11-29 09:52:02 +07:00
97eacbbaea
refactor(db): Handle the addition from SQL directly instead from code 2024-11-29 08:07:01 +07:00
renovate[bot]
39428f8c79
chore(deps): Update agp to v8.7.2 (#249)
* chore(deps): Update agp to v8.7.2

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2024-11-28 21:37:03 +07:00
445395200a
fix: Make it case-insensitive 2024-11-28 21:26:40 +07:00
291d4ffc35
fix(db): Fix incorrect query 2024-11-28 21:12:46 +07:00
c183802096
refactor(db): Migrate GetTrack to SQLDelight 2024-11-28 20:53:26 +07:00
MajorTanya
e9a68f661f
fix: Add weird Honor app to list of invalid browsers 2024-11-28 11:25:24 +07:00
97e72e3b4b
docs: Sync changelog [skip ci] 2024-11-27 14:23:47 +07:00
5d2a08f2b0
chore(backup/create): Remove fixme note [skip ci]
The issues I was having with auto backup was caused by my device's Power
Saving, unfortunately I don't think there's a way around it...
2024-11-27 13:47:49 +07:00
34818eb7de
fix(settings/general): Incorrect sorting 2024-11-27 13:37:34 +07:00
65682bb5bd
refactor(locale): Simplify locale list parsing 2024-11-27 13:24:44 +07:00
0c6f86c1ae
fix(webview): Add imePadding 2024-11-26 23:04:13 +07:00
ba837c75e7
fix(webview): Don't try to get binding 2024-11-26 22:41:29 +07:00
arkon
5e84586ff5
refactor(webview): Replace WebView with its Compose counterpart
Co-authored-by: null2264 <palembani@gmail.com>
2024-11-26 22:26:33 +07:00
a199ff326d
refactor(db): Replace deleteHistoryNoLastRead StorIO with SQLDelight eqv 2024-11-26 09:05:36 +07:00
f14118a8c1
chore: Clean up 2024-11-26 08:50:16 +07:00
128e14882d
refactor(source/local): Convert to list after .map() 2024-11-26 08:42:33 +07:00
b8a2a4de47
chore(source/local): Create local dir when possible 2024-11-26 08:17:28 +07:00
7df619ac13
fix(source/local): Double orEmpty 2024-11-25 14:49:18 +07:00
8f9194c4a9
feat(source/local): Scan external storage for entries (GH-197)
An experimental feature that allow user to store local entries on `/storage/sdcard/Android/data/<yokai>/local/`
2024-11-25 14:44:57 +07:00
f9bfb0b423
refactor(DiskUtil): Simplify code 2024-11-25 13:18:37 +07:00
4f9e5bfe62
refactor(db): Replace some more StorIO queries with SQLDelight 2024-11-24 19:08:42 +07:00
nonproto
e9d22f4dba
fix: Up the delay for tryToSetForeground 2024-11-24 10:32:14 +07:00
27f4cea4c4
docs: Sync changelog 2024-11-24 08:55:10 +07:00
renovate[bot]
ed23afa32d
chore(deps): Update mikepenz/action-junit-report action to v5 (#264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-24 08:54:45 +07:00
renovate[bot]
4c4163f270
chore(deps): Update dependency gradle to v8.11.1 (#263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-24 08:54:25 +07:00
6e76ab125e
fix(chapter): Read state is not saved 2024-11-23 20:37:29 +07:00
00b7ac6c25
sync: Sync project 2024-11-23 20:14:40 +07:00
bf7c7f79cb
revert: "fix(chapter): Scanlator is nullable"
This reverts commit 0f44474596.
2024-11-23 20:10:08 +07:00
5378d2a99b
refactor(db): Replace some more StorIO queries with SQLDelight
- Manga: db.getManga -> getManga.await*
- History
- Chapter: db.updateChapter* -> updateChapter.await; db.getChapter -> getChapter.await*
2024-11-23 19:28:59 +07:00
822cfa56a6
refactor(backup): Inject storageManager inside getAutomaticBackupLocation func 2024-11-23 08:12:26 +07:00
a7a6dc96d3
sync: Sync project 2024-11-23 07:46:03 +07:00
060b40e59d
fix: Crashes cause by Bangumi invalid status 2024-11-23 07:44:54 +07:00
d7160db53a
fix: Add modified version of RollingFileLogWriter 2024-11-22 19:02:38 +07:00
5fa5815541
fix: Some attempt 2024-11-22 17:04:08 +07:00
cea9da9c6d
fix: Disable log file for now 2024-11-22 15:46:22 +07:00
53ea5bafee
fix: Log file is not being created 2024-11-22 15:31:15 +07:00
b1766ebb94
feat: Write logs to file 2024-11-22 12:57:52 +07:00
5d6d25c261
docs: Sync changelog 2024-11-22 11:10:25 +07:00
renovate[bot]
5735003018
fix(deps): Update lifecycle to v2.8.7 (#261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 11:09:59 +07:00
renovate[bot]
b6edf17860
fix(deps): Update junit5 monorepo to v5.11.3 (#260)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 11:08:48 +07:00
renovate[bot]
8063cb2edb
fix(deps): Update dependency co.touchlab:kermit to v2.0.5 (#259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-22 11:07:35 +07:00
Cuong-Tran
350c5f35dd
fix: Fix app update error notification disappearing 2024-11-22 07:56:29 +07:00
95504d7582
chore(reader): Adjust error handler for webtoon too 2024-11-21 14:00:03 +07:00
AntsyLich
9322836e48
refactor(reader): Improve hardware bitmap threshold option 2024-11-21 13:50:09 +07:00
37ba0634ac
docs: Some note [skip ci] 2024-11-21 13:31:11 +07:00
cdea102fcb
fix: Deprecated function 2024-11-21 11:48:23 +07:00
d25a857b7a
feat(reader): Debug mode 2024-11-21 11:31:49 +07:00
b1f2c30892
chore(settings): Add "danger zone" category 2024-11-21 10:28:29 +07:00
d163dc500c
fix: Revert the check for now 2024-11-21 00:06:15 +07:00
1f17965d24
fix: Wrong value 2024-11-20 23:50:49 +07:00
AntsyLich
fd73958923
feat: Add option to lower the threshold for hardware bitmaps 2024-11-20 23:31:53 +07:00
27002a20ef
docs: FIXME note [skip ci] 2024-11-20 22:26:01 +07:00
d003e85b6b
fix: Regression caused by 01fcd7d122 2024-11-20 22:19:41 +07:00
74168c0f1d
fix: Wrong variable name 2024-11-20 22:02:14 +07:00
AntsyLich
01fcd7d122
fix: Switch to hardware bitmap in reader only if device can handle it 2024-11-20 21:56:00 +07:00
fbe2d8c701
refactor(metadata): Simplify code 2024-11-20 20:20:39 +07:00
a2d6ac2a8b
docs: Add missing credit [skip ci] 2024-11-20 18:25:02 +07:00
AntsyLich
736b4e6b68
fix: Handle Android SDK 35 API collision 2024-11-20 14:32:08 +07:00
78df24e566
docs: Add missing credit [skip ci] 2024-11-20 14:24:11 +07:00
renovate[bot]
fcc234f0ab
fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.3 (#257)
* fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.3

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-11-20 14:15:50 +07:00
renovate[bot]
5996a8a863
fix(deps): Update shizuku to v13 (major) (#256)
* fix(deps): Update shizuku to v13

* fix: Use reflection to fix shizuku

* docs: Sync changelog [skip ci]

* fix: Ignore shizuku's minSdk

We have desugaring enabled

* fix: Missing import

* fix: Move newProcess declaration

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jobobby04 <jobobby04@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-11-20 14:02:15 +07:00
Redjard
fa0e565fa5
fix: Fix shizuku being buggy for multi user setups
Fetch the current userid separately because shizuku always runs as the main user and would otherwise install and update for the main user
2024-11-20 13:26:47 +07:00
fabc9f4c1b
docs: Sync changelog 2024-11-20 13:19:46 +07:00
renovate[bot]
14efd1b96d
fix(deps): Update xml.serialization to v0.90.3 (#254)
* fix(deps): Update xml.serialization to v0.90.3

* fix: Fix build

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
2024-11-20 13:19:31 +07:00
6affec4b97
chore(config): Revert "Enable fork processing"
This reverts commit 52ba1b02d3.
2024-11-20 13:06:26 +07:00
renovate[bot]
7355ec841a
chore(config): migrate config .renovaterc.json5 (#255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 13:05:25 +07:00
52ba1b02d3
chore(renovate): Enable fork processing 2024-11-20 12:57:18 +07:00
54b27a5efa
docs: Sync changelog 2024-11-20 12:43:22 +07:00
renovate[bot]
5a12fc3670
fix(deps): Update activity to v1.9.3 (#245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:43:09 +07:00
renovate[bot]
87d803812a
fix(deps): Update dependency io.coil-kt.coil3:coil-bom to v3.0.3 (#246)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:42:55 +07:00
renovate[bot]
0df6647324
fix(deps): Update dependency io.mockk:mockk to v1.13.13 (#247)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:42:42 +07:00
renovate[bot]
9d49957d8d
fix(deps): Update dependency me.zhanghai.android.libarchive:library to v1.1.4 (#248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:42:25 +07:00
renovate[bot]
f5dce81b64
fix(deps): Update dependency androidx.compose:compose-bom to v2024.11.00 (#250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:42:14 +07:00
renovate[bot]
59193076f1
fix(deps): Update dependency androidx.core:core-ktx to v1.15.0 (#251)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:33:34 +07:00
renovate[bot]
53498c78c6
fix(deps): Update dependency androidx.work:work-runtime-ktx to v2.10.0 (#252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:33:08 +07:00
renovate[bot]
392e2895a3
fix(deps): Update dependency com.google.firebase:firebase-bom to v33.6.0 (#253)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 12:32:57 +07:00
6d470c0314
docs: Sync changelog 2024-11-20 11:51:06 +07:00
renovate[bot]
2a49f2437b
chore(deps): Update kotlin monorepo to v2.0.21 (#244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-20 11:50:56 +07:00
6dc5c1e608
fix: Fix build 2024-11-20 11:40:27 +07:00
MajorTanya
7ee44f1fe5
feat(tracker): Some improvements to Bangumi tracker search
In short:
- fetch & show actual summary
- fallback to "name" if "name_cn" is empty
- request larger responseGroup to get & display the summary & rating
- add type filter query param to make Bangumi filter, not us

Previously, we only displayed the "name" in the summary area and used
"name_cn" as the entry name. However, "name_cn" (Chinese name) can be
an empty string at times, resulting in an awkward looking search
result list where some, many, or even all the results have no title
displayed and only show the "name" (Japanese name) in the summary
area. This has been solved by using "name" as a fallback value should
"name_cn" be empty.

If a Chinese name is available, the original name is prepended to the
summary with the addition "作品原名:" (meaning "original series title").

By using the "responseGroup=large" query parameter, we can request
the required data we need to display the actual summary for an entry
and the entry's average rating.
The "name" is prepended to the summary contents, if any exist, so it
is still accessible for series identification if a "name_cn" exists
too and was used for the result title.

Adding the "type=1" filter query parameter means Bangumi will only
return entries of type 1 ("book") instead of all types and Mihon
needing to filter, resulting in potentially missed entry matches.
2024-11-20 11:32:06 +07:00
cd3e526fdf
chore: Compile the app against SDK 35 2024-11-20 11:26:49 +07:00
76de509ae5
chore(reader): Adjust error handler 2024-11-20 08:14:25 +07:00
dbdafafba7
sync: Sync project 2024-11-19 08:46:13 +07:00
e27c527ad9
fix(chapter): Scanlator is nullable 2024-11-19 08:25:55 +07:00
0a7a65aa33
fix(GH-228): Fixed filtered scanlator not working properly 2024-11-19 08:17:53 +07:00
d832f8ce46
fix(manga/details): Only update chapter
Since manga update already handled by `MangaUtil.setScanlatorFilter`
2024-11-19 07:45:40 +07:00
cae3edd77b
fix(manga/details): Made some tasks non cancellable 2024-11-19 07:35:41 +07:00
c2db3f959f
docs: Sync changelog 2024-11-18 14:12:30 +07:00
8469fe2535
fix(metadata): Only use characters that supported by XML 1.0
Fixes GH-242

REF: https://www.w3.org/TR/xml/#charsets
2024-11-18 14:00:04 +07:00
dceac33b7c
chore: Testing setToForeground on auto backup (again) 2024-11-14 09:13:21 +07:00
231ece7bfb
refactor(backup): Manage options from BackupCreator 2024-11-04 13:46:30 +07:00
ca7496bda1
refactor(backup): Remove unnecessary function 2024-11-04 13:22:02 +07:00
30726d030c
docs: Sync changelog 2024-10-13 19:57:17 +07:00
6e87bee93c
fix: Fix build 2024-10-13 19:33:34 +07:00
71e7545b29
refactor(backup): Map chapter directly into BackupChapter 2024-10-13 19:20:34 +07:00
5160fabfde
refactor(backup): Remove broken sources from backup
Doesn't seem to be used, relic from the past?
2024-10-13 19:12:22 +07:00
193d50d0d6
refactor(backup): Retrieve categories with SQLDelight 2024-10-13 19:10:56 +07:00
9b45767667
refactor(backup): Retrieve manga with SQLDelight 2024-10-13 19:09:15 +07:00
ad070fd59a
feat: Open random series from global list 2024-10-13 18:50:12 +07:00
e4cc6505dd
docs(CHANGELOG): Don't prefix version with 'v' [skip ci] 2024-10-12 08:34:46 +07:00
fe2bdc3846
chore: Clean up log 2024-10-12 08:13:45 +07:00
6ba22b9e45
revert: "Temporarily notify backup error for auto backup"
This reverts commit 1ae32840eb.
2024-10-10 10:24:58 +07:00
1ae32840eb
debug: Temporarily notify backup error for auto backup 2024-10-10 10:02:02 +07:00
15ca197c4e
chore: Don't use generic log message 2024-10-10 09:32:47 +07:00
0ebf79fcb0
docs: Sync changelog 2024-10-09 07:36:52 +07:00
renovate[bot]
75c7431b36
fix(deps): Update shizuku to v12.2.0 (#223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 07:36:30 +07:00
renovate[bot]
812c467cb4
fix(deps): Update dependency com.google.firebase:firebase-bom to v33.4.0 (#222)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 07:36:17 +07:00
renovate[bot]
9bda841d98
fix(deps): Update junit5 monorepo to v5.11.2 (#221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 07:36:04 +07:00
renovate[bot]
d8eb9cc6c1
fix(deps): Update dependency androidx.compose:compose-bom to v2024.09.03 (#220)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 07:35:51 +07:00
c2f07fbfc0
chore: Try to set backup creator to foreground for manual backup 2024-10-07 11:36:50 +07:00
54fdec2fc3
chore: Sync project 2024-10-06 16:13:44 +07:00
7fe5e9a767
fix: Backup progress is never shown 2024-10-06 16:09:30 +07:00
f6e5a6c712
fix: Re-enable file picker for creating backup 2024-10-06 15:49:30 +07:00
f4bf249477
revert: "Simplify backup code"
This reverts commit 58c5a17c50.
2024-10-06 15:48:31 +07:00
Secozzi
a33a167e2c
fix: Fix AniList ALSearchItem.status nullibility 2024-10-06 11:08:08 +07:00
04d0003963
chore: Sync project [skip ci] 2024-10-02 10:30:48 +07:00
5ba3600c28
docs: Sync changelog 2024-09-29 09:55:31 +07:00
renovate[bot]
956da54722
fix(deps): Update dependency androidx.webkit:webkit to v1.12.0 (#214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-29 09:54:44 +07:00
renovate[bot]
26af20f067
fix(deps): Update dependency io.mockk:mockk to v1.13.12 (#213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-29 09:54:27 +07:00
badfd75ebb
fix(renovate): Fix "invalid JSON" 2024-09-29 09:48:43 +07:00
61b51756c9
fix(renovate): Ignore injekt-koin [skip ci] 2024-09-29 09:46:28 +07:00
f370aa7a83
docs: Update FUNDING.yml 2024-09-29 09:44:57 +07:00
6e567ea732
fix: Fix build 2024-09-28 21:46:44 +07:00
d8f8264b34
fix: Capture self-closing tag with space before slash 2024-09-28 21:20:40 +07:00
AntsyLich
6d15f383d1
refactor: Generate locales_config.xml in build dir 2024-09-28 21:02:31 +07:00
0f96f4dbe1
refactor(backup/creator): Use injectLazy 2024-09-27 06:50:44 +07:00
ec5e86942a
chore(backup/creator): Temporarily disable setForeground 2024-09-27 06:41:57 +07:00
a6f697199d
revert: "Explicitly bind app to Application class"
This reverts commit a8b3d97b14.
2024-09-26 13:35:02 +07:00
renovate[bot]
465564f977
chore(deps): Update dependency gradle to v8.10.2 (#188)
* chore(deps): Update dependency gradle to v8.10.2

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2024-09-26 11:37:33 +07:00
2bb525ca45
docs: Sync changelog 2024-09-26 10:26:06 +07:00
renovate[bot]
fda98a25be
fix(deps): Update junit5 monorepo to v5.11.1 (#210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:25:52 +07:00
renovate[bot]
7ec631ba0b
fix(deps): Update dependency androidx.test.ext:junit to v1.2.1 (#209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:25:39 +07:00
renovate[bot]
c744f35ffc
fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.3.8 (#208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:25:25 +07:00
renovate[bot]
47dbc34fad
fix(deps): Update dependency org.jsoup:jsoup to v1.18.1 (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:25:10 +07:00
renovate[bot]
2b09434837
fix(deps): Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.9.0 (#206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:24:54 +07:00
renovate[bot]
d41c21d128
fix(deps): Update serialization to v1.7.3 (#205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:24:41 +07:00
5325c4c847
docs: Sync changelog 2024-09-26 09:54:05 +07:00
renovate[bot]
334fcdaf58
chore(deps): Update agp to v8.6.1 (#187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 09:53:43 +07:00
6c1dba1831
fix(GestureDetector): Prevent crashes caused by firstEvent being null on some devices 2024-09-26 08:14:43 +07:00
1ca914a792
docs: Sync changelog 2024-09-25 12:13:32 +07:00
renovate[bot]
bade424fb8
fix(deps): Update dependency com.google.firebase:firebase-bom to v33.3.0 (#202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:13:01 +07:00
renovate[bot]
b3aa373621
fix(deps): Update dependency com.squareup.okio:okio to v3.9.1 (#201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:12:46 +07:00
renovate[bot]
1ef5d70150
fix(deps): Update activity to v1.9.2 (#200)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:12:32 +07:00
renovate[bot]
63e7cda7b8
fix(deps): Update lifecycle to v2.8.6 (#199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:12:07 +07:00
renovate[bot]
b1d63bdf66
fix(deps): Update dependency me.zhanghai.android.libarchive:library to v1.1.2 (#198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-25 12:11:54 +07:00
f838dcb243
fix: Don't try to auto backup if battery is too low 2024-09-25 11:51:56 +07:00
dbd007127d
feat: Option to prune finished workers 2024-09-24 08:40:32 +07:00
7719847106
refactor: Simplify code 2024-09-24 06:51:10 +07:00
83a8abe07c
refactor: Extension method for WorkManager 2024-09-24 06:46:29 +07:00
233758b219
fix: Library refresh state didn't update right away 2024-09-24 06:34:32 +07:00
a5a5ce8797
fix(backup/creator): Set backoff criteria 2024-09-24 05:30:35 +07:00
186d95fcf9
fix: Backup failed trying to get foreground info 2024-09-23 07:12:37 +07:00
7752f64efb
chore(deps): Use JetPack's compose BOM
chrisbanes's compose BOM is now deprecated as JetPack now provide their
own Beta and Alpha BOMs for compose

REF: 61b195353c
2024-09-22 12:10:20 +07:00
a8b3d97b14
fix: Explicitly bind app to Application class
Also bound app to Context class
2024-09-21 20:21:42 +07:00
3cd29696d4
chore(deps): Bump injekt-koin revision to aad18b6148 2024-09-20 10:36:59 +07:00
58c5a17c50
refactor(settings/data): Simplify backup code
* Remove picker argument, it's permanently false now
* Simplify backup file path retrieval
* Remove unused code
2024-09-20 08:24:57 +07:00
5d457a7ae5
ci(pr): Unit test for KMM modules [skip ci] 2024-09-20 07:35:38 +07:00
61870c1115
fix(backup/creator): Better error message
The previous error message is way too vague, the actual error is caused
by UniFile unable to retrieve a path or the path didn't lead to a file.
So it's probably safe to assume UniFile failed create an empty backup
file (somehow).
2024-09-20 07:22:52 +07:00
775829e28b
refactor(backup/creator): Some clean up
* Don't try to auto backup if restorer is running
* Return early if file path is null
* Set to foreground before attempting to create a backup
* Added separate function to retrieve auto backup directory instead
  using injectLazy
2024-09-20 07:20:16 +07:00
e248de76d7
refactor: Retrieve auto backup directory from Uri argument 2024-09-20 07:12:49 +07:00
c6c6ed0553
docs: Sync changelog [skip ci]
Everything seems to be working fine even after migrating to Koin.
2024-09-20 07:09:38 +07:00
Ahmad Ansori Palembani
9d858cc810
refactor: Replace Injekt with Koin (Experiment) (#191)
* refactor: Use Koin

An experiment, aims to ditch Injekt and replace it with Koin while providing Injekt API facade for extensions

* fix: Mimic "InjektScope"

* fix: Mimic more classes

Completely fixed source search

* refactor(deps): Use Injekt-Koin library

* fix(r8): Keep Koin
2024-09-19 13:18:02 +07:00
AntsyLich
bc65f17f60
chore(crashlytics): Remove unnecessary permission and disable unnecessary features 2024-09-19 05:45:53 +07:00
19f6b26567
docs: Sync changelog 2024-09-17 18:09:09 +07:00
MajorTanya
33ec0d0f91
fix: Kitsu synopsis nullability
This time, the Kitsu API docs are silent on whether this field (or
any other field) can be null/undefined/etc, but it can happen and
caused an error during search and update. This change just ensures the
attribute is nullable and is set to an empty String when it is null.
2024-09-17 18:07:43 +07:00
Roshan Varughese
0d8276040f
feat: Re-enable fetching chapters list for entries with licenced 2024-09-17 18:06:21 +07:00
NGB-Was-Taken
353a002eb5
chore: Show toast for app restart when User-Agent is changed 2024-09-09 08:09:38 +07:00
6e585fd142
fix(deps): Replace com.github.inorichi.injekt with com.github.null2264.injekt
Potential build failure due to JCenter fully dead and spring.io decided
to restrict their repo from anon access without fully migrating their
old stuff to maven central. Their reasoning is that those libs are
deprecated, ignoring the fact that there still quite a lot of projects
that uses those libs for several reasons... It is what it is, I guess.

REF: https://spring.io/blog/2022/12/14/notice-of-permissions-changes-to-repo-spring-io-january-2023
REF: https://spring.io/blog/2022/12/14/notice-of-permissions-changes-to-repo-spring-io-january-2023#comment-6197778931
2024-09-08 10:18:31 +07:00
44742854c4
docs(README): Copyright symbol [skip ci] 2024-09-07 08:51:36 +07:00
5271d1b66f
docs(README): Link SY and make README formatting more consistent [skip ci] 2024-09-07 08:49:54 +07:00
c4e04c11d0
ci: Fallback to Unreleased before falling back to empty string 2024-09-05 11:07:31 +07:00
ae8959d4d1
docs(CHANGELOG): Include every version changelog
REF: b404a71e26
REF: https://keepachangelog.com/
2024-09-05 10:43:49 +07:00
nzoba
942ca851d5
chore: Use "Page x of y" instead of "x pages left" 2024-09-05 08:33:49 +07:00
38db95b6de
fix(kitsu): It's Int? not String? 2024-09-05 08:11:40 +07:00
34ffc2dd85
chore: Sync project 2024-09-05 07:08:10 +07:00
3ac6c551d6
chore(log/crashlytics): Only send error 2024-09-04 19:53:40 +07:00
renovate[bot]
aed615fe9c
fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.2 (#183)
* fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.2

* docs: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2024-09-04 18:02:46 +07:00
3b427e8b7c
docs: Sync feature list with Discord pinned message [skip ci]
Also explain current state of the fork
2024-09-04 11:27:05 +07:00
b26b526b1e
fix(DiskUtil): Fixed NPE crashes trying to get directory size 2024-09-04 07:24:24 +07:00
bf1f258455
style: Actually set chapter info text color
Apparently it is done programmatically
2024-09-03 11:20:35 +07:00
0ee76b9a50
style: Less contrast chapter info 2024-09-03 10:50:13 +07:00
bede8460e7
docs: Sync changelog 2024-09-03 10:29:25 +07:00
MajorTanya
93738ffaa3
refactor: Use DTOs to parse tracking API responses 2024-09-03 10:09:58 +07:00
Smol Ame
6ee9a41587
chore: Enable 'Split Tall Images' by default 2024-09-03 06:53:45 +07:00
5a1a7063b2
feat: Verbose logging 2024-09-02 09:40:54 +07:00
1925a503d9
refactor(reader): Simplify code 2024-09-01 10:35:04 +07:00
51a5633751
docs: Sync changelog [skip ci] 2024-08-31 22:15:16 +07:00
153268052a
fix(ImageUtil): Throw an exception instead 2024-08-31 21:57:46 +07:00
c1dd80bd71
fix(reader): Return the original image if split bitmap failed 2024-08-31 21:52:45 +07:00
03043ef01b
fix(reader): Don't use coil pipeline if image size can't be extracted 2024-08-31 21:36:57 +07:00
84f4a35180
fix(reader): Abort split attempt if the app can't read bitmap size 2024-08-31 21:11:32 +07:00
59f9e556ec
fix: Prevent NPE crashes on touch event 2024-08-31 12:14:39 +07:00
cb9f8fa398
docs: Sync changelog 2024-08-30 21:30:44 +07:00
renovate[bot]
0363d40f2b
fix(deps): Update dependency com.android.tools:desugar_jdk_libs to v2.1.1 (#178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-30 21:30:31 +07:00
renovate[bot]
a73994b652
chore(deps): Update moko to v0.24.2 (#177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-30 21:30:17 +07:00
08b629d1f5
fix: Actually stop it from showing duplicate entries 2024-08-30 21:12:20 +07:00
70cde33c04
docs: Sync changelog 2024-08-30 20:05:56 +07:00
91a9e081d7
fix(library): Duplicate entries on Ungrouped library
Fixes GH-176
2024-08-30 20:02:00 +07:00
a27c0edf13
refactor(recents): Fully migrate recents to use SQLDelight, for real this time 2024-08-27 13:47:18 +07:00
0d9ffc2206
fix(recents): Partially revert SQLDelight migration
getAllRecentsTypes query is too cursed, will need some refactoring
2024-08-27 10:01:17 +07:00
6e3eaad481
fix(sql): Union issue
It doesn't like history.*
2024-08-27 09:50:32 +07:00
3199f07363
refactor: Some multiplatform bs 2024-08-27 09:35:05 +07:00
a19b767aff
fix(sql): Remove alias 2024-08-27 08:52:17 +07:00
354ed7ce8a
refactor(recents): Fully migrate recents to use SQLDelight 2024-08-27 08:23:11 +07:00
79929b395e
fix: Tell splash state to stop being lazy
We need the class as soon as possible. It being lazy might be the reason why there's a race condition.
2024-08-26 15:33:18 +07:00
3d7b6b88be
fix(archive): Move common code to android
Since these codes still relies heavily on java and iOS doesn't have java support.
2024-08-26 15:25:01 +07:00
84ee9213be
refactor(library/item): Injekt lazily 2024-08-26 14:59:18 +07:00
94567a37fa
refactor(tracker): Check if tracker is EnhancedTrackService from the function itself 2024-08-26 14:47:32 +07:00
da99cf5cfa
refactor(extension/repo): Refactor the ExtensionRepoService to use DTOs
Co-authored-by: MajorTanya <39014446+MajorTanya@users.noreply.github.com>
2024-08-26 14:10:20 +07:00
c7405e0b33
chore(network): Sync doh provider with upstream
Also, actually use all the DoH providers. Not sure why only up to Quad9 is actually being implemented.
2024-08-26 08:02:07 +07:00
2818bfa82f
refactor(settings/advanced): Don't use i18n for DoH names 2024-08-26 07:55:03 +07:00
ce9a3ea399
refactor(network): Preserve original exception 2024-08-26 07:41:33 +07:00
195613cb1e
refactor(network): Simplify helper
Co-authored-by: arkon <arkon@users.noreply.github.com>
2024-08-26 07:33:37 +07:00
arkon
5bd2190980
chore: Don't unnecessarily wrap IOExceptions in UncaughtExceptionInterceptor 2024-08-26 07:08:04 +07:00
2debbc0a10
chore(release): v1.8.5.6 2024-08-25 19:30:12 +07:00
d48b4c330a
fix(manga/details): NPE on tablets 2024-08-25 19:25:55 +07:00
515a96e649
chore(release): v1.8.5.5 2024-08-25 09:20:12 +07:00
0e61db0250
revert: Revert "chore: Don't use memory cache when creating manga shortcut"
This reverts commit 0c3e4a3c24.
2024-08-25 09:06:57 +07:00
0c3e4a3c24
chore: Don't use memory cache when creating manga shortcut 2024-08-25 08:49:09 +07:00
857bccf433
chore: Use original size whenever possible 2024-08-25 08:25:22 +07:00
889b20797e
fix(manga/details): Fix "Theme buttons based on cover"
Palette doesn't like copy's bitmap, could be because of mutability is disabled or perhaps because it's set to HARDWARE configured, not entirely sure, because Google didn't document it. Classic Google behaviour :^)
2024-08-24 20:30:19 +07:00
e9b4292295
refactor(manga/details): Reduce manga variable even further
Basically telling Presenter to take full responsible for `manga` variable on keeping it up to date

Also rename `onFirstLoad` to `onCreateLate` to make it clearer that it's just onCreate but executed late and put stuff that can be executed sooner in onCreate function
2024-08-24 19:47:20 +07:00
4c6efe28c2
docs: Sync changelog 2024-08-24 13:49:11 +07:00
f74c33107b
chore: Set default text value to blank 2024-08-24 13:47:58 +07:00
05bbacda19
chore: Adjust .editorconfig 2024-08-24 13:43:12 +07:00
4d60166fa1
style: Adjust LoadingButton styling 2024-08-24 11:42:25 +07:00
6f94dd091b
refactor: Replace LoadingButtonAndroid with compose
REF: https://gist.github.com/mmolosay/584ce5c47567cb66228b76ef98c3c4e4
2024-08-24 11:07:01 +07:00
61e43e047f
chore: Preparing to migrate History to SQLDelight 2024-08-24 07:27:33 +07:00
10b9fa53a6
fix(deps): Downgrade gradle again
Apparently, it'll be fixed on Kotlin 2.1.0 beta. Classic JetBrains :^)

REF: https://youtrack.jetbrains.com/issue/KT-70700/Gradle-8.10-The-value-for-task-commonizeNativeDistribution-property-kotlinNativeBundleBuildService-cannot-be-changed-any-further#focus=Comments-27-10351376.0-0
2024-08-24 06:58:10 +07:00
579cf7e0d3
docs: Sync changelog 2024-08-23 18:10:15 +07:00
renovate[bot]
2c6382c0c3
chore(deps): Update kotlin monorepo to v2.0.20 (#168)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-23 18:09:00 +07:00
renovate[bot]
a3ccca8347
chore(deps): Update aboutlibraries to v11.2.3 (#169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-23 18:08:39 +07:00
36c63b9746
revert: "Migrate even more stuff to use SQLDelight" 2024-08-23 18:08:13 +07:00
ac95e32c8b
revert: "Migrate even more stuff to use SQLDelight"
This reverts commit c7ef06717d.
2024-08-23 18:02:56 +07:00
renovate[bot]
cc5a6ac7a8
fix(deps): Update dependency dev.chrisbanes.compose:compose-bom to v2024.08.00-alpha02 (#167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-23 17:44:04 +07:00
renovate[bot]
b5c5f13d5d
chore(deps): Update dependency gradle to v8.10 (#165)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-23 17:43:05 +07:00
c7ef06717d
fix(migration): Wrong manga 2024-08-23 17:38:38 +07:00
0934e7518b
refactor: Migrate even more stuff to use SQLDelight 2024-08-23 17:14:12 +07:00
4af54a906a
refactor: Migrate more stuff to use SQLDelight 2024-08-23 16:58:14 +07:00
4d2909340e
fix(extension): Prevent crashes related to extensions
Mostly to prevent "NetworkOnMainThreadException"
2024-08-23 08:25:22 +07:00
ba7baba449
revert(manga/details): Revert flow usage
The value refuses to change for some reason
2024-08-21 19:40:27 +07:00
399c532cd2
chore(release): v1.8.5.4 2024-08-21 18:22:43 +07:00
65e8fb3b8e
refactor(manga/details): Start migrating presenter to use flow
Also fix issue with manga details not showing latest custom cover if the cover is set from reader screen

Not sure why MangaHeaderItem has its own manga object, seems unnecessary (and could cause some sync issue) since it can be retrieved from adapter
2024-08-21 18:13:57 +07:00
f7e43536b4
chore(release): v1.8.5.3 2024-08-21 08:57:08 +07:00
d4f2fee889
docs: Rephrasing
I couldn't eliminate the flickering entirely, the cached image keep being too small or too large compared to the original image.
2024-08-21 08:42:06 +07:00
4f17048da7
fix(coil): Don't use original size
Caused flickering if cached image is smaller than the original image. Still flicker sometimes if the cached image is too small (more than 1 pixel differences)
2024-08-21 08:39:57 +07:00
d41f9359f1
fix(manga/details): Update header on confirm delete from controller itself
Should reduce crashes
2024-08-21 08:15:02 +07:00
2e0fca04ca
fix(manga/details): Use presenterScope 2024-08-21 07:49:51 +07:00
e52a55e4f4
fix(manga/details): Update header much sooner 2024-08-21 07:32:15 +07:00
29a30b30e5
fix(manga/details): Update header on confirm delete 2024-08-21 07:06:34 +07:00
7564566452
refactor(coil): Turn "useCustomCover" into an extension method 2024-08-21 06:45:19 +07:00
b044842475
feat(cover): Custom cover now shown globally 2024-08-21 06:27:04 +07:00
e8fc16d166
refactor(coil): Move ImageView.loadManga() to its own directory 2024-08-21 06:01:21 +07:00
8a168f5581
chore(coil): Actually make it the default 2024-08-20 18:40:49 +07:00
e1c0d1a9d7
fix(coil): Use original size and in-exact precision by default 2024-08-20 18:02:14 +07:00
1d825e25c0
fix(coil): Only use original size if grid item size is not fixed 2024-08-20 17:41:33 +07:00
5d29c0a6cf
fix(coil): Use original size if it has ratio
Should fix flickering (or at least reduced it)

Fixes GH-160
2024-08-20 16:48:03 +07:00
d002642697
chore: Convert all cover usage to use MangaCover data class 2024-08-19 20:06:54 +07:00
839f762fa7
refactor(cover): Data class for manga cover 2024-08-18 18:44:04 +07:00
8ad123956c
fix(coil): Unsatisfied request error when both disk and network read is enabled 2024-08-18 14:56:01 +07:00
2c0c17f287
fix(coil): Set ratio and colors never called for URI 2024-08-18 12:03:45 +07:00
b220705492
chore(coil): Move stuff around 2024-08-18 11:36:24 +07:00
103fae06d3
refactor(coil): Simplify cover fetcher code
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2024-08-18 11:33:26 +07:00
7fc73ddcdc
feat: Toggle for double tap to zoom
Closes GH-161
2024-08-18 10:10:21 +07:00
c7eed6d2d3
docs: Sync changelog 2024-08-17 20:52:06 +07:00
dd808a4240
feat: Toggle for chapter swipe action 2024-08-17 20:10:16 +07:00
8827e4433c
refactor(library): Simplify library item hashcode 2024-08-17 19:38:05 +07:00
7e1ce69d7f
refactor(cover): Adjust cover cache key and how cover update is prepared 2024-08-17 19:12:32 +07:00
c9b302ab21
refactor: Add cover_last_modified 2024-08-17 14:26:57 +07:00
653b2d7839
refactor(cover): Adjust cover (memory) cache key
Hopefully fix library image flickering on resume/bind
2024-08-17 11:45:15 +07:00
df66327996
chore(coil): Remove explicit memoryCache config 2024-08-17 09:59:23 +07:00
b645a208cd
chore(renovate): Disable renovate for custom dependencies 2024-08-17 09:02:13 +07:00
d7a59686d2
chore: Editor config for json files 2024-08-17 08:54:19 +07:00
b15d7c4dee
docs: Update FUNDING.yml 2024-08-17 08:52:34 +07:00
4e6ab3360f
chore: Target Android 15 2024-08-17 08:21:40 +07:00
1e68e55cf7
refactor(chapter): Migrate more queries to SQLDelight 2024-08-17 07:59:39 +07:00
fac21dbab7
refactor(backup/restore): Migrate bulk insert chapter to SQLDelight 2024-08-17 06:39:52 +07:00
16a51f00c4
fix(recents): Fix get recent chapters query 2024-08-16 20:07:22 +07:00
ce94bd2ad6
refactor(reader): Use SQLDelight to load chapter url 2024-08-16 20:01:00 +07:00
a6ef46a90f
refactor(recents): Migrate getRecentChapters to SQLDelight 2024-08-16 19:46:14 +07:00
eb8727afcf
docs: Sync changelog [skip ci] 2024-08-16 10:18:46 +07:00
e5a7416284
docs: Sync changelog 2024-08-16 10:10:14 +07:00
db03bcec15
fix: Exclude :presentation:widget
Not yet ready to be used
2024-08-16 09:38:26 +07:00
fb705b9605
ci: Fix run name 2024-08-16 09:36:48 +07:00
6580a217fe
fix: Fix build 2024-08-16 09:27:31 +07:00
Naputt1
4549f937f2
fix(chapter): Fixed chapter number parsing when number is after unwanted tag 2024-08-16 09:22:27 +07:00
b34ca34d1c
test(chapter): Incorrect expected value 2024-08-16 09:19:06 +07:00
0842a3014d
test(chapter): Fix test 2024-08-16 09:15:30 +07:00
59845065fb
ci: Run subproject unit test 2024-08-16 09:11:28 +07:00
97fc65a28d
test(chapter): Fix test 2024-08-16 08:59:00 +07:00
4adddcf6ac
test(chapter): (Re)added test for chapter recognition 2024-08-16 08:49:01 +07:00
497d387c4b
refactor(chapter): Make chapter recognition return its value
Also move it to domain module
2024-08-16 08:32:03 +07:00
stevenyomi
1cd40907ef
refactor(chapter): Improve chapter recognition 2024-08-16 08:11:15 +07:00
842ca9ec4a
ci: Run name for manually triggered runs [skip ci] 2024-08-16 07:21:33 +07:00
0280c4e00d
chore(release): v1.8.5.2 2024-08-16 07:12:49 +07:00
f15fbdc8cb
fix(preference): Minus and Plus assign missing set(...) call 2024-08-16 07:06:22 +07:00
0374942a34
refactor(library): Remove unnecessary lazy 2024-08-15 18:56:19 +07:00
e52ee9289f
refactor(library): More migration effort to flow 2024-08-15 17:54:32 +07:00
a9b2ec06cd
refactor(library): Some adjustments 2024-08-15 17:09:53 +07:00
bf164608a0
refactor(library): Simplify code 2024-08-15 17:07:34 +07:00
a60776b0a5
revert(deps): Downgrade gradle
It broke KMP somehow

REF: https://youtrack.jetbrains.com/issue/KT-67162/KGP-Kotlin-Native-with-Isolated-Projects-kotlinNativeBundleBuildService-cannot-be-changed-any-futher
2024-08-15 16:44:29 +07:00
renovate[bot]
07c62405df
fix(deps): Update lifecycle to v2.8.4 (#158)
* fix(deps): Update lifecycle to v2.8.4

* docs: Sync changelog [skip ci]

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2024-08-15 15:40:28 +07:00
renovate[bot]
c1828da229
chore(deps): Update null2264/actions digest to a4d6620 (#157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-15 15:32:05 +07:00
renovate[bot]
282ba25332
fix(deps): Update dependency co.touchlab:kermit to v2.0.4 (#156)
* fix(deps): Update dependency co.touchlab:kermit to v2.0.4

* chore: Sync changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ahmad Ansori Palembani <46041660+null2264@users.noreply.github.com>
2024-08-15 15:27:56 +07:00
f432d32776
chore: Setup renovate bot [skip ci] 2024-08-15 15:21:12 +07:00
bc944dab12
chore(deps): Update gradle to v8.10 2024-08-15 14:58:37 +07:00
1d736b0bab
chore(release): v1.8.5.1 2024-08-15 14:28:13 +07:00
f2b2767a23
fix(library): Entry list should be unique 2024-08-15 14:05:36 +07:00
9c29758a5d
fix(theme): Accidentally pushed this change 2024-08-14 12:35:34 +07:00
3202d8cdde
ci: Message input [skip ci] 2024-08-14 12:04:46 +07:00
MajorTanya
0b1ed293f3
fix: Fixed MAL start date parsing
The previous approach would always throw an Exception because
`SimpleDateFormat.format()` expects the input to be of type `Date` or
`Number`, not `String`.
2024-08-14 11:54:51 +07:00
c0059846b7
style: Adjust notification icon 2024-08-14 11:21:09 +07:00
4827ee0583
style: Increase monochrome icon spacing 2024-08-14 11:16:00 +07:00
480d5a3f61
ci: Simplify script [skip ci] 2024-08-14 09:07:59 +07:00
e33addcae3
docs: Add missing credits and use mihon git instead of archive.org 2024-08-14 08:30:21 +07:00
615c18a01f
refactor: Attach Tachiyomi preferenceStore to Preference widget on bound
Hopefully fix ClassCastException issues
2024-08-14 08:03:19 +07:00
d3149abf9c
chore: Remove unused migrations
Yokai earliest version starts at version code 111 anyway, so older
migrations are basically useless
2024-08-14 07:14:37 +07:00
6c8bd82d41
fix: Unread badge wont show up for some sources
Always happened on Komga apparently, but not as frequent on other sources

Fixes GH-95
2024-08-12 18:07:54 +07:00
65260e8bd7
fix: Try loading custom cover if non-custom cover is not found
Fixes GH-150
2024-08-12 15:49:58 +07:00
84a3f91b80
chore(deps): Update Android Gradle Plugin to v8.5.2 2024-08-12 14:27:22 +07:00
945c2c28cd
fix: Kitsu domain change from kitsu.io to kitsu.app
REF: 244fdccca9
2024-08-12 13:30:00 +07:00
1720472f33
fix: Splash state assignment only happened on empty library
Hopefully fixed long splash time (GH-147, GH-26)
2024-08-12 10:27:30 +07:00
a8e8fe01b7
fix(theme): Status bar stays on dark mode when app is following system theme
Fixes GH-146
2024-08-12 08:10:32 +07:00
0bdecfe60f
chore: Information about Legacy installer
It's currently only there so that backup from upstream won't crash the app
2024-08-12 07:52:14 +07:00
MajorTanya
28034d7133
fix(migration): Fix some migrations never running 2024-08-12 07:29:24 +07:00
d19aca3e0b
refactor(downloader/split): Use UniFile to split tall images
Hopefully will improve stability
2024-08-12 07:17:58 +07:00
AntsyLich
5ac816225f
fix: Fix UI freeze after migration 2024-08-12 06:32:57 +07:00
9403a9c64c
chore: Sync project 2024-08-10 11:50:13 +07:00
e1ccfeeb0a
refactor: Simplify SQL 2024-08-10 11:30:42 +07:00
7e1b532f83
fix: Scanlator filter not working properly 2024-08-10 11:10:52 +07:00
Carlos
0e2c336a37
fix(deps): Remove and replace some dependencies
Not sure what happened, some dependencies' versions seems to be missing from Maven
central

- com.dmitrymalkovich.android:material-design-dimens v1.4 is not
  available on central
- Replace br.com.simplepass:loading-button-android with
  com.github.leandroBorgesFerreira:LoadingButtonAndroid. Not
  available on central
- Replace com.github.florent37:viewtooltip with
  com.github.CarlosEsco:ViewTooltip. Not available on central
2024-08-09 11:39:42 +07:00
Ahmad Ansori Palembani
98a8951c48
fix: Wrong module id 2024-08-09 10:19:24 +07:00
a4bb99c710
ci(deps): Bump null2264/actions hash to a4d662095a2f2af1ed24f1228eb6e55b0f9f1f29 2024-08-09 09:30:05 +07:00
38aff1573a
fix(deps): Update dependency androidx.annotation:annotation to v1.8.2 2024-08-09 09:28:13 +07:00
7eaa5cc0b9
fix(deps): Update dependency androidx.work:work-runtime-ktx to v2.9.1 2024-08-09 09:26:29 +07:00
cf3690574e
chore(deps): Update compose bom to v2024.08.00-alpha01 2024-08-09 09:24:39 +07:00
2fc1b25192
refactor(deps): Use coil-bom and update coil to v3.0.0-alpha10 2024-08-09 09:23:34 +07:00
77fb64bb5d
chore: Use Coil pipeline to handle HEIF images 2024-08-09 07:48:19 +07:00
223395035a
chore: Sync project 2024-08-09 07:32:08 +07:00
5e5f27c353
chore(deps): Update kotlin to v2.0.10 2024-08-08 07:53:27 +07:00
93962b2649
feat(core/preference): Add Darwin implementation
Yoinked from my college final project
2024-08-08 07:48:57 +07:00
97417d175f
chore(settings/advanced): Allow any users to set Display Color Profile
Not sure why I gate this out for only Android 8+ previously
2024-08-08 07:19:12 +07:00
9b46c6a763
chore(reader/webtoon): Only increase RecyclerView cache size on Android 8 or newer 2024-08-08 07:16:04 +07:00
e6eff2c3d0
revert(PagerPageHolder): Revert "Split mergeOrSplitPages code into several functions"
This reverts commit 1ce2663dfa.
2024-08-07 13:40:23 +07:00
arkon
d3a47bdfe3
chore(reader/webtoon): Try to ensure that error message is removed when image is loaded 2024-08-07 12:51:51 +07:00
Two-Ai
0f609ab0c7
refactor(reader/webtoon): Simplify page holder 2024-08-07 12:41:54 +07:00
4f9ee5dade
refactor(WebView): Simplify code 2024-08-07 12:22:07 +07:00
1ce2663dfa
refactor(PagerPageHolder): Split mergeOrSplitPages code into several functions 2024-08-07 11:14:41 +07:00
298cac44c9
docs(readme): Use WebP instead of PNG [skip ci] 2024-08-07 08:57:02 +07:00
13f77ac5a8
docs: Sync changelog [skip ci] 2024-08-06 13:15:37 +07:00
arkon
f0a761ad47
fix: Fixed tap controls not working when zoomed in
Co-authored-by: Paloys <Paloys@users.noreply.github.com>
2024-08-06 13:01:59 +07:00
FooIbar
938489d0e0
fix(reader/webtoon): Fix tap control area shifting after zooming out 2024-08-06 12:55:14 +07:00
FooIbar
33e22b8aee
fix(reader/webtoon): Fix recycled item's height being 0 in webtoon mode
Which will prevent the new image from being decoded until it's visible.
2024-08-06 12:51:45 +07:00
ad18b6db7d
docs: Sync changelog
This fix is backported in v1.8.4.4
2024-08-06 07:29:19 +07:00
97bd9c4bc8
fix(worker): Add delay to tryToSetForeground function
> The system will crash the app if the worker that calls setForeground() finished
> before the service runner be able to call Service.startForeground(). This edge
> case is not handled by WorkManager and there is no way to check if the required
> calls are done.

The delay duration is a magic number where we assume by then the
transition to foreground service is done.

Seems to be an issue introduced in Android 8, and Google refuses to fix
it, as usual.

REF: https://developer.android.com/about/versions/oreo/background.html#services (Last paragraph of the section)
REF: https://issuetracker.google.com/issues/76112072#comment36 (Status is set to Won't Fix)

Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
2024-08-06 07:08:03 +07:00
3168f87012
chore: Sync project [skip ci] 2024-08-05 21:56:49 +07:00
a3dd231e00
fix: Handle compatibility issue with J2K backup file
Fixes GH-135
2024-08-05 20:58:24 +07:00
a6fbba607f
chore: Something changed apparently [skip ci] 2024-08-05 20:24:38 +07:00
d7f6f78b6c
fix: Missing monochrome icon for _round 2024-08-05 19:46:22 +07:00
062ed0997a
feat: Add monochrome icon
Experimental, might be too small, might be too big

Fixes GH-82
2024-08-05 19:36:00 +07:00
Ahmad Ansori Palembani
1bf38a5fb0
docs: Don't specify image size [skip ci]
GitHub doesn't like that apparently
2024-08-05 13:19:59 +07:00
38a9153240
chore: Re-added some links 2024-08-05 12:56:28 +07:00
FooIbar
8195fe56fd
chore(reader/webtoon): Match extra layout space with scroll distance
Also increase recycler item view cache size
2024-08-05 12:32:32 +07:00
6a02e7f0a0
chore(reader): That's not supposed to be there [skip ci] 2024-08-05 12:25:14 +07:00
e7e5b07605
refactor(reader): Handle background from ReaderActivity
Also allow long strip and continuous vertical background colour to be changed

Fixes GH-98
2024-08-05 12:21:03 +07:00
75d73cf604
refactor(WebtoonPageHolder): Move stuff around 2024-08-05 09:56:45 +07:00
9d16172095
refactor(WebtoonRecyclerView): Redundant qualifier name 2024-08-05 09:49:48 +07:00
28a8a675ad
refactor(WebtoonPageHolder): Remove unnecessary functions 2024-08-05 09:46:57 +07:00
4789a1652a
refactor(WebtoonPageHolder): Adjust how margin is set 2024-08-05 09:40:23 +07:00
092ebf84f6
chore: Disable image split for animated images
It's duplicating instead of splitting
2024-08-05 09:27:42 +07:00
67b4e59658
docs(README): Add mihon discord server [skip ci] 2024-08-04 12:56:24 +07:00
AntsyLich
d0a99ca8d9
chore: Make categories backup independent 2024-08-04 12:56:15 +07:00
8c409ff306
chore: Allow user to edit local manga without adding it to their library 2024-08-03 22:22:47 +07:00
951773053b
feat(backup): Add information on when was the last time backup automatically created 2024-08-03 22:12:43 +07:00
6d60177a09
refactor: Split backup related preferences to its own class
Also increased default number of backups to 5
2024-08-03 22:02:25 +07:00
0f9ff92f89
chore: Add missing "Max automatic backups" option 2024-08-03 21:51:49 +07:00
ad3bb53519
fix(backup/creator): Backup should be created after backup file path is validated 2024-08-03 21:34:12 +07:00
24784d6e78
fix(backup): Allow creating backup without entries 2024-08-03 21:29:36 +07:00
19e87040f6
refactor(backup/creator): Make backup functions into operator function 2024-08-03 21:27:24 +07:00
2a073f6154
refactor(backup/creator): Source backup creator should depends on BackupManga
This eliminates the need to check libraryEntries backup option twice
2024-08-03 21:24:15 +07:00
66ee8fb0cd
refactor(backup/creator): Reformat code 2024-08-03 21:18:38 +07:00
9c435891f6
chore: Re-enable reading mode detection for LTR
But now it's only triggered if it's comic, since manhua is now mixed of
webtoon format and comic format.
2024-08-02 10:24:31 +07:00
fb6ee8271f
chore: Vivo notch (pre-Android P) internal API [skip ci] 2024-08-02 09:03:05 +07:00
4a8cf8d24d
refactor: Simplify beta version format 2024-08-02 07:05:40 +07:00
93709836e7
docs: Sync changelog [skip ci] 2024-08-01 11:18:03 +07:00
99769e4c6e
fix: Don't show nightly version format on beta 2024-08-01 10:55:37 +07:00
a37365401e
docs: Sync changelog 2024-08-01 08:30:14 +07:00
d82c029fd5
chore(deps): Remove nucleus dependency
Jay removed Nucleus since 1.6.4, not sure why it's still listed on the deps
2024-08-01 08:29:25 +07:00
fd47319064
refactor: Remove google guava dependency 2024-08-01 08:06:55 +07:00
6c05cbd41a
chore: Remove FIXME comment [skip ci]
Fixed after using Voyager as navigation
2024-08-01 07:47:36 +07:00
3fa755117f
refactor: Keep some library manga variables as Long 2024-08-01 07:19:04 +07:00
2e1faefa97
refactor: Start using Voyager for navigation 2024-08-01 07:14:14 +07:00
563a3a0289
refactor: Don't use statusBarsPadding
The documentation says these AppBars automatically handles them
2024-07-31 13:12:48 +07:00
f64bdb2ca5
refactor: Replace java-string-similarity with pure Kotlin Levenshtein implementation 2024-07-31 12:20:40 +07:00
8cca5186dd
chore: Disable padding 2024-07-31 11:42:24 +07:00
e9b852a04d
docs: Sync changelog 2024-07-31 11:25:38 +07:00
41c565ca9f
chore: Disable LTR/Comic/Manhua auto detection
Causing too many confusion (GH-125)
2024-07-31 11:22:49 +07:00
741 changed files with 19114 additions and 13292 deletions

View file

@ -1,8 +1,28 @@
[*]
insert_final_newline=true
root = true
[*]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.xml]
indent_size = 4
# noinspection EditorConfigKeyCorrectness
[*.{kt,kts}]
indent_style=space
indent_size=4
ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
indent_size = 4
max_line_length = 120
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ktlint_code_style = intellij_idea
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_class-signature = disabled
ktlint_standard_discouraged-comment-location = disabled
ktlint_standard_function-expression-body = disabled
ktlint_standard_function-signature = disabled

4
.github/FUNDING.yml vendored
View file

@ -1,2 +1,2 @@
github: inorichi
ko_fi: inorichi
github: [null2264]
ko_fi: ziro2264

View file

@ -35,9 +35,24 @@ body:
required: true
- label: If this is an issue with an extension, or a request for an extension, I should be contacting the extensions repository's maintainer/support for help.
required: true
- label: I have updated the app to version **[1.8.4.3](https://github.com/null2264/yokai/releases/latest)**.
- label: I have updated the app to version **[1.9.7.3](https://github.com/null2264/yokai/releases/latest)**.
required: true
- label: I have checked through the app settings for my feature.
required: true
- label: I will fill out all of the requested information in this form.
required: true
- type: "textarea"
id: "prioritisation"
attributes:
label: "Is this issue important to you?"
description: |
**Please do not modify this text area!**
This template let users to vote with a :+1: reaction if they find it important.
This is not a guarantee that highly-requested issues will be fixed first, but it helps us to figure out what's important to users. Please react on other users' issues if you find them important.
value: |
Add a :+1: [reaction] to [issues you find important].
[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
[issues you find important]: https://github.com/null2264/yokai/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc

View file

@ -94,15 +94,30 @@ body:
required: true
- label: I have written a short but informative title.
required: true
- label: If this is an issue with an extension, or a request for an extension, I should be contacting the extensions repository's maintainer/support for help.
- label: If this is an issue with an extension, or a request for an extension, I should be contacting the extensions repository's maintainer/support for help ([Browse → Extensions → Find the extension → Settings → Tap the `[<>]` icon](https://cdn.aap.my.id/extension-repo-link.png), it *should* redirect you to the maintainer).
required: true
- label: I am reporting an issue exclusive to this fork. I have also checked that is not an issue on the [main version of Mihon](https://github.com/mihonapp/mihon)
- label: I am reporting an issue exclusive to this fork. I have also checked that is not an issue on the [main version of Mihon](https://github.com/mihonapp/mihon).
required: true
- label: I have tried the [troubleshooting guide](https://mihon.app/help/).
- label: I have tried the [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
required: true
- label: I have updated the app to version **[1.8.4.3](https://github.com/null2264/yokai/releases/latest)**.
- label: I have updated the app to version **[1.9.7.3](https://github.com/null2264/yokai/releases/latest)**.
required: true
- label: I have updated all installed extensions.
required: true
- label: I have filled out all of the requested information in this form.
required: true
- type: "textarea"
id: "prioritisation"
attributes:
label: "Is this issue important to you?"
description: |
**Please do not modify this text area!**
This template let users to vote with a :+1: reaction if they find it important.
This is not a guarantee that highly-requested issues will be fixed first, but it helps us to figure out what's important to users. Please react on other users' issues if you find them important.
value: |
Add a :+1: [reaction] to [issues you find important].
[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
[issues you find important]: https://github.com/null2264/yokai/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,11 @@
<!--
^ Please summarise the changes you have made here ^
-->
---
Add a :+1: [reaction] to [pull requests you find important].
[reaction]: https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/
[pull requests you find important]: https://github.com/null2264/yokai/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc

View file

@ -1,23 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="338.66666mm"
height="338.66666mm"
viewBox="0 0 338.66665 338.66665"
version="1.1"
id="svg8"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
sodipodi:docname="app-icon.inkscape.svg"
xml:space="preserve"
inkscape:export-filename="../../../../../bitmap.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
width="338.66666mm"
height="338.66666mm"
viewBox="0 0 338.66665 338.66665"
version="1.1"
id="svg8"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
sodipodi:docname="app-icon.inkscape.svg"
xml:space="preserve"
inkscape:export-filename="../../app-icon-monochrome-padded.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
id="defs2" /><sodipodi:namedview
id="base"
pagecolor="#2a3039"
@ -25,9 +24,9 @@
borderopacity="1"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-865.71429"
inkscape:cy="2012.8571"
inkscape:zoom="0.35000001"
inkscape:cx="602.85713"
inkscape:cy="627.14284"
inkscape:document-units="px"
inkscape:current-layer="svg8"
inkscape:document-rotation="0"
@ -37,7 +36,7 @@
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:window-width="1366"
inkscape:window-height="740"
inkscape:window-height="739"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@ -167,9 +166,11 @@
sodipodi:open="true"
sodipodi:arc-type="arc"
d="M 338.34535,158.90727 A 169.33331,169.33331 0 0 1 179.81526,338.34189 169.33331,169.33331 0 0 1 0.32821083,179.87117 169.33331,169.33331 0 0 1 158.73956,0.33170574 169.33331,169.33331 0 0 1 338.33141,158.68366" /><path
id="path4"
id="path4-67-6"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 186.1447,2.0231322 c -2.54593,0.203777 -4.19625,2.8233552 -5.69784,8.5100748 -1.17527,4.450871 -2.32731,5.569396 0.51676,22.714374 1.85846,11.203323 -79.33779,63.44833 -74.10813,76.913129 5.22964,13.4648 8.12662,30.06433 -7.264675,51.85523 -15.391293,21.7909 -30.393452,48.18628 -18.352884,93.68575 11.640451,43.98751 81.161449,46.34446 81.161449,46.34446 0,0 70.48657,1.99687 92.02125,-45.34245 34.36971,-75.55435 -28.64251,-96.23379 -20.31297,-113.70933 18.75807,-39.35485 -29.03473,-44.739284 -13.17852,-68.7865 10.14669,-15.388269 9.15497,-39.973286 -11.1311,-56.083913 C 197.11924,8.0548154 190.38792,1.6835039 186.1447,2.0231322 Z m 5.16506,19.5838168 c 2.52668,-0.134551 7.59773,5.246279 11.93415,11.02775 14.67602,19.566546 4.37633,26.99266 4.84519,30.292206 10.12241,-5.72966 5.96459,-23.748469 8.2336,-9.815938 3.00062,18.424875 -10.54984,22.472488 -12.35893,32.287434 -2.8219,15.309769 3.83241,18.898959 14.39912,34.576179 16.83557,24.97798 -3.40325,32.22262 0.56947,44.54922 3.97277,12.32662 39.20572,24.82625 28.04997,75.29359 -11.15585,50.46736 -54.37742,53.57174 -95.10014,55.28241 -26.11609,1.09707 -68.139433,-30.31018 -61.710954,-73.97119 4.876175,-33.11806 16.641064,-34.40378 39.626004,-60.46142 46.85864,-53.12287 -18.88848,-40.90666 34.46559,-85.383417 21.92452,-18.276589 30.91957,-28.46973 27.31151,-40.947369 -2.65568,-9.184044 -2.22978,-12.624804 -0.26458,-12.729455 z" /><path
d="m 186.14473,2.0231825 c -2.54593,0.20378 -4.19625,2.82336 -5.69784,8.5100795 -1.17527,4.45087 -2.32731,5.56939 0.51676,22.71437 1.85846,11.20332 -79.33779,63.44833 -74.10813,76.913138 5.22964,13.4648 8.12662,30.06433 -7.264668,51.85523 -15.391306,21.7909 -30.393466,48.18628 -18.352896,93.68575 11.64045,43.98751 81.161454,46.34446 81.161454,46.34446 0,0 70.48657,1.99687 92.02125,-45.34245 34.36971,-75.55435 -28.64251,-96.23379 -20.31297,-113.70933 18.75807,-39.35485 -29.03473,-44.739288 -13.17852,-68.786508 10.14669,-15.38827 9.15497,-39.97329 -11.1311,-56.08391 C 197.11927,8.0548725 190.38795,1.6835525 186.14473,2.0231825 Z m 5.16506,19.5838195 c 2.52668,-0.13455 7.59773,5.24628 11.93415,11.02775 14.67602,19.56655 4.37633,26.99266 4.84519,30.2922 10.12241,-5.72965 5.96459,-23.74846 8.2336,-9.81593 3.00062,18.42487 -10.54984,22.47249 -12.35893,32.28743 -2.8219,15.309778 8.40233,12.82104 14.98085,30.545428 7.43932,20.04362 -2.04047,21.5507 0.81899,36.87158 2.37612,12.73116 38.37447,36.53465 27.21872,87.00199 -11.15585,50.46736 -69.00828,54.25991 -98.3554,49.67184 -30.9465,-4.83813 -59.981444,-20.42956 -58.455694,-63.79616 1.17702,-33.45441 16.641064,-38.96824 39.626004,-65.02588 46.85864,-53.12287 -18.88848,-40.90666 34.46559,-85.383428 21.92452,-18.27659 30.91957,-28.46973 27.31151,-40.94737 -2.65568,-9.18404 -2.22978,-12.6248 -0.26458,-12.72945 z"
sodipodi:nodetypes="sssssscssssssscsssscssssss"
transform="translate(-1.3875429e-6)" /><path
style="font-weight:900;font-stretch:condensed;font-size:254px;line-height:1.25;font-family:'Nunito Sans 7pt Condensed';-inkscape-font-specification:'Nunito Sans 7pt Condensed, Heavy Condensed';fill:#ffffff;stroke-width:0.264583"
d="m 146.72732,258.86832 v -93.218 l 7.874,30.734 L 91.863317,79.798317 h 49.022003 l 28.194,66.040003 h 1.016 l 28.448,-66.040003 h 48.26 l -62.992,116.586003 7.874,-30.734 -0.254,93.218 z"
id="text1"
@ -272,18 +273,6 @@
d="m 653.7436,244.67619 c 0,0 6.11985,0.10159 5.18148,-7.36588 l -8.63458,-6.74177 -12.27623,-1.39951 -4.28038,28.58802 36.87692,8.6492 8.30445,-40.4403 -39.64984,-20.45633 -27.30398,26.67987 25.61361,69.18626 59.42842,-30.42164 10.19852,-36.56341 -43.92344,-54.65035 9.65333,-54.55827 10.42362,-28.866469 -13.56393,30.179739 -11.75151,54.32514 39.13954,55.56972 -7.9805,28.79923 -47.89909,25.46744 -17.83992,-56.83183 17.68426,-20.33639 29.99557,16.78463 -5.84652,27.8404 -24.37487,-4.38782 1.4397,-16.6845 3.52692,0.49037 z"
id="path13"
sodipodi:nodetypes="cccccccccccccccccccccccccccc" /></g></g><g
id="g4"
inkscape:export-filename="../../../../../g4.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
transform="translate(643.86251,-325.54406)"><path
id="path4-67"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -457.71765,327.56709 c -2.54593,0.20378 -4.19625,2.82336 -5.69784,8.51008 -1.17527,4.45087 -2.32731,5.56939 0.51676,22.71437 1.85846,11.20332 -79.33779,63.44833 -74.10813,76.91313 5.22964,13.4648 8.12662,30.06433 -7.26467,51.85523 -15.3913,21.7909 -30.39346,48.18628 -18.35289,93.68575 11.64045,43.98751 81.16145,46.34446 81.16145,46.34446 0,0 70.48657,1.99687 92.02125,-45.34245 34.36971,-75.55435 -28.64251,-96.23379 -20.31297,-113.70933 18.75807,-39.35485 -29.03473,-44.73928 -13.17852,-68.7865 10.14669,-15.38827 9.15497,-39.97329 -11.1311,-56.08391 -12.6788,-10.06914 -19.41012,-16.44046 -23.65334,-16.10083 z m 5.16506,19.58382 c 2.52668,-0.13455 7.59773,5.24628 11.93415,11.02775 14.67602,19.56655 4.37633,26.99266 4.84519,30.2922 10.12241,-5.72965 5.96459,-23.74846 8.2336,-9.81593 3.00062,18.42487 -10.54984,22.47249 -12.35893,32.28743 -2.8219,15.30977 3.83241,18.89896 14.39912,34.57618 16.83557,24.97798 -3.40325,32.22262 0.56947,44.54922 3.97277,12.32662 39.20572,24.82625 28.04997,75.29359 -11.15585,50.46736 -54.37742,53.57174 -95.10014,55.28241 -26.11609,1.09707 -68.13943,-30.31018 -61.71095,-73.97119 4.87617,-33.11806 16.64106,-34.40378 39.626,-60.46142 46.85864,-53.12287 -18.88848,-40.90666 34.46559,-85.38342 21.92452,-18.27659 30.91957,-28.46973 27.31151,-40.94737 -2.65568,-9.18404 -2.22978,-12.6248 -0.26458,-12.72945 z" /><path
style="font-weight:900;font-stretch:condensed;font-size:254px;line-height:1.25;font-family:'Nunito Sans 7pt Condensed';-inkscape-font-specification:'Nunito Sans 7pt Condensed, Heavy Condensed';fill:#ffffff;stroke-width:0.264583"
d="m -497.13503,584.41228 v -93.218 l 7.874,30.734 -62.738,-116.586 h 49.022 l 28.194,66.04 h 1.016 l 28.448,-66.04 h 48.26 l -62.992,116.586 7.874,-30.734 -0.254,93.218 z"
id="text1-5"
aria-label="Y" /></g><g
id="g22"
transform="translate(-115.06893,355.91004)"><path
style="fill:#0e1418;fill-opacity:1;stroke-width:1.18329;stroke-linejoin:bevel"
@ -387,4 +376,31 @@
style="fill:#c2c2c2;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 653.7436,244.67619 c 0,0 6.11985,0.10159 5.18148,-7.36588 l -8.63458,-6.74177 -12.27623,-1.39951 -4.28038,28.58802 36.87692,8.6492 8.30445,-40.4403 -39.64984,-20.45633 -27.30398,26.67987 25.61361,69.18626 59.42842,-30.42164 10.19852,-36.56341 -43.92344,-54.65035 9.65333,-54.55827 10.42362,-28.866469 -13.56393,30.179739 -11.75151,54.32514 39.13954,55.56972 -7.9805,28.79923 -47.89909,25.46744 -17.83992,-56.83183 17.68426,-20.33639 29.99557,16.78463 -5.84652,27.8404 -24.37487,-4.38782 1.4397,-16.6845 3.52692,0.49037 z"
id="path32"
sodipodi:nodetypes="cccccccccccccccccccccccccccc" /></g></g></svg>
sodipodi:nodetypes="cccccccccccccccccccccccccccc" /></g></g><g
id="g35"
inkscape:export-filename="../../../../../g4.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
transform="matrix(0.49302775,0,0,0.49302775,404.03883,-66.125968)"><path
id="path34"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -457.71765,327.56709 c -2.54593,0.20378 -4.19625,2.82336 -5.69784,8.51008 -1.17527,4.45087 -2.32731,5.56939 0.51676,22.71437 1.85846,11.20332 -79.33779,63.44833 -74.10813,76.91313 5.22964,13.4648 8.12662,30.06433 -7.26467,51.85523 -15.3913,21.7909 -30.39346,48.18628 -18.35289,93.68575 11.64045,43.98751 81.16145,46.34446 81.16145,46.34446 0,0 70.48657,1.99687 92.02125,-45.34245 34.36971,-75.55435 -28.64251,-96.23379 -20.31297,-113.70933 18.75807,-39.35485 -29.03473,-44.73928 -13.17852,-68.7865 10.14669,-15.38827 9.15497,-39.97329 -11.1311,-56.08391 -12.6788,-10.06914 -19.41012,-16.44046 -23.65334,-16.10083 z m 8.88281,24.92034 c 0.67722,1.13595 0.36269,1.77788 2.84427,6.10039 6.57897,11.4595 5.49525,22.68396 2.47521,28.11884 6.43826,-7.45859 1.4727,-26.30017 6.44731,-8.21341 1.96501,7.14443 -5.51434,16.39535 -8.90768,31.41337 -3.43104,15.18487 9.85029,15.92394 16.42881,33.64832 7.43932,20.04362 -5.53783,21.36123 -3.00839,36.74004 3.64147,22.13988 37.60189,29.26408 26.44614,79.73142 -9.62323,42.04211 -55.57705,45.83902 -83.46765,45.04474 -26.44613,-0.75315 -50.24692,-16.65693 -52.02244,-44.63211 -2.17095,-34.20562 4.54921,-48.17024 27.53415,-74.22788 46.85864,-53.12287 -17.67263,-36.07227 35.1275,-81.20525 17.78811,-15.20507 35.36624,-29.86328 31.75818,-42.34092 -2.65568,-9.18404 -2.66316,-11.86794 -1.65541,-10.17755 z"
sodipodi:nodetypes="sssssscssssssscsssscssssss" /><path
style="font-weight:900;font-stretch:condensed;font-size:254px;line-height:1.25;font-family:'Nunito Sans 7pt Condensed';-inkscape-font-specification:'Nunito Sans 7pt Condensed, Heavy Condensed';fill:#ffffff;stroke-width:0.264583"
d="m -497.13503,584.41228 v -93.218 l 7.874,30.734 -62.738,-116.586 h 49.022 l 28.194,66.04 h 1.016 l 28.448,-66.04 h 48.26 l -62.992,116.586 7.874,-30.734 -0.254,93.218 z"
id="path35"
aria-label="Y" /></g><g
id="g4"
inkscape:label="monochrome icon padding"><rect
style="fill:#000000;stroke-width:0.427425"
id="rect1"
width="338.66663"
height="95.298286"
x="0"
y="0" /><rect
style="fill:#000000;stroke-width:0.427425"
id="rect2"
width="338.66663"
height="95.298286"
x="0"
y="243.36835" /></g></svg>

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

BIN
.github/readme-images/app-icon.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -4,7 +4,15 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: [pull_request]
on:
pull_request:
paths:
- '**'
- '!**.md'
- '!i18n/src/commonMain/moko-resources/**/strings.xml'
- '!i18n/src/commonMain/moko-resources/**/plurals.xml'
- 'i18n/src/commonMain/moko-resources/base/strings.xml'
- 'i18n/src/commonMain/moko-resources/base/plurals.xml'
jobs:
build:
@ -19,24 +27,27 @@ jobs:
- name: Setup Android SDK
run: |
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;35.0.0"
- name: Setup Gradle
uses: null2264/actions/gradle-setup@c63d62af63686cb442114b979d4bedb96a514881
uses: null2264/actions/gradle-java-setup@363cb9cf3d66bd9c72ed6860142c6b2c121d7e94
with:
java: 17
distro: adopt
distro: temurin
- name: Copy CI gradle.properties
run: |
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
- name: Build and run tests
run: ./gradlew assembleStandardRelease testStandardReleaseUnitTest
- name: Build the app
run: ./gradlew assembleStandardRelease
- name: Run unit tests
run: ./gradlew testReleaseUnitTest testStandardReleaseUnitTest
- name: Publish test report
uses: mikepenz/action-junit-report@v4
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
include_passed: true

View file

@ -1,4 +1,7 @@
name: Build app
run-name: ${{ github.event_name == 'workflow_dispatch' && format('Build and Release v{0}', github.event.inputs.version) || github.event.commits[0].message }}
on:
push:
branches:
@ -14,6 +17,11 @@ on:
default: false
required: false
type: boolean
message:
description: 'Message for releases (the text at the top of changelog)'
default: ''
required: false
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name == 'workflow_dispatch' }}
@ -32,13 +40,16 @@ jobs:
- name: Setup Android SDK
run: |
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;34.0.0"
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;35.0.0"
- name: Setup Gradle
uses: null2264/actions/gradle-setup@c63d62af63686cb442114b979d4bedb96a514881
uses: null2264/actions/gradle-java-setup@363cb9cf3d66bd9c72ed6860142c6b2c121d7e94
with:
java: 17
distro: adopt
distro: temurin
- name: Setup CHANGELOG parser
uses: taiki-e/install-action@parse-changelog
- name: Copy CI gradle.properties
run: |
@ -54,10 +65,12 @@ jobs:
id: changelog
shell: bash
run: |
# extended SemVer (major.minor.patch.hotfix)
VERSION_FORMAT='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$|^Unreleased$'
{
echo "CHANGELOG<<END_OF_FILE"
cat CHANGELOG.md || echo ""
echo
parse-changelog CHANGELOG.md ${{ github.event.inputs.version == '' && 'Unreleased' || github.event.inputs.version }} --version-format $VERSION_FORMAT || parse-changelog CHANGELOG.md Unreleased --version-format $VERSION_FORMAT || echo "No documented changes so far..."
echo ""
echo "END_OF_FILE"
} >> "$GITHUB_OUTPUT" 2> /dev/null
@ -67,6 +80,7 @@ jobs:
run: |
set -x
echo "VERSION_TAG=v${{github.event.inputs.version}}" >> $GITHUB_ENV
echo "BUILD_TYPE=StandardRelease" >> $GITHUB_ENV
# BETA
- name: Prepare beta build
@ -75,9 +89,14 @@ jobs:
set -x
BETA_COUNT=$(git tag -l --sort=refname "v${{github.event.inputs.version}}-b*" | tail -n1 | sed "s/^\S*-b//g")
[ "$BETA_COUNT" = "" ] && BETA_COUNT="1" || BETA_COUNT=$((BETA_COUNT+1))
if [ -z "$BETA_COUNT" ]; then
BETA_COUNT="1"
else
BETA_COUNT=$((BETA_COUNT+1))
fi
echo "VERSION_TAG=v${{github.event.inputs.version}}-b${BETA_COUNT}" >> $GITHUB_ENV
echo "BUILD_TYPE=StandardBeta" >> $GITHUB_ENV
# NIGHTLY
- name: Prepare nightly build
@ -85,23 +104,17 @@ jobs:
run: |
set -x
echo "VERSION_TAG=r$(git rev-list --count HEAD)" >> $GITHUB_ENV
echo "BUILD_TYPE=StandardNightly" >> $GITHUB_ENV
echo "COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV
echo "COMMIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
# PROD
- name: Build release build and run tests
if: startsWith(env.VERSION_TAG, 'v') && github.event.inputs.beta != 'true'
run: ./gradlew assembleStandardRelease testStandardReleaseUnitTest
- name: Build the app
if: startsWith(env.BUILD_TYPE, 'Standard')
run: ./gradlew assemble${{ env.BUILD_TYPE }}
# BETA
- name: Build beta build and run tests
if: startsWith(env.VERSION_TAG, 'v') && github.event.inputs.beta == 'true'
run: ./gradlew assembleStandardBeta testStandardBetaUnitTest
# NIGHTLY
- name: Build nightly build and run tests
if: startsWith(env.VERSION_TAG, 'r')
run: ./gradlew assembleStandardNightly testStandardNightlyUnitTest
- name: Run unit tests
if: startsWith(env.BUILD_TYPE, 'Standard')
run: ./gradlew testReleaseUnitTest test${{ env.BUILD_TYPE }}UnitTest
- name: Upload R8 APK to artifact
uses: actions/upload-artifact@v4
@ -118,7 +131,7 @@ jobs:
path: app/build/outputs/mapping/standard*
- name: Publish test report
uses: mikepenz/action-junit-report@v4
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
include_passed: true
@ -132,8 +145,7 @@ jobs:
stage=""
case "${{ env.VERSION_TAG }}" in
v*)
_test=$(echo "${{ env.VERSION_TAG }}" | grep "v*-b")
[ "$_test" = "" ] && stage="release" || stage="beta"
[ "$(echo '${{ env.VERSION_TAG }}' | grep 'v*-b')" = "" ] && stage="release" || stage="beta"
;;
r*) stage="nightly" ;;
esac
@ -142,7 +154,7 @@ jobs:
echo "STAGE=${stage}" >> $GITHUB_OUTPUT
- name: Sign APK
uses: null2264/actions/android-signer@102c5b82a95381eeb420bb2d62639d1ff2a3eb1c
uses: null2264/actions/android-signer@363cb9cf3d66bd9c72ed6860142c6b2c121d7e94
if: env.VERSION_TAG != ''
with:
releaseDir: app/build/outputs/apk/standard/${{ steps.version_stage.outputs.STAGE }}
@ -186,8 +198,9 @@ jobs:
tag_name: ${{ env.VERSION_TAG }}
name: Yōkai ${{ env.VERSION_TAG }}
body: |
${{ steps.changelog.outputs.CHANGELOG }}
${{ github.event.inputs.message }}
${{ steps.changelog.outputs.CHANGELOG }}
---
### Checksums
@ -230,7 +243,6 @@ jobs:
It is not ready for daily use and we do not guarantee its usability. Please download the latest stable releases instead (https://github.com/null2264/yokai/releases/latest). If you insist, please be sure to ALWAYS backup before updating.
${{ steps.changelog.outputs.CHANGELOG }}
---
### Checksums

51
.github/workflows/mirror.yml vendored Normal file
View file

@ -0,0 +1,51 @@
# REF: https://github.com/JamesRobionyRogers/GitHub-to-GitBucket-Action/blob/47a44e9/.github/workflows/push-to-gitbucket.yml
name: Mirror Repository
on:
workflow_dispatch:
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Fetch all branches
run: |
git fetch --all
git fetch --tags
- name: List branches
run: |
git branch -a
# Enables tracking of remote branches ensuring all branches are pushed to mirror repo
- name: Track remote branches
run: |
for branch in $(git branch -r | grep -v '\->'); do
local_branch=${branch#origin/}
git branch --track "$local_branch" "$branch" || true
done
- name: Push to mirror repo (GitLab)
env:
MIRROR_URL: "gitlab.com/null2264/yokai.git"
MIRROR_USERNAME: ${{ secrets.MIRROR_USERNAME }}
MIRROR_AUTH: ${{ secrets.MIRROR_AUTH }}
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "actions@github.com"
git remote add mirror https://$MIRROR_USERNAME:$MIRROR_AUTH@$MIRROR_URL
git push --mirror -v mirror || true

1
.gitignore vendored
View file

@ -10,3 +10,4 @@
*/*/build
.kotlin/
kls_database.db
weblate.conf

36
.renovaterc.json5 Normal file
View file

@ -0,0 +1,36 @@
{
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
extends: [
'config:recommended',
],
labels: [
'dependencies',
],
packageRules: [
{
groupName: 'Compose BOM (Alpha)',
matchPackageNames: [
'dev.chrisbanes.compose:compose-bom',
],
ignoreUnstable: false,
},
{
matchPackageNames: [
'com.github.arkon.FlexibleAdapter:flexible-adapter-ui',
'com.github.arkon.FlexibleAdapter:flexible-adapter',
'com.github.CarlosEsco:ViewTooltip',
'com.github.null2264:subsampling-scale-image-view',
'com.github.null2264:injekt-koin',
'com.github.tachiyomiorg:image-decoder',
'com.github.tachiyomiorg:unifile',
'com.github.tachiyomiorg:conductor-support-preference',
'com.github.chrisbanes:PhotoView',
'com.github.PhilJay:MPAndroidChart',
],
enabled: false,
},
],
dependencyDashboardApproval: true,
semanticCommits: 'enabled',
commitMessageLowerCase: 'never',
}

View file

@ -1,38 +1,414 @@
<!-- Formatting
## Additions ?? New features
# Changelog
## Changes ?? Behaviour changes
All notable changes to this project will be documented in this file.
## Fixes ?? Bugfixes
The format is simplified version of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- `Additions` - New features
- `Changes` - Behaviour/visual changes
- `Fixes` - Bugfixes
- `Other` - Technical changes/updates
## Translation ?? translation changes/updates
## [Unreleased]
## Other ?? Technical stuff, what happened behind the scene
-->
## Changes
### Additions
- Add random library sort
- Add the ability to save search queries
- Add toggle to enable/disable hide source on swipe (@Hiirbaf)
- Add the ability to mark duplicate read chapters as read (@AntsyLich)
### Changes
- Temporarily disable log file
- Categories' header now show filtered count when you search the library when you have "Show number of items" enabled (@LeeSF03)
- Chapter progress now saved everything the page is changed
### Fixes
- Allow users to bypass onboarding's permission step if Shizuku is installed
- Fix Recents page shows "No recent chapters" instead of a loading screen
- Fix not fully loaded entries can't be selected on Library page
- Fix certain Infinix devices being unable to use any "Open link in browser" actions, including tracker setup (@MajorTanya)
- Fix source filter bottom sheet unable to be fully scrolled to the bottom
- Prevent potential "Comparison method violates its general contract!" crash
- Fix staggered grid cover being squashed for local source (@AwkwardPeak7)
### Translation
- Update translations from Weblate
### Other
- Refactor Library to utilize Flow even more
- Refactor EmptyView to use Compose
- Refactor Reader ChapterTransition to use Compose (@arkon)
- [Experimental] Add modified version of LargeTopAppBar that mimic J2K's ExpandedAppBarLayout
- Refactor About page to use Compose
- Adjust Compose-based pages' transition to match J2K's Conductor transition
- Resolve deprecation warnings
- Kotlin's context-receiver, schedule for removal on Kotlin v2.1.x and planned to be replaced by context-parameters on Kotlin v2.2
- Project.exec -> Providers.exec
- Remove internal API usage to retrieve Kotlin version for kotlin-stdlib
- Move :core module to :core:main
- Move archive related code to :core:archive (@AntsyLich)
- Refactor Library to store LibraryMap instead of flatten list of LibraryItem
- LibraryItem abstraction to make it easier to manage
- LibraryManga no longer extend MangaImpl
- Update dependency gradle to v8.12
- Update user agent (@Hiirbaf)
- Update serialization to v1.8.1
- Update dependency io.github.fornewid:material-motion-compose-core to v1.2.1
- Update lifecycle to v2.9.0
- Update dependency org.jsoup:jsoup to v1.20.1
- Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.4.0
- Update dependency io.mockk:mockk to v1.14.2
- Update dependency io.coil-kt.coil3:coil-bom to v3.2.0
- Update dependency com.squareup.okio:okio to v3.12.0
- Update dependency com.google.firebase:firebase-bom to v33.14.0
- Update dependency com.google.accompanist:accompanist-themeadapter-material3 to v0.36.0
- Update dependency com.github.requery:sqlite-android to v3.49.0
- Update dependency com.getkeepsafe.taptargetview:taptargetview to v1.15.0
- Update dependency androidx.window:window to v1.4.0
- Update dependency androidx.webkit:webkit to v1.13.0
- Update dependency androidx.sqlite:sqlite-ktx to v2.5.1
- Update dependency androidx.sqlite:sqlite to v2.5.1
- Update dependency androidx.recyclerview:recyclerview to v1.4.0
- Update dependency androidx.core:core-ktx to v1.16.0
- Update dependency androidx.compose:compose-bom to v2025.05.01
- Update aboutlibraries to v11.6.3
- Update plugin kotlinter to v5.1.0
- Update plugin gradle-versions to v0.52.0
- Update okhttp monorepo to v5.0.0-alpha.16
- Update moko to v0.24.5
- Update kotlin monorepo to v2.1.21
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.2
- Update dependency me.zhanghai.android.libarchive:library to v1.1.5
- Update dependency io.insert-koin:koin-bom to v4.0.4
- Update dependency com.android.tools:desugar_jdk_libs to v2.1.5
- Update dependency androidx.work:work-runtime-ktx to v2.10.1
- Update dependency androidx.constraintlayout:constraintlayout to v2.2.1
- Update plugin firebase-crashlytics to v3.0.3
- Update null2264/actions digest to 363cb9c
- Update dependency io.github.pdvrieze.xmlutil:core-android to v0.91.1
## [1.9.7.3]
### Fixes
- More `Comparison method violates its general contract!` crash prevention
## [1.9.7.2]
### Fixes
- Fix MyAnimeList timeout issue
## [1.9.7.1]
### Fixes
- Prevent `Comparison method violates its general contract!` crashes
## [1.9.7]
### Changes
- Adjust log file to only log important information by default
### Fixes
- Fix sorting by latest chapter is not working properly
- Prevent some NPE crashes
- Fix some flickering issues when browsing sources
- Fix download count is not updating
### Translation
- Update Korean translation (@Meokjeng)
### Other
- Update NDK to v27.2.12479018
## [1.9.6]
### Fixes
- Fix some crashes
## [1.9.5]
### Changes
- Entries from local source now behaves similar to entries from online sources
### Fixes
- Fix new chapters not showing up in `Recents > Grouped`
- Add potential workarounds for duplicate chapter bug
- Fix favorite state is not being updated when browsing source
### Other
- Update dependency androidx.compose:compose-bom to v2024.12.01
- Update plugin kotlinter to v5
- Update plugin gradle-versions to v0.51.0
- Update kotlin monorepo to v2.1.0
## [1.9.4]
### Fixes
- Fix chapter date fetch always null causing it to not appear on Updates tab
## [1.9.3]
### Fixes
- Fix slow chapter load
- Fix chapter bookmark state is not persistent
### Other
- Refactor downloader
- Replace RxJava usage with Kotlin coroutines
- Replace DownloadQueue with Flow to hopefully fix ConcurrentModificationException entirely
## [1.9.2]
### Changes
- Adjust chapter title-details contrast
- Make app updater notification consistent with other notifications
### Fixes
- Fix "Remove from read" not working properly
## [1.9.1]
### Fixes
- Fix chapters cannot be opened from `Recents > Grouped` and `Recents > All`
- Fix crashes caused by malformed XML
- Fix potential memory leak
### Other
- Update dependency io.github.kevinnzou:compose-webview to v0.33.6
- Update dependency org.jsoup:jsoup to v1.18.3
- Update voyager to v1.1.0-beta03
- Update dependency androidx.annotation:annotation to v1.9.1
- Update dependency androidx.constraintlayout:constraintlayout to v2.2.0
- Update dependency androidx.glance:glance-appwidget to v1.1.1
- Update dependency com.google.firebase:firebase-bom to v33.7.0
- Update fast.adapter to v5.7.0
- Downgrade dependency org.conscrypt:conscrypt-android to v2.5.2
## [1.9.0]
### Additions
- Sync DoH provider list with upstream (added Mullvad, Control D, Njalla, and Shecan)
- Add option to enable verbose logging
- Add category hopper long-press action to open random series from **any** category
- Add option to enable reader debug mode
- Add option to adjust reader's hardware bitmap threshold (@AntsyLich)
- Always use software bitmap on certain devices (@MajorTanya)
- Add option to scan local entries from `/storage/(sdcard|emulated/0)/Android/data/<yokai>/files/local`
### Changes
- Enable 'Split Tall Images' by default (@Smol-Ame)
- Minor visual adjustments
- Tell user to restart the app when User-Agent is changed (@NGB-Was-Taken)
- Re-enable fetching licensed manga (@Animeboynz)
- Bangumi search now shows the score and summary of a search result (@MajorTanya)
- Logs are now written to a file for easier debugging
- Bump default user agent (@AntsyLich)
- Custom cover is now compressed to WebP to prevent OOM crashes
### Fixes
- Fix only few DoH provider is actually being used (Cloudflare, Google, AdGuard, and Quad9)
- Fix "Group by Ungrouped" showing duplicate entries
- Fix reader sometimes won't load images
- Handle some uncaught crashes
- Fix crashes due to GestureDetector's firstEvent is sometimes null on some devices
- Fix download failed due to invalid XML 1.0 character
- Fix issues with shizuku in a multi-user setup (@Redjard)
- Fix some regional/variant languages is not listed in app language option
- Fix browser not opening in some cases in Honor devices (@MajorTanya)
- Fix "ConcurrentModificationException" crashes
- Fix Komga unread badge, again
- Fix default category can't be updated manually
- Fix crashes trying to load Library caused by cover being too large
### Other
- Simplify network helper code
- Fully migrated from StorIO to SQLDelight
- Update dependency com.android.tools:desugar_jdk_libs to v2.1.3
- Update moko to v0.24.4
- Refactor trackers to use DTOs (@MajorTanya)
- Fix AniList `ALSearchItem.status` nullibility (@Secozzi)
- Replace Injekt with Koin
- Remove unnecessary permission added by Firebase
- Remove unnecessary features added by Firebase
- Replace BOM dev.chrisbanes.compose:compose-bom with JetPack's BOM
- Update dependency androidx.compose:compose-bom to v2024.11.00
- Update dependency com.google.firebase:firebase-bom to v33.6.0
- Update dependency com.squareup.okio:okio to v3.9.1
- Update activity to v1.9.3
- Update lifecycle to v2.8.7
- Update dependency me.zhanghai.android.libarchive:library to v1.1.4
- Update agp to v8.7.3
- Update junit5 monorepo to v5.11.3
- Update dependency androidx.test.ext:junit to v1.2.1
- Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.3.8
- Update dependency org.jsoup:jsoup to v1.18.1
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.9.0
- Update serialization to v1.7.3
- Update dependency gradle to v8.11.1
- Update dependency androidx.webkit:webkit to v1.12.0
- Update dependency io.mockk:mockk to v1.13.13
- Update shizuku to v13.1.5
- Use reflection to fix shizuku breaking changes (@Jobobby04)
- Bump compile sdk to 35
- Handle Android SDK 35 API collision (@AntsyLich)
- Update kotlin monorepo to v2.0.21
- Update dependency androidx.work:work-runtime-ktx to v2.10.0
- Update dependency androidx.core:core-ktx to v1.15.0
- Update dependency io.coil-kt.coil3:coil-bom to v3.0.4
- Update xml.serialization to v0.90.3
- Update dependency co.touchlab:kermit to v2.0.5
- Replace WebView to use Compose (@arkon)
- Fixed Keyboard is covering web page inputs
- Increased `tryToSetForeground` delay to fix potential crashes (@nonproto)
- Update dependency org.conscrypt:conscrypt-android to v2.5.3
- Port upstream's download cache system
## [1.8.5.13]
### Fixed
- Fix version checker
## [1.8.5.12]
### Fixed
- Fixed scanlator data sometimes disappear
## [1.8.5.11]
### Fixed
- Fixed crashes caused by Bangumi invalid status
## [1.8.5.10]
### Fixes
- Fixed scanlator filter not working properly
## [1.8.5.9]
### Changes
- Revert create backup to use file picker
## [1.8.5.8]
### Other
- Separate backup error log when destination is null or not a file
- Replace com.github.inorichi.injekt with com.github.null2264.injekt
## [1.8.5.7]
### Fixes
- Fixed more NPE crashes
## [1.8.5.6]
### Fixes
- Fixed NPE crash on tablets
## [1.8.5.5]
### Fixes
- Fixed crashes caused by certain extension implementation
- Fixed "Theme buttons based on cover" doesn't work properly
- Fixed library cover images looks blurry then become sharp after going to
entry's detail screen
### Other
- More StorIO to SQLDelight migration effort
- Update dependency dev.chrisbanes.compose:compose-bom to v2024.08.00-alpha02
- Update kotlin monorepo to v2.0.20
- Update aboutlibraries to v11.2.3
- Remove dependency com.github.leandroBorgesFerreira:LoadingButtonAndroid
## [1.8.5.4]
### Fixes
- Fixed custom cover set from reader didn't show up on manga details
## [1.8.5.3]
### Additions
- Add toggle to enable/disable chapter swipe action(s)
- Add toggle to enable/disable webtoon double tap to zoom
### Changes
- Custom cover now shown globally
### Fixes
- Fixed chapter number parsing (@Naputt1)
- Reduced library flickering (still happened in some cases when the cached image size is too different from the original image size, but should be reduced quite a bit)
- Fixed entry details header didn't update when being removed from library
### Other
- Refactor chapter recognition (@stevenyomi)
- (Re)added unit test for chapter recognition
- More StorIO to SQLDelight migration effort
- Target Android 15
- Adjust manga cover cache key
- Refactor manga cover fetcher (@ivaniskandar, @AntsyLich, @null2264)
## [1.8.5.2]
### Fixes
- Fixed some preference not being saved properly
### Other
- Update dependency co.touchlab:kermit to v2.0.4
- Update lifecycle to v2.8.4
## [1.8.5.1]
### Fixes
- Fixed library showing duplicate entry when using dynamic category
## [1.8.5]
### Additions
- Add missing "Max automatic backups" option on experimental Data and Storage setting menu
- Add information on when was the last time backup automatically created to experimental Data and Storage setting menu
- Add monochrome icon
### Changes
- Add more info to WorkerInfo page
- Added "next scheduled run"
- Added attempt count
- `english` tag no longer cause reading mode to switch to LTR (mangkoran)
- `english` tag no longer cause reading mode to switch to LTR (@mangkoran)
- `chinese` tag no longer cause reading mode to switch to LTR
- `manhua` tag no longer cause reading mode to switch to LTR
- Local source manga's cover now being invalidated on refresh
- It is now possible to create a backup without any entries using experimental Data and Storage setting menu
- Increased default maximum automatic backup files to 5
- It is now possible to edit a local source entry without adding it to library
- Long Strip and Continuous Vertical background color now respect user setting
- Display Color Profile setting no longer limited to Android 8 or newer
- Increased long strip cache size to 4 for Android 8 or newer (@FooIbar)
- Use Coil pipeline to handle HEIF images
## Fixes
### Fixes
- Fixed auto backup, auto extension update, and app update checker stop working
if it crash/failed
- Fixed crashes when trying to reload extension repo due to connection issue
- Fixed tap controls not working properly after zoom (@arkon, @Paloys, @FooIbar)
- Fixed (sorta, more like workaround) ANR issues when running background tasks, such as updating extensions (@ivaniskandar)
- Fixed split (downloaded) tall images sometimes doesn't work
- Fixed status bar stuck in dark mode when app is following system theme
- Fixed splash screen state only getting updates if library is empty (Should slightly reduce splash screen duration)
- Fixed kitsu tracker issue due to domain change
- Fixed entry custom cover won't load if entry doesn't have cover from source
- Fixed unread badge doesn't work properly for some sources (notably Komga)
- Fixed MAL start date parsing (@MajorTanya)
## Translation
- Update Japanese translation (akir45)
- Update Brazilian Portuguese translation (AshbornXS)
- Update Filipino translation (infyProductions)
### Translation
- Update Japanese translation (@akir45)
- Update Brazilian Portuguese translation (@AshbornXS)
- Update Filipino translation (@infyProductions)
## Other
### Other
- Re-added several social media links to Mihon
- Some code refactors
- Simplify some messy code
- Rewrite version checker
- Rewrite Migrator
- Rewrite Migrator (@ghostbear)
- Split the project into several modules
- Migrated i18n to use Moko Resources
- Removed unnecessary dependencies (@null2264, @nonproto)
- Update firebase bom to v33.1.0
- Replace com.google.android.gms:play-services-oss-licenses with com.mikepenz:aboutlibraries
- Update dependency com.google.gms:google-services to v4.4.2
@ -41,14 +417,401 @@
- More StorIO to SQLDelight migrations
- Merge lastFetch and lastRead query into library_view VIEW
- Migrated a few more chapter related queries
- Migrated most of manga related queries
- Migrated most of the manga related queries
- Bump dependency com.github.tachiyomiorg:unifile revision to a9de196cc7
- Update project to Kotlin 2.0
- Update compose bom to v2024.07.00-alpha02
- Refactor archive support to use `libarchive`
- Update project to Kotlin 2.0 (v2.0.10)
- Update compose bom to v2024.08.00-alpha01
- Refactor archive support to use `libarchive` (@FooIbar)
- Use version catalog for gradle plugins
- Update dependency org.jsoup:jsoup to v1.7.1
- Bump dependency com.github.tachiyomiorg:image-decoder revision to 41c059e540
- Update dependency io.coil-kt.coil3 to v3.0.0-alpha08
- Update Android Gradle Plugin to v8.5.1
- Update dependency io.coil-kt.coil3 to v3.0.0-alpha10
- Update Android Gradle Plugin to v8.5.2
- Update gradle to v8.9
- Start using Voyager for navigation
- Update dependency androidx.work:work-runtime-ktx to v2.9.1
- Update dependency androidx.annotation:annotation to v1.8.2
## [1.8.4.6]
### Fixes
- Fixed scanlator filter not working properly if it contains " & "
### Other
- Removed dependency com.dmitrymalkovich.android:material-design-dimens
- Replace dependency br.com.simplepass:loading-button-android with
com.github.leandroBorgesFerreira:LoadingButtonAndroid
- Replace dependency com.github.florent37:viewtooltip with
com.github.CarlosEsco:ViewTooltip
## [1.8.4.5]
### Fixes
- Fixed incorrect library entry chapter count
## [1.8.4.4]
### Fixes
- Fixed incompatibility issue with J2K backup file
## [1.8.4.3]
### Fixes
- Fixed "Open source repo" icon's colour
## [1.8.4.2]
### Changes
- Changed "Open source repo" icon to prevent confusion
## [1.8.4.1]
### Fixes
- Fixed saving combined pages not doing anything
## [1.8.4]
### Additions
- Added option to change long tap browse and recents nav behaviour
- Added browse long tap behaviour to open global search (@AshbornXS)
- Added recents long tap behaviour to open last read chapter (@AshbornXS)
- Added option to backup sensitive settings (such as tracker login tokens)
- Added beta version of "Data and storage" settings (can be accessed by long tapping "Data and storage")
### Changes
- Remove download location redirection from `Settings > Downloads`
- Moved cache related stuff from `Settings > Advanced` to `Settings > Data and storage`
- Improve webview (@AshbornXS)
- Show url as subtitle
- Add option to clear cookies
- Allow zoom
- Handle urls on global search (@AshbornXS)
- Improve download queue (@AshbornXS)
- Download badge now show download queue count
- Add option to move series to bottom
- Only show "open repo url" button when repo url is not empty
### Fixes
- Fix potential crashes for some custom Android rom
- Allow MultipartBody.Builder for extensions
- Refresh extension repo now actually refresh extension(s) trust status
- Custom manga info now relink properly upon migration
- Fixed extension repo list did not update when a repo is added via deep link
- Fixed download unread trying to download filtered (by scanlator) chapters
- Fixed extensions not retaining their repo url
- Fixed more NullPointerException crashes
- Fixed split layout caused non-split images to not load
### Other
- Migrate some StorIO queries to SQLDelight, should improve stability
- Migrate from Timber to Kermit
- Update okhttp monorepo to v5.0.0-alpha.14
- Refactor backup code
- Migrate backup flags to not use bitwise
- Split it to several smaller classes
- Update androidx.compose.material3:material3 to v1.3.0-beta02
## [1.8.3.4]
### Fixes
- Fixed crashes caused by invalid ComicInfo XML
If this caused your custom manga info to stop working, try resetting it by deleting `ComicInfoEdits.xml` file located in `Android/data/eu.kanade.tachiyomi.yokai`
- Fixed crashes caused by the app trying to round NaN value
## [1.8.3.3]
### Changes
- Crash report can now actually be disabled
### Other
- Loading GlobalExceptionHandler before Crashlytics
## [1.8.3.2]
### Other
- Some more NullPointerException prevention that I missed
## [1.8.3.1]
### Other
- A bunch of NullPointerException prevention
## [1.8.3]
### Additions
- Extensions now can be trusted by repo
### Changes
- Extensions now required to have `repo.json`
### Other
- Migrate to SQLDelight
- Custom manga info is now stored in the database
## [1.8.2]
### Additions
- Downloaded chapters now include ComicInfo file
- (LocalSource) entry chapters' info can be edited using ComicInfo
### Fixes
- Fixed smart background colour by page failing causing the image to not load
- Fixed downloaded chapter can't be opened if it's too large
- Downloaded page won't auto append chapter ID even tho the option is enabled
### Other
- Re-route nightly to use its own repo, should fix "What's new" page
## [1.8.1.2]
### Additions
- Added a couple new tags to set entry as SFW (`sfw` and `non-erotic`)
### Fixes
- Fixed smart background colour by page failing causing the image to not load
### Other
- Re-route nightly to use its own repo, should fix "What's new" page
## [1.8.1.1]
### Fixes
- Fixed crashes when user try to edit an entry
## [1.8.1]
### Additions
- (Experimental) Option to append chapter ID to download filename to avoid conflict
### Changes
- Changed notification icon to use Yōkai's logo instead
- Yōkai is now ComicInfo compliant. [Click here to learn more](https://anansi-project.github.io/docs/comicinfo/intro)
- Removed "Couldn't split downloaded image" notification to reduce confusion. It has nothing to do with unsuccessful split, it just think it shouldn't split the image
### Fixes
- Fixed not being able to open different chapter when a chapter is already opened
- Fixed not being able to read chapters from local source
- Fixed local source can't detect archives
### Other
- Wrap SplashState to singleton factory, might fix issue where splash screen shown multiple times
- Use Okio instead of `java.io`, should improve reader stability (especially long strip)
## [1.8.0.2]
### Fixes
- Fixed app crashes when backup directory is null
- Fixed app asking for All Files access permission when it's no longer needed
## [1.8.0.1]
### Additions
- Added CrashScreen
### Fixes
- Fixed version checker for nightly against hotfix patch version
- Fixed download cache causes the app to crash
## [1.8.0]
### Additions
- Added cutout support for some pre-Android P devices
- Added option to add custom colour profile
- Added onboarding screen
### Changes
- Permanently enable 32-bit colour mode
- Unified Storage™ ([Click here](https://mihon.app/docs/faq/storage#migrating-from-tachiyomi-v0-14-x-or-earlier) to learn more about it)
### Fixes
- Fixed cutout behaviour for Android P
- Fixed some extensions doesn't detect "added to library" entries properly ([GH-40](https://github.com/null2264/yokai/issues/40))
- Fixed nightly and debug variant doesn't include their respective prefix on their app name
- Fixed nightly version checker
### Other
- Update dependency com.github.tachiyomiorg:image-decoder to e08e9be535
- Update dependency com.github.null2264:subsampling-scale-image-view to 338caedb5f
- Added Unit Test for version checker
- Use Coil pipeline instead of SSIV for image decode whenever possible, might improve webtoon performance
- Migrated from Coil2 to Coil3
- Update compose compiler to v1.5.14
- Update dependency androidx.compose.animation:animation to v1.6.7
- Update dependency androidx.compose.foundation:foundation to v1.6.7
- Update dependency androidx.compose.material:material to v1.6.7
- Update dependency androidx.compose.ui:ui to v1.6.7
- Update dependency androidx.compose.ui:ui-tooling to v1.6.7
- Update dependency androidx.compose.ui:ui-tooling-preview to v1.6.7
- Update dependency androidx.compose.material:material-icons-extended to v1.6.7
- Update dependency androidx.lifecycle:lifecycle-viewmodel-compose to v2.8.0
- Update dependency androidx.activity:activity-ktx to v1.9.0
- Update dependency androidx.activity:activity-compose to v1.9.0
- Update dependency androidx.annotation:annotation to v1.8.0
- Update dependency androidx.browser:browser to v1.8.0
- Update dependency androidx.core:core-ktx to v1.13.1
- Update dependency androidx.lifecycle:lifecycle-viewmodel-ktx to v2.8.0
- Update dependency androidx.lifecycle:lifecycle-livedata-ktx to v2.8.0
- Update dependency androidx.lifecycle:lifecycle-common to v2.8.0
- Update dependency androidx.lifecycle:lifecycle-process to v2.8.0
- Update dependency androidx.lifecycle:lifecycle-runtime-ktx to v2.8.0
- Update dependency androidx.recyclerview:recyclerview to v1.3.2
- Update dependency androidx.sqlite:sqlite to v2.4.0
- Update dependency androidx.webkit:webkit to v1.11.0
- Update dependency androidx.work:work-runtime-ktx to v2.9.0
- Update dependency androidx.window:window to v1.2.0
- Update dependency com.google.firebase:firebase-crashlytics-gradle to v3.0.1
- Update dependency com.google.gms:google-services to v4.4.1
- Update dependency com.google.android.material:material to v1.12.0
- Update dependency com.squareup.okio:okio to v3.8.0
- Update dependency com.google.firebase:firebase-bom to v33.0.0
- Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v1.9.24
- Update dependency org.jetbrains.kotlin:kotlin-serialization to v1.9.24
- Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.2
- Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json-okio to v1.6.2
- Update dependency org.jetbrains.kotlinx:kotlinx-serialization-protobuf to v1.6.2
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-android to v1.8.0
- Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-core to v1.8.0
- Resolved some compile warnings
- Update dependency com.github.tachiyomiorg:unifile to 7c257e1c64
## [1.7.14]
### Changes
- Added splash to reader (in case it being opened from shortcut)
- Increased long strip split height
- Use normalized app name by default as folder name
### Fixes
- Fixed cutout support being broken
### Other
- Move AppState from DI to Application class to reduce race condition
## [1.7.13]
### Additions
- Ported Tachi's cutout option
- Added Doki theme (dark only)
### Changes
- Repositioned cutout options in settings
- Splash icon now uses coloured variant of the icon
- Removed deep link for sources, this should be handled by extensions
- Removed braces from nightly (and debug) app name
### Fixes
- Fixed preference summary not updating after being changed once
- Fixed legacy appbar is visible on compose when being launched from deeplink
- Fixed some app icon not generated properly
- Fixed splash icon doesn't fit properly on Android 12+
### Other
- Migrate to using Android 12's SplashScreen API
- Clean up unused variables from ExtensionInstaller
## [1.7.12]
### Additions
- Scanlator filter is now being backed up (@jobobby04)
### Fixes
- Fixed error handling for MAL tracking (@AntsyLich)
- Fixed extension installer preference incompatibility with modern Tachi
### Other
- Split PreferencesHelper even more
- Simplify extension install issue fix (@AwkwardPeak7)
- Update dependency com.github.tachiyomiorg:image-decoder to fbd6601290
- Replace dependency com.github.jays2kings:subsampling-scale-image-view with com.github.null2264:subsampling-scale-image-view
- Update dependency com.github.null2264:subsampling-scale-image-view to e3cffd59c5
## [1.7.11]
### Fixes
- Fixed MAL tracker issue (@AntsyLich)
- Fixed trusting extension caused it to appear twice
### Other
- Change Shikimori client from Tachi's to Yōkai's
- Move TrackPreferences to PreferenceModule
## [1.7.10]
### Addition
- Content type filter to hide SFW/NSFW entries
- Confirmation before revoking all trusted extension
### Changes
- Revert Webcomic -> Webtoon
### Fixes
- Fix app bar disappearing on (scrolled) migration page
- Fix installed extensions stuck in "installable" state
- Fix untrusted extensions not having an icon
### Other
- Changed (most) trackers' client id and secret
- Add or changed user-agent for trackers
## [1.7.9]
### Other
- Sync project with J2K [v1.7.4](https://github.com/Jays2Kings/tachiyomiJ2K/releases/tag/v1.7.4)
## [1.7.8]
### Changes
- Local source now try to find entries not only in `Yōkai/` but also in `Yokai/` and `TachiyomiJ2K/` for easier migration
### Other
- Changed AniList and MAL clientId, you may need to logout and re-login
## [1.7.7]
### Changes
- Hopper icon now changes depending on currently active group type (J2K)
### Fixes
- Fixed bookmarked entries not being detected as bookmarked on certain extensions
## [1.7.6]
### Additions
- Shortcut to Extension Repos from Browser -> Extensions page
- Added confirmation before extension repo deletion
### Changes
- Adjusted dialogs background colour to be more consistent with app theme
### Fixes
- Fixed visual glitch where page sometime empty on launch
- Fixed extension interceptors receiving compressed responses (T)
### Other
- Newly added strings from v1.7.5 is now translatable
## [1.7.5]
### Additions
- Ported custom extension repo from upstream
### Changes
- Removed built-in extension repo
- Removed links related to Tachiyomi
- Ported upstream's trust extension logic
- Rebrand to Yōkai
### Other
- Start migrating to Compose
## [1.7.4]
### Changes
- Rename project to Yōkai (Z)
- Replace Tachiyomi's purged extensions with Keiyoushi extensions (Temporary solution until I ported custom extension repo feature) (Z)
- Unread count now respect scanlator filter (J2K)
### Fixes
- Fixed visual glitch on certain page (J2K)

View file

@ -1,7 +1,7 @@
<div align="center">
<a href="https://github.com/null2264/yokai">
<img src="./.github/readme-images/app-icon.png" alt="Yokai logo" height="200px" width="200px" />
<img src="./.github/readme-images/app-icon.webp" alt="Yokai logo" height="200px" width="200px" />
</a>
# Yōkai
@ -12,11 +12,15 @@
A free and open source manga reader
[![CI](https://github.com/null2264/yokai/actions/workflows/build_push.yml/badge.svg)](https://github.com/null2264/yokai/actions/workflows/build_push.yml)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](/LICENSE)
[![Mirror: GitLab](https://img.shields.io/badge/mirror-GitLab-orange.svg)](https://gitlab.com/null2264/yokai)
[![Discord: Mihon](https://img.shields.io/discord/1195734228319617024.svg?label=&labelColor=6A7EC2&color=7389D8&logo=discord&logoColor=FFFFFF)](https://discord.gg/mihon)
[![Mirror: GitLab](https://img.shields.io/badge/mirror-GitLab-orange.svg?labelColor=27303D)](https://gitlab.com/null2264/yokai)
[![Mirror: git.aap](https://img.shields.io/badge/mirror-git.aap-red.svg?labelColor=27303D)](https://git.aap.my.id/null2264/yokai)
<img src="./.github/readme-images/screens.gif" alt="Yokai screenshot" height="412px" width="800px" />
[![CI](https://github.com/null2264/yokai/actions/workflows/build_push.yml/badge.svg?labelColor=27303D)](https://github.com/null2264/yokai/actions/workflows/build_push.yml)
[![License: Apache-2.0](https://img.shields.io/github/license/null2264/yokai?labelColor=27303D&color=0877d2)](/LICENSE)
[![Translation status](https://img.shields.io/weblate/progress/yokai?labelColor=27303D&color=946300)](https://hosted.weblate.org/engage/yokai/)
<img src="./.github/readme-images/screens.gif" alt="Yokai screenshots" />
## Download
@ -31,10 +35,22 @@ This fork was created for personal usage, the name Yōkai is chosen in theme of
Updates are sporadic, sometime fast, sometime slow.
As of the time of writing, this fork is currently focusing on migrating to a much more modern infrastructure, some features may be added, but most changes are happening behind the scene.
## Features
<div align="left">
<details open="">
<summary><h3>From Yōkai</h3></summary>
* NSFW/SFW library filter (taken from [TachiyomiSY](https://github.com/jobobby04/TachiyomiSY)).
* Fix backup incompatibility with upstream.
* New theme.
* Local Source chapters now reads ComicInfo.xml for chapter title, number, and scanlator.
</details>
<details open="">
<summary><h3>From upstream (Tachiyomi/Mihon)</h3></summary>
@ -43,7 +59,7 @@ Updates are sporadic, sometime fast, sometime slow.
* Tracker support:
[MyAnimeList](https://myanimelist.net/),
[AniList](https://anilist.co/),
[Kitsu](https://kitsu.io/explore/anime),
[Kitsu](https://kitsu.app/explore/anime),
[Manga Updates](https://www.mangaupdates.com/),
[Shikimori](https://shikimori.one),
and [Bangumi](https://bgm.tv/) support.
@ -79,14 +95,6 @@ Updates are sporadic, sometime fast, sometime slow.
</details>
<details>
<summary><h3>From Yōkai</h3></summary>
* New theme
* NSFW/SFW filter
</details>
</div>
## Contributing
@ -111,8 +119,8 @@ Pull requests are welcome. For major changes, please open an issue first to disc
* If it could be device-dependent, try reproducing on another device (if possible).
* For large logs use [Pastebin](https://pastebin.com/) (or similar).
* Don't group unrelated requests into one issue.
- **DO**: [Example #1](https://web.archive.org/web/20240117150729/https://github.com/tachiyomiorg/tachiyomi/issues/24), [Example #2](https://web.archive.org/web/20240117150735/https://github.com/tachiyomiorg/tachiyomi/issues/71).
- **DON'T**: [Example #1](https://web.archive.org/web/20240117150741/https://github.com/tachiyomiorg/tachiyomi/issues/75).
- **DO**: [Example #1](https://git.mihon.tech/tachiyomi/tachiyomi/issues/24), [Example #2](https://git.mihon.tech/tachiyomi/tachiyomi/issues/71).
- **DON'T**: [Example #1](https://git.mihon.tech/tachiyomi/tachiyomi/issues/75).
</details>
@ -141,8 +149,8 @@ The developer(s) of this application does not have any affiliation with the cont
### License
<pre>
Copyright 2015 Javier Tomás
Copyright 2024 null2264
Copyright © 2015 Javier Tomás
Copyright © 2024 null2264
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -1,15 +1,13 @@
import com.google.firebase.crashlytics.buildtools.gradle.CrashlyticsPlugin
import com.google.gms.googleservices.GoogleServicesPlugin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.ByteArrayOutputStream
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(androidx.plugins.application)
alias(kotlinx.plugins.android)
alias(kotlinx.plugins.compose.compiler)
id("yokai.android.application")
id("yokai.android.application.compose")
alias(kotlinx.plugins.serialization)
alias(kotlinx.plugins.parcelize)
alias(libs.plugins.aboutlibraries)
@ -17,30 +15,27 @@ plugins {
alias(libs.plugins.google.services) apply false
}
if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
if (gradle.startParameter.taskRequests.toString().contains("standard", true)) {
apply<CrashlyticsPlugin>()
apply<GoogleServicesPlugin>()
}
fun runCommand(command: String): String {
val byteOut = ByteArrayOutputStream()
project.exec {
commandLine = command.split(" ")
standardOutput = byteOut
}
return String(byteOut.toByteArray()).trim()
val result = providers.exec { commandLine(command.split(" ")) }
return result.standardOutput.asText.get().trim()
}
val _versionName = "1.8.5"
@Suppress("PropertyName")
val _versionName = "1.10.0"
val betaCount by lazy {
val betaTags = runCommand("git tag -l --sort=refname v${_versionName}-b*")
String.format("%02d", if (betaTags.isNotEmpty()) {
if (betaTags.isNotEmpty()) {
val betaTag = betaTags.split("\n").last().substringAfter("-b").toIntOrNull()
((betaTag ?: 0) + 1)
} else {
1
})
}.toString()
}
val commitCount by lazy { runCommand("git rev-list --count HEAD") }
val commitHash by lazy { runCommand("git rev-parse --short HEAD") }
@ -54,7 +49,7 @@ val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android {
defaultConfig {
applicationId = "eu.kanade.tachiyomi"
versionCode = 140
versionCode = 158
versionName = _versionName
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled = true
@ -72,11 +67,6 @@ android {
//noinspection ChromeOsAbiSupport
abiFilters += supportedAbis
}
externalNativeBuild {
cmake {
this.arguments("-DHAVE_LIBJXL=FALSE")
}
}
}
splits {
@ -122,7 +112,6 @@ android {
buildFeatures {
viewBinding = true
compose = true
// If you're here because there's not BuildConfig, build the app first, it'll generate it for you
buildConfig = true
@ -156,7 +145,8 @@ android {
}
dependencies {
implementation(projects.core)
implementation(projects.core.archive)
implementation(projects.core.main)
implementation(projects.data)
implementation(projects.domain)
implementation(projects.i18n)
@ -168,13 +158,7 @@ dependencies {
implementation(compose.bundles.compose)
debugImplementation(compose.ui.tooling)
implementation(libs.compose.theme.adapter3)
implementation(libs.accompanist.webview)
// Modified dependencies
implementation(libs.subsamplingscaleimageview) {
exclude(module = "image-decoder")
}
implementation(libs.image.decoder)
implementation(compose.webview)
implementation(libs.flexbox)
@ -213,25 +197,23 @@ dependencies {
// HTML parser
implementation(libs.jsoup)
// Job scheduling
implementation(libs.guava)
implementation(libs.play.services.gcm)
// Database
implementation(libs.sqlite.android)
implementation(libs.bundles.sqlite)
//noinspection UseTomlInstead
implementation("com.github.inorichi.storio:storio-common:8be19de@aar")
//noinspection UseTomlInstead
implementation("com.github.inorichi.storio:storio-sqlite:8be19de@aar")
// Model View Presenter
implementation(libs.nucleus)
implementation(libs.nucleus.support.v7)
implementation(libs.conductor)
implementation(libs.conductor.support.preference)
// Image library
implementation(platform(libs.coil3.bom))
implementation(libs.bundles.coil)
implementation(libs.subsamplingscaleimageview) { // modified
exclude(module = "image-decoder")
}
implementation(libs.image.decoder)
// Sort
implementation(libs.java.nat.sort)
@ -239,8 +221,6 @@ dependencies {
implementation(libs.aboutlibraries)
// UI
implementation(libs.material.design.dimens)
implementation(libs.loading.button)
implementation(libs.fastadapter)
implementation(libs.fastadapter.extensions.binding)
implementation(libs.flexible.adapter)
@ -254,22 +234,16 @@ dependencies {
implementation(libs.viewtooltip)
implementation(libs.taptargetview)
// Conductor
implementation(libs.conductor)
implementation(libs.conductor.support.preference)
// Navigation
implementation(libs.bundles.voyager)
// Shizuku
implementation(libs.shizuku.api)
implementation(libs.shizuku.provider)
implementation(kotlin("stdlib", org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION))
implementation(platform(kotlinx.coroutines.bom))
implementation(kotlinx.bundles.coroutines)
// Text distance
implementation(libs.java.string.similarity)
// TLS 1.3 support for Android < 10
implementation(libs.conscrypt)
@ -283,23 +257,26 @@ dependencies {
testRuntimeOnly(libs.bundles.test.runtime)
androidTestImplementation(libs.bundles.test.android)
testImplementation(kotlinx.coroutines.test)
// For detecting memory leaks
// REF: https://square.github.io/leakcanary/
debugImplementation(libs.leakcanary.android)
implementation(libs.leakcanary.plumber)
}
tasks {
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> {
compilerOptions.freeCompilerArgs.addAll(
"-Xcontext-receivers",
// "-opt-in=kotlin.Experimental",
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
// "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi",
// "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
@ -307,19 +284,6 @@ tasks {
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
)
if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") {
compilerOptions.freeCompilerArgs.addAll(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
(project.layout.buildDirectory.asFile.orNull?.absolutePath ?: "/tmp/yokai") + "/compose_metrics",
)
compilerOptions.freeCompilerArgs.addAll(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
(project.layout.buildDirectory.asFile.orNull?.absolutePath ?: "/tmp/yokai") + "/compose_metrics",
)
}
}
// Duplicating Hebrew string assets due to some locale code issues on different devices

View file

@ -17,6 +17,7 @@
-keep,allowoptimization class com.google.gson.** { public protected *; }
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
-keep,allowoptimization class org.koin.** { public protected *; }
-keep,allowoptimization class eu.davidea.flexibleadapter.** { public protected *; }
-keep class io.requery.android.database.** { public protected *; }

View file

@ -257,4 +257,7 @@
</application>
<uses-sdk tools:overrideLibrary="rikka.shizuku.api"
tools:ignore="ManifestOrder" />
</manifest>

View file

@ -22,11 +22,11 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.multidex.MultiDex
import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.Logger
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.SingletonImageLoader
import coil3.memory.MemoryCache
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil3.request.allowHardware
import coil3.request.allowRgb565
@ -34,48 +34,59 @@ import coil3.request.crossfade
import coil3.util.DebugLogger
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager
import eu.kanade.tachiyomi.core.preference.Preference
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher
import eu.kanade.tachiyomi.data.coil.CoilDiskCache
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
import eu.kanade.tachiyomi.data.coil.MangaKeyer
import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.source.SourcePresenter
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
import eu.kanade.tachiyomi.util.system.GLUtil
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.notification
import eu.kanade.tachiyomi.util.system.setToDefault
import java.security.Security
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.conscrypt.Conscrypt
import org.koin.core.context.startKoin
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.core.CrashlyticsLogWriter
import yokai.core.di.AppModule
import yokai.core.di.DomainModule
import yokai.core.di.PreferenceModule
import yokai.core.RollingUniFileLogWriter
import yokai.core.di.appModule
import yokai.core.di.domainModule
import yokai.core.di.initExpensiveComponents
import yokai.core.di.preferenceModule
import yokai.core.migration.Migrator
import yokai.core.migration.migrations.migrations
import yokai.domain.base.BasePreferences
import yokai.domain.storage.StorageManager
import yokai.i18n.MR
import yokai.util.lang.getString
import java.security.Security
open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
val preferences: PreferencesHelper by injectLazy()
val basePreferences: BasePreferences by injectLazy()
val networkPreferences: NetworkPreferences by injectLazy()
private val storageManager: StorageManager by injectLazy()
private val disableIncognitoReceiver = DisableIncognitoReceiver()
@ -83,8 +94,6 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
override fun onCreate() {
super<Application>.onCreate()
if (!BuildConfig.DEBUG) Logger.addLogWriter(CrashlyticsLogWriter())
// TLS 1.3 support for Android 10 and below
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Security.insertProviderAt(Conscrypt.newProvider(), 1)
@ -96,11 +105,21 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
if (packageName != process) WebView.setDataDirectorySuffix(process)
}
Injekt.apply {
importModule(PreferenceModule(this@App))
importModule(AppModule(this@App))
importModule(DomainModule())
startKoin {
modules(preferenceModule(this@App), appModule(this@App), domainModule())
}
initExpensiveComponents(this)
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
val scope = ProcessLifecycleOwner.get().lifecycleScope
networkPreferences.verboseLogging().changes()
.onEach { enabled ->
// FlexibleAdapter.enableLogs(if (enabled) Level.VERBOSE else Level.SUPPRESS)
Logger.setToDefault(buildLogWritersToAdd(storageManager.getLogsDirectory(), enabled))
}
.launchIn(scope)
basePreferences.crashReport().changes()
.onEach {
@ -110,18 +129,23 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
// Probably already enabled/disabled
}
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
.launchIn(scope)
setupNotificationChannels()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
MangaCoverMetadata.load()
preferences.nightMode().changes()
.onEach { AppCompatDelegate.setDefaultNightMode(it) }
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
.launchIn(scope)
ProcessLifecycleOwner.get().lifecycleScope.launchIO {
basePreferences.hardwareBitmapThreshold().let { preference ->
if (!preference.isSet()) preference.set(GLUtil.DEVICE_TEXTURE_LIMIT)
}
basePreferences.hardwareBitmapThreshold().changes()
.onEach { ImageUtil.hardwareBitmapThreshold = it }
.launchIn(scope)
scope.launchIO {
with(TachiyomiWidgetManager()) { this@App.init() }
}
@ -160,7 +184,7 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
notificationManager.cancel(Notifications.ID_INCOGNITO_MODE)
}
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
.launchIn(scope)
initializeMigrator()
}
@ -172,12 +196,7 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
Preference.appStateKey("last_version_code"),
0,
)
// TODO: Remove later
val old = preferenceStore.getInt("last_version_code", -1)
if (old.get() >= preference.get()) {
preference.set(old.get())
old.delete()
}
if (preference.get() < 141) preference.set(0)
Logger.i { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" }
Migrator.initialize(
@ -243,7 +262,6 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
override fun newImageLoader(context: PlatformContext): ImageLoader {
return ImageLoader.Builder(this@App).apply {
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
val diskCacheLazy = lazy { CoilDiskCache.get(this@App) }
components {
// NetworkFetcher.Factory
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
@ -251,17 +269,16 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
add(TachiyomiImageDecoder.Factory())
// Fetcher.Factory
add(BufferedSourceFetcher.Factory())
add(MangaCoverFetcher.Factory(callFactoryLazy, diskCacheLazy))
add(MangaCoverFetcher.MangaFactory(callFactoryLazy))
add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy))
// Keyer
add(MangaKeyer())
add(MangaCoverKeyer())
}
diskCache(diskCacheLazy::value)
memoryCache { MemoryCache.Builder().maxSizePercent(this@App, 0.40).build() }
crossfade(true)
allowRgb565(this@App.getSystemService<ActivityManager>()!!.isLowRamDevice)
allowHardware(true)
if (BuildConfig.DEBUG) {
if (networkPreferences.verboseLogging().get()) {
logger(DebugLogger())
}
@ -272,4 +289,18 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
}
}
fun buildLogWritersToAdd(logPath: UniFile?): List<LogWriter> {
val networkPreferences: NetworkPreferences = Injekt.get()
return buildLogWritersToAdd(logPath, networkPreferences.verboseLogging().get())
}
fun buildLogWritersToAdd(
logPath: UniFile?,
isVerbose: Boolean,
) = buildList {
if (!BuildConfig.DEBUG) add(CrashlyticsLogWriter())
// if (logPath != null && !BuildConfig.DEBUG) add(RollingUniFileLogWriter(logPath = logPath, isVerbose = isVerbose))
}
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"

View file

@ -37,10 +37,16 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.launchIO
import kotlinx.coroutines.MainScope
import uy.kohesive.injekt.injectLazy
import java.util.*
import java.util.Calendar
import java.util.Date
import kotlin.math.min
import kotlin.math.roundToLong
import kotlinx.coroutines.MainScope
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.models.cover
import yokai.domain.recents.interactor.GetRecents
class UpdatesGridGlanceWidget : GlanceAppWidget() {
private val app: Application by injectLazy()
@ -62,6 +68,33 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() {
}
}
// FIXME: Don't depends on RecentsPresenter
private suspend fun getUpdates(customAmount: Int = 0, getRecents: GetRecents = Injekt.get()): List<Pair<Manga, Long>> {
return getRecents
.awaitUpdates(
limit = when {
customAmount > 0 -> (customAmount * 1.5).roundToLong()
else -> 25L
}
)
.mapNotNull {
when {
it.chapter.read || it.chapter.id == null -> RecentsPresenter.getNextChapter(it.manga)
it.history.id == null -> RecentsPresenter.getFirstUpdatedChapter(it.manga, it.chapter)
else -> it.chapter
} ?: return@mapNotNull null
it
}
.asSequence()
.distinctBy { it.manga.id }
.sortedByDescending { it.history.last_read }
// nChapterItems + nAdditionalItems + cReadingItems
.take((RecentsPresenter.UPDATES_CHAPTER_LIMIT * 2) + RecentsPresenter.UPDATES_READING_LIMIT_LOWER)
.filter { it.manga.id != null }
.map { it.manga to it.history.last_read }
.toList()
}
fun loadData(list: List<Pair<Manga, Long>>? = null) {
coroutineScope.launchIO {
// Don't show anything when lock is active
@ -78,7 +111,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() {
.flatMap { manager.getAppWidgetSizes(it) }
.maxBy { it.height.value * it.width.value }
.calculateRowAndColumnCount()
val processList = list ?: RecentsPresenter.getRecentManga(customAmount = min(50, rowCount * columnCount))
val processList = list ?: getUpdates(customAmount = min(50, rowCount * columnCount))
data = prepareList(processList, rowCount * columnCount)
ids.forEach { update(app, it) }
@ -97,7 +130,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() {
.map { it.first }
.map { updatesView ->
val request = ImageRequest.Builder(app)
.data(updatesView)
.data(updatesView.cover())
.memoryCachePolicy(CachePolicy.DISABLED)
.precision(Precision.EXACT)
.size(widthPx, heightPx)

View file

@ -17,15 +17,15 @@ import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import androidx.glance.unit.ColorProvider
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import eu.kanade.tachiyomi.appwidget.ContainerModifier
import eu.kanade.tachiyomi.appwidget.util.stringResource
import eu.kanade.tachiyomi.ui.main.MainActivity
import yokai.i18n.MR
import yokai.presentation.core.Constants
@Composable
fun LockedWidget() {
val context = LocalContext.current
val intent = Intent(LocalContext.current, Class.forName(MainActivity.MAIN_ACTIVITY)).apply {
val intent = Intent(LocalContext.current, Class.forName(Constants.MAIN_ACTIVITY)).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
Box(

View file

@ -11,8 +11,6 @@ import androidx.glance.layout.ContentScale
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.size
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import eu.kanade.tachiyomi.appwidget.util.appWidgetInnerRadius
val CoverWidth = 58.dp

View file

@ -17,19 +17,18 @@ import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.text.Text
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import eu.kanade.tachiyomi.appwidget.ContainerModifier
import eu.kanade.tachiyomi.appwidget.util.calculateRowAndColumnCount
import eu.kanade.tachiyomi.appwidget.util.stringResource
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.SearchActivity
import yokai.i18n.MR
import yokai.presentation.core.Constants
@Composable
fun UpdatesWidget(data: List<Pair<Long, Bitmap?>>?) {
val (rowCount, columnCount) = LocalSize.current.calculateRowAndColumnCount()
val mainIntent = Intent(LocalContext.current, MainActivity::class.java).setAction(MainActivity.SHORTCUT_RECENTS)
val mainIntent = Intent(LocalContext.current, MainActivity::class.java).setAction(Constants.SHORTCUT_RECENTS)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
Column(
modifier = ContainerModifier.clickable(actionStartActivity(mainIntent)),

View file

@ -5,9 +5,17 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import eu.kanade.tachiyomi.core.preference.Preference
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Locale
@Composable
fun <T> Preference<T>.collectAsState(): State<T> {
val flow = remember(this) { changes() }
return flow.collectAsState(initial = get())
}
fun String.asDateFormat(): DateFormat = when (this) {
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
else -> SimpleDateFormat(this, Locale.getDefault())
}

View file

@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.BackupUtil
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.i18n.MR
import yokai.util.lang.getString
class BackupFileValidator(
@ -28,11 +27,7 @@ class BackupFileValidator(
throw IllegalStateException(e)
}
if (backup.backupManga.isEmpty()) {
throw IllegalStateException(context.getString(MR.strings.backup_has_no_manga))
}
val sources = backup.backupSources.map { it.sourceId to it.name }.toMap()
val sources = backup.backupSources.associate { it.sourceId to it.name }
val missingSources = sources
.filter { sourceManager.get(it.key) == null }
.map { sourceManager.getOrStub(it.key).name }
@ -40,7 +35,7 @@ class BackupFileValidator(
val trackers = backup.backupManga
.flatMap { it.tracking }
.map { it.syncId }
.map { it.syncId.toLong() }
.distinct()
val missingTrackers = trackers
.mapNotNull { trackManager.getService(it) }

View file

@ -38,7 +38,7 @@ class BackupNotifier(private val context: Context) {
context.notificationManager.notify(id, build())
}
fun showBackupProgress() {
fun showBackupProgress(): NotificationCompat.Builder {
val builder = with(progressNotificationBuilder) {
setContentTitle(context.getString(MR.strings.creating_backup))
@ -47,6 +47,8 @@ class BackupNotifier(private val context: Context) {
}
builder.show(Notifications.ID_BACKUP_PROGRESS)
return builder
}
fun showBackupError(error: String?) {

View file

@ -5,25 +5,29 @@ import android.net.Uri
import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.data.backup.create.creators.CategoriesBackupCreator
import eu.kanade.tachiyomi.data.backup.create.creators.MangaBackupCreator
import eu.kanade.tachiyomi.data.backup.create.creators.PreferenceBackupCreator
import eu.kanade.tachiyomi.data.backup.create.creators.SourcesBackupCreator
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.domain.manga.models.Manga
import java.io.FileOutputStream
import java.time.Instant
import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer
import okio.gzip
import okio.sink
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.storage.StorageManager
import yokai.domain.backup.BackupPreferences
import yokai.domain.manga.interactor.GetManga
import yokai.i18n.MR
import yokai.util.lang.getString
import java.io.FileOutputStream
class BackupCreator(
val context: Context,
@ -31,12 +35,11 @@ class BackupCreator(
private val mangaBackupCreator: MangaBackupCreator = MangaBackupCreator(),
private val preferenceBackupCreator: PreferenceBackupCreator = PreferenceBackupCreator(),
private val sourcesBackupCreator: SourcesBackupCreator = SourcesBackupCreator(),
private val getManga: GetManga = Injekt.get(),
) {
val parser = ProtoBuf
private val db: DatabaseHelper = Injekt.get()
private val preferences: PreferencesHelper = Injekt.get()
private val storageManager: StorageManager by injectLazy()
private val backupPreferences: BackupPreferences = Injekt.get()
/**
* Create backup Json file from database
@ -44,57 +47,42 @@ class BackupCreator(
* @param uri path of Uri
* @param isAutoBackup backup called from scheduled backup job
*/
@Suppress("RedundantSuspendModifier")
suspend fun createBackup(uri: Uri, options: BackupOptions, isAutoBackup: Boolean): String {
// Create root object
var backup: Backup? = null
db.inTransaction {
val databaseManga = db.getFavoriteMangas().executeAsBlocking() +
if (options.readManga) {
db.getReadNotInLibraryMangas().executeAsBlocking()
} else {
emptyList()
}
backup = Backup(
mangaBackupCreator.backupMangas(databaseManga, options),
categoriesBackupCreator.backupCategories(options),
emptyList(),
sourcesBackupCreator.backupExtensionInfo(databaseManga, options),
preferenceBackupCreator.backupAppPreferences(options),
preferenceBackupCreator.backupSourcePreferences(options),
)
}
var file: UniFile? = null
try {
file = (
if (isAutoBackup) {
// Get dir of file and create
val dir = storageManager.getAutomaticBackupsDirectory()
file = if (isAutoBackup) {
// Get dir of file and create
val dir = UniFile.fromUri(context, uri)
// Delete older backups
val numberOfBackups = preferences.numberOfBackups().get()
dir?.listFiles { _, filename -> Backup.filenameRegex.matches(filename) }
.orEmpty()
.sortedByDescending { it.name }
.drop(numberOfBackups - 1)
.forEach { it.delete() }
// Delete older backups
val numberOfBackups = backupPreferences.numberOfBackups().get()
dir?.listFiles { _, filename -> Backup.filenameRegex.matches(filename) }
.orEmpty()
.sortedByDescending { it.name }
.drop(numberOfBackups - 1)
.forEach { it.delete() }
// Create new file to place backup
dir?.createFile(Backup.getBackupFilename())
} else {
UniFile.fromUri(context, uri)
}
)
?: throw Exception("Couldn't create backup file")
// Create new file to place backup
dir?.createFile(Backup.getBackupFilename())
} else {
UniFile.fromUri(context, uri)
} ?: throw IllegalStateException("Unable to retrieve backup destination")
if (!file.isFile) {
throw IllegalStateException("Failed to get handle on file")
throw IllegalStateException("Invalid backup destination")
}
val byteArray = parser.encodeToByteArray(Backup.serializer(), backup!!)
val readNotFavorites = if (options.readManga) getManga.awaitReadNotFavorites() else emptyList()
val backupManga = backupMangas(getManga.awaitFavorites() + readNotFavorites, options)
val backup = Backup(
backupManga = backupManga,
backupCategories = backupCategories(options),
backupSources = backupSources(backupManga),
backupPreferences = backupAppPreferences(options),
backupSourcePreferences = backupSourcePreferences(options),
)
val byteArray = parser.encodeToByteArray(Backup.serializer(), backup)
if (byteArray.isEmpty()) {
throw IllegalStateException(context.getString(MR.strings.empty_backup_error))
}
@ -108,11 +96,43 @@ class BackupCreator(
// Make sure it's a valid backup file
BackupFileValidator().validate(context, fileUri)
if (isAutoBackup) {
backupPreferences.lastAutoBackupTimestamp().set(Instant.now().toEpochMilli())
}
return fileUri.toString()
} catch (e: Exception) {
Logger.e(e)
Logger.e(e) { "Backup failed: ${e.message}" }
file?.delete()
throw e
}
}
private suspend fun backupCategories(options: BackupOptions): List<BackupCategory> {
if (!options.categories) return emptyList()
return categoriesBackupCreator()
}
private suspend fun backupMangas(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
if (!options.libraryEntries) return emptyList()
return mangaBackupCreator(mangas, options)
}
private fun backupSources(mangas: List<BackupManga>): List<BackupSource> {
return sourcesBackupCreator(mangas)
}
private fun backupAppPreferences(options: BackupOptions): List<BackupPreference> {
if (!options.appPrefs) return emptyList()
return preferenceBackupCreator.createApp(includePrivatePreferences = options.includePrivate)
}
private fun backupSourcePreferences(options: BackupOptions): List<BackupSourcePreferences> {
if (!options.sourcePrefs) return emptyList()
return preferenceBackupCreator.createSource(includePrivatePreferences = options.includePrivate)
}
}

View file

@ -1,49 +1,62 @@
package eu.kanade.tachiyomi.data.backup.create
import android.content.Context
import android.content.pm.ServiceInfo
import android.net.Uri
import android.os.Build
import androidx.core.net.toUri
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.tryToSetForeground
import eu.kanade.tachiyomi.util.system.workManager
import java.util.concurrent.TimeUnit
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.backup.BackupPreferences
import yokai.domain.storage.StorageManager
import java.util.concurrent.*
class BackupCreatorJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
private val notifier = BackupNotifier(context.localeContext)
override suspend fun doWork(): Result {
val storageManager: StorageManager by injectLazy()
val notifier = BackupNotifier(context.localeContext)
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() ?: storageManager.getAutomaticBackupsDirectory()?.uri
val options = inputData.getBooleanArray(BACKUP_FLAGS_KEY)?.let { BackupOptions.fromBooleanArray(it) }
?: BackupOptions()
val isAutoBackup = inputData.getBoolean(IS_AUTO_BACKUP_KEY, true)
notifier.showBackupProgress()
if (isAutoBackup && BackupRestoreJob.isRunning(context)) return Result.retry()
val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
?: getAutomaticBackupLocation()
?: return Result.failure()
tryToSetForeground()
val options = inputData.getBooleanArray(BACKUP_FLAGS_KEY)?.let { BackupOptions.fromBooleanArray(it) }
?: BackupOptions()
return try {
val location = BackupCreator(context).createBackup(uri!!, options, isAutoBackup)
val location = BackupCreator(context).createBackup(uri, options, isAutoBackup)
if (!isAutoBackup) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri())!!)
Result.success()
} catch (e: Exception) {
Logger.e(e)
Logger.e(e) { "Unable to create backup" }
if (!isAutoBackup) notifier.showBackupError(e.message)
Result.failure()
} finally {
@ -51,30 +64,53 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
}
}
private fun getAutomaticBackupLocation(): Uri? {
val storageManager = Injekt.get<StorageManager>()
return storageManager.getAutomaticBackupsDirectory()?.uri
}
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
Notifications.ID_BACKUP_PROGRESS,
notifier.showBackupProgress().build(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
},
)
}
companion object {
fun isManualJobRunning(context: Context): Boolean {
val list = WorkManager.getInstance(context).getWorkInfosByTag(TAG_MANUAL).get()
return list.find { it.state == WorkInfo.State.RUNNING } != null
return context.workManager
.getWorkInfosByTag(TAG_MANUAL).get()
.find { it.state == WorkInfo.State.RUNNING } != null
}
fun setupTask(context: Context, prefInterval: Int? = null) {
val preferences = Injekt.get<PreferencesHelper>()
val preferences = Injekt.get<BackupPreferences>()
val interval = prefInterval ?: preferences.backupInterval().get()
val workManager = WorkManager.getInstance(context)
if (interval > 0) {
val constraints = Constraints(
requiresBatteryNotLow = true,
)
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
interval.toLong(),
TimeUnit.HOURS,
10,
TimeUnit.MINUTES,
)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.MINUTES)
.addTag(TAG_AUTO)
.setConstraints(constraints)
.setInputData(workDataOf(IS_AUTO_BACKUP_KEY to true))
.build()
workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
context.workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
} else {
workManager.cancelUniqueWork(TAG_AUTO)
context.workManager.cancelUniqueWork(TAG_AUTO)
}
}
@ -88,7 +124,7 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
.addTag(TAG_MANUAL)
.setInputData(inputData)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(TAG_MANUAL, ExistingWorkPolicy.KEEP, request)
context.workManager.enqueueUniqueWork(TAG_MANUAL, ExistingWorkPolicy.KEEP, request)
}
}
}

View file

@ -49,12 +49,6 @@ data class BackupOptions(
getter = BackupOptions::libraryEntries,
setter = { options, enabled -> options.copy(libraryEntries = enabled) },
),
Entry(
label = MR.strings.categories,
getter = BackupOptions::categories,
setter = { options, enabled -> options.copy(categories = enabled) },
enabled = { it.libraryEntries },
),
Entry(
label = MR.strings.chapters,
getter = BackupOptions::chapters,
@ -85,6 +79,11 @@ data class BackupOptions(
setter = { options, enabled -> options.copy(readManga = enabled) },
enabled = { it.libraryEntries },
),
Entry(
label = MR.strings.categories,
getter = BackupOptions::categories,
setter = { options, enabled -> options.copy(categories = enabled) },
),
Entry(
label = MR.strings.app_settings,
getter = BackupOptions::appPrefs,

View file

@ -2,23 +2,20 @@ package eu.kanade.tachiyomi.data.backup.create.creators
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.category.interactor.GetCategories
class CategoriesBackupCreator(
private val db: DatabaseHelper = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
) {
/**
* Backup the categories of library
*
* @return list of [BackupCategory] to be backed up
*/
fun backupCategories(options: BackupOptions): List<BackupCategory> {
if (!options.libraryEntries) return emptyList()
return db.getCategories()
.executeAsBlocking()
suspend operator fun invoke(): List<BackupCategory> {
return getCategories.await()
.map { BackupCategory.copyFrom(it) }
}
}

View file

@ -5,19 +5,25 @@ import eu.kanade.tachiyomi.data.backup.models.BackupChapter
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupTracking
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.domain.manga.models.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.data.DatabaseHandler
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.history.interactor.GetHistory
import yokai.domain.track.interactor.GetTrack
class MangaBackupCreator(
private val db: DatabaseHelper = Injekt.get(),
private val customMangaManager: CustomMangaManager = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val getChapter: GetChapter = Injekt.get(),
private val getHistory: GetHistory = Injekt.get(),
private val getTrack: GetTrack = Injekt.get(),
) {
fun backupMangas(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
if (!options.libraryEntries) return emptyList()
suspend operator fun invoke(mangas: List<Manga>, options: BackupOptions): List<BackupManga> {
return mangas.map {
backupManga(it, options)
}
@ -30,23 +36,32 @@ class MangaBackupCreator(
* @param options options for the backup
* @return [BackupManga] containing manga in a serializable form
*/
private fun backupManga(manga: Manga, options: BackupOptions): BackupManga {
private suspend fun backupManga(manga: Manga, options: BackupOptions): BackupManga {
// Entry for this manga
val mangaObject = BackupManga.copyFrom(manga, if (options.customInfo) customMangaManager else null)
// Check if user wants chapter information in backup
if (options.chapters) {
// Backup all the chapters
val chapters = db.getChapters(manga).executeAsBlocking()
val chapters = manga.id?.let {
handler.awaitList {
chaptersQueries.getChaptersByMangaId(
it,
0, // We want all chapters, so ignore scanlator filter
BackupChapter::mapper)
}
}.orEmpty()
if (chapters.isNotEmpty()) {
mangaObject.chapters = chapters.map { BackupChapter.copyFrom(it) }
mangaObject.chapters = chapters
}
}
// Check if user wants category information in backup
if (options.categories) {
// Backup categories for this manga
val categoriesForManga = db.getCategoriesForManga(manga).executeAsBlocking()
val categoriesForManga = manga.id?.let {
getCategories.awaitByMangaId(it)
}.orEmpty()
if (categoriesForManga.isNotEmpty()) {
mangaObject.categories = categoriesForManga.mapNotNull { it.order }
}
@ -54,7 +69,9 @@ class MangaBackupCreator(
// Check if user wants track information in backup
if (options.tracking) {
val tracks = db.getTracks(manga).executeAsBlocking()
val tracks = manga.id?.let {
getTrack.awaitAllByMangaId(it)
}.orEmpty()
if (tracks.isNotEmpty()) {
mangaObject.tracking = tracks.map { BackupTracking.copyFrom(it) }
}
@ -62,10 +79,12 @@ class MangaBackupCreator(
// Check if user wants history information in backup
if (options.history) {
val historyForManga = db.getHistoryByMangaId(manga.id!!).executeAsBlocking()
val historyForManga = manga.id?.let {
getHistory.awaitAllByMangaId(it)
}.orEmpty()
if (historyForManga.isNotEmpty()) {
val history = historyForManga.mapNotNull { history ->
val url = db.getChapter(history.chapter_id).executeAsBlocking()?.url
val url = getChapter.awaitById(history.chapter_id)?.url
url?.let { BackupHistory(url, history.last_read, history.time_read) }
}
if (history.isNotEmpty()) {

View file

@ -23,21 +23,19 @@ class PreferenceBackupCreator(
private val sourceManager: SourceManager = Injekt.get(),
private val preferenceStore: PreferenceStore = Injekt.get(),
) {
fun backupAppPreferences(options: BackupOptions): List<BackupPreference> {
if (!options.appPrefs) return emptyList()
fun createApp(includePrivatePreferences: Boolean): List<BackupPreference> {
return preferenceStore.getAll().toBackupPreferences()
.withPrivatePreferences(options.includePrivate)
.withPrivatePreferences(includePrivatePreferences)
}
fun backupSourcePreferences(options: BackupOptions): List<BackupSourcePreferences> {
if (!options.sourcePrefs) return emptyList()
fun createSource(includePrivatePreferences: Boolean): List<BackupSourcePreferences> {
return sourceManager.getOnlineSources()
.filterIsInstance<ConfigurableSource>()
.map {
BackupSourcePreferences(
it.preferenceKey(),
it.sourcePreferences().all.toBackupPreferences()
.withPrivatePreferences(options.includePrivate),
.withPrivatePreferences(includePrivatePreferences),
)
}
}

View file

@ -1,8 +1,7 @@
package eu.kanade.tachiyomi.data.backup.create.creators
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.source.SourceManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -10,9 +9,7 @@ import uy.kohesive.injekt.api.get
class SourcesBackupCreator(
private val sourceManager: SourceManager = Injekt.get(),
) {
fun backupExtensionInfo(mangas: List<Manga>, options: BackupOptions): List<BackupSource> {
if (!options.libraryEntries) return emptyList()
operator fun invoke(mangas: List<BackupManga>): List<BackupSource> {
return mangas
.asSequence()
.map { it.source }

View file

@ -1,17 +1,17 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.BuildConfig
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class Backup(
@ProtoNumber(1) val backupManga: List<BackupManga>,
@ProtoNumber(2) var backupCategories: List<BackupCategory> = emptyList(),
@ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(),
//@ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(),
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
@ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(),

View file

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@ -42,20 +41,32 @@ data class BackupChapter(
}
companion object {
fun copyFrom(chapter: Chapter): BackupChapter {
return BackupChapter(
url = chapter.url,
name = chapter.name,
chapterNumber = chapter.chapter_number,
scanlator = chapter.scanlator,
read = chapter.read,
bookmark = chapter.bookmark,
lastPageRead = chapter.last_page_read,
dateFetch = chapter.date_fetch,
dateUpload = chapter.date_upload,
sourceOrder = chapter.source_order,
pagesLeft = chapter.pages_left,
)
}
fun mapper(
id: Long,
mangaId: Long,
url: String,
name: String,
scanlator: String?,
read: Boolean,
bookmark: Boolean,
lastPageRead: Long,
pagesLeft: Long,
chapterNumber: Double,
sourceOrder: Long,
dateFetch: Long,
dateUpload: Long,
) = BackupChapter(
url = url,
name = name,
scanlator = scanlator,
read = read,
bookmark = bookmark,
lastPageRead = lastPageRead.toInt(),
pagesLeft = pagesLeft.toInt(),
chapterNumber = chapterNumber.toFloat(),
sourceOrder = sourceOrder.toInt(),
dateFetch = dateFetch,
dateUpload = dateUpload,
)
}
}

View file

@ -57,8 +57,10 @@ data class BackupManga(
@ProtoNumber(805) var customGenre: List<String>? = null,
) {
fun getMangaImpl(): MangaImpl {
return MangaImpl().apply {
url = this@BackupManga.url
return MangaImpl(
source = this.source,
url = this.url,
).apply {
title = this@BackupManga.title
artist = this@BackupManga.artist
author = this@BackupManga.author
@ -67,7 +69,6 @@ data class BackupManga(
status = this@BackupManga.status
thumbnail_url = this@BackupManga.thumbnailUrl
favorite = this@BackupManga.favorite
source = this@BackupManga.source
date_added = this@BackupManga.dateAdded
viewer_flags = (
this@BackupManga.viewer_flags

View file

@ -32,7 +32,7 @@ data class BackupTracking(
fun getTrackingImpl(): TrackImpl {
return TrackImpl().apply {
sync_id = this@BackupTracking.syncId
sync_id = this@BackupTracking.syncId.toLong()
media_id = if (this@BackupTracking.mediaIdInt != 0) {
this@BackupTracking.mediaIdInt.toLong()
} else {
@ -41,7 +41,7 @@ data class BackupTracking(
library_id = this@BackupTracking.libraryId
title = this@BackupTracking.title
last_chapter_read = this@BackupTracking.lastChapterRead
total_chapters = this@BackupTracking.totalChapters
total_chapters = this@BackupTracking.totalChapters.toLong()
score = this@BackupTracking.score
status = this@BackupTracking.status
started_reading_date = this@BackupTracking.startedReadingDate
@ -53,13 +53,13 @@ data class BackupTracking(
companion object {
fun copyFrom(track: Track): BackupTracking {
return BackupTracking(
syncId = track.sync_id,
syncId = track.sync_id.toInt(),
mediaId = track.media_id,
// forced not null so its compatible with 1.x backup system
libraryId = track.library_id!!,
libraryId = track.library_id ?: 0L,
title = track.title,
lastChapterRead = track.last_chapter_read,
totalChapters = track.total_chapters,
totalChapters = track.total_chapters.toInt(),
score = track.score,
status = track.status,
startedReadingDate = track.started_reading_date,

View file

@ -9,7 +9,6 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import co.touchlab.kermit.Logger
@ -20,6 +19,7 @@ import eu.kanade.tachiyomi.util.system.jobIsRunning
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.tryToSetForeground
import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
import yokai.i18n.MR
import yokai.util.lang.getString
@ -30,13 +30,15 @@ class BackupRestoreJob(val context: Context, workerParams: WorkerParameters) : C
private val restorer = BackupRestorer(context, notifier)
override suspend fun getForegroundInfo(): ForegroundInfo {
val notification = notifier.showRestoreProgress(progress = -1).build()
val id = Notifications.ID_RESTORE_PROGRESS
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ForegroundInfo(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
} else {
ForegroundInfo(id, notification)
}
return ForegroundInfo(
Notifications.ID_RESTORE_PROGRESS,
notifier.showRestoreProgress(progress = -1).build(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
} else {
0
}
)
}
override suspend fun doWork(): Result {
@ -72,14 +74,13 @@ class BackupRestoreJob(val context: Context, workerParams: WorkerParameters) : C
.setInputData(workDataOf(BackupConst.EXTRA_URI to uri.toString()))
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}
fun stop(context: Context) {
WorkManager.getInstance(context).cancelUniqueWork(TAG)
context.workManager.cancelUniqueWork(TAG)
}
fun isRunning(context: Context) = WorkManager.getInstance(context).jobIsRunning(TAG)
fun isRunning(context: Context) = context.workManager.jobIsRunning(TAG)
}
}

View file

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.data.backup.restore.restorers.CategoriesBackupRestorer
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaBackupRestorer
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceBackupRestorer
@ -54,9 +53,7 @@ class BackupRestorer(
restoreAmount = backup.backupManga.size + 3 // +3 for categories, app prefs, source prefs
// Store source mapping for error messages
val backupMaps = backup.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources
sourceMapping = backupMaps.associate { it.sourceId to it.name }
sourceMapping = backup.backupSources.associate { it.sourceId to it.name }
coroutineScope {
// Restore categories

View file

@ -1,25 +1,23 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.data.DatabaseHandler
import yokai.domain.category.interactor.GetCategories
class CategoriesBackupRestorer(
private val db: DatabaseHelper = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get(),
) {
suspend fun restoreCategories(backupCategories: List<BackupCategory>, onComplete: () -> Unit) {
// Get categories from file and from db
// Do it outside of transaction because StorIO might hang because we're using SQLDelight
val dbCategories = getCategories.await()
db.inTransaction {
handler.await(true) {
// Iterate over them
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
// Used to know if the category is already in the db
var found = false
for (dbCategory in dbCategories) {
for (dbCategory in getCategories.await()) {
// If the category is already in the db, assign the id to the file's category
// and do nothing
if (category.name == dbCategory.name) {
@ -33,8 +31,13 @@ class CategoriesBackupRestorer(
if (!found) {
// Let the db assign the id
category.id = null
val result = db.insertCategory(category).executeAsBlocking()
category.id = result.insertedId()?.toInt()
categoriesQueries.insert(
name = category.name,
mangaOrder = category.mangaOrderToString(),
sort = category.order.toLong(),
flags = category.flags.toLong(),
)
category.id = categoriesQueries.selectLastInsertedRowId().executeAsOneOrNull()?.toInt()
}
}
}

View file

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.MangaCategory
@ -15,24 +14,39 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
import eu.kanade.tachiyomi.util.manga.MangaUtil
import eu.kanade.tachiyomi.util.system.launchNow
import kotlin.math.max
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.data.DatabaseHandler
import yokai.domain.category.interactor.GetCategories
import yokai.domain.category.interactor.SetMangaCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.InsertChapter
import yokai.domain.chapter.interactor.UpdateChapter
import yokai.domain.history.interactor.GetHistory
import yokai.domain.history.interactor.UpsertHistory
import yokai.domain.library.custom.model.CustomMangaInfo
import yokai.domain.manga.interactor.GetManga
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
import kotlin.math.max
import yokai.domain.track.interactor.GetTrack
import yokai.domain.track.interactor.InsertTrack
class MangaBackupRestorer(
private val db: DatabaseHelper = Injekt.get(),
private val customMangaManager: CustomMangaManager = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val getChapter: GetChapter = Injekt.get(),
private val insertChapter: InsertChapter = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val getManga: GetManga = Injekt.get(),
private val insertManga: InsertManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val getHistory: GetHistory = Injekt.get(),
private val upsertHistory: UpsertHistory = Injekt.get(),
private val getTrack: GetTrack = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(),
) {
suspend fun restoreManga(
backupManga: BackupManga,
@ -136,8 +150,8 @@ class MangaBackupRestorer(
}
val newChapters = chapters.groupBy { it.id != null }
newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() }
newChapters[false]?.let { db.insertChapters(it).executeAsBlocking() }
newChapters[true]?.let { updateChapter.awaitAll(it.map(Chapter::toProgressUpdate)) }
newChapters[false]?.let { insertChapter.awaitBulk(it) }
}
private suspend fun restoreExtras(
@ -184,8 +198,7 @@ class MangaBackupRestorer(
// Update database
if (mangaCategoriesToUpdate.isNotEmpty()) {
db.deleteOldMangasCategories(listOf(manga)).executeAsBlocking()
db.insertMangasCategories(mangaCategoriesToUpdate).executeAsBlocking()
setMangaCategories.awaitAll(listOf(manga.id!!), mangaCategoriesToUpdate)
}
}
@ -198,7 +211,7 @@ class MangaBackupRestorer(
// List containing history to be updated
val historyToBeUpdated = ArrayList<History>(history.size)
for ((url, lastRead, readDuration) in history) {
val dbHistory = db.getHistoryByChapterUrl(url).executeAsBlocking()
val dbHistory = handler.awaitOneOrNull { historyQueries.getByChapterUrl(url, History::mapper) }
// Check if history already in database and update
if (dbHistory != null) {
dbHistory.apply {
@ -208,7 +221,7 @@ class MangaBackupRestorer(
historyToBeUpdated.add(dbHistory)
} else {
// If not in database create
db.getChapter(url).executeAsBlocking()?.let {
getChapter.awaitByUrl(url, false)?.let {
val historyToAdd = History.create(it).apply {
last_read = lastRead
time_read = readDuration
@ -217,7 +230,7 @@ class MangaBackupRestorer(
}
}
}
db.upsertHistoryLastRead(historyToBeUpdated).executeAsBlocking()
upsertHistory.awaitBulk(historyToBeUpdated)
}
/**
@ -231,7 +244,7 @@ class MangaBackupRestorer(
tracks.map { it.manga_id = manga.id!! }
// Get tracks from database
val dbTracks = db.getTracks(manga).executeAsBlocking()
val dbTracks = getTrack.awaitAllByMangaId(manga.id!!)
val trackToUpdate = mutableListOf<Track>()
tracks.forEach { track ->
@ -259,7 +272,7 @@ class MangaBackupRestorer(
}
// Update database
if (trackToUpdate.isNotEmpty()) {
db.insertTracks(trackToUpdate).executeAsBlocking()
insertTrack.awaitBulk(trackToUpdate)
}
}

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers
import android.content.Context
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
import eu.kanade.tachiyomi.data.backup.create.BackupCreatorJob
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
@ -13,11 +14,14 @@ import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.source.sourcePreferences
import eu.kanade.tachiyomi.ui.library.LibrarySort
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.base.BasePreferences
import yokai.domain.ui.settings.ReaderPreferences
class PreferenceBackupRestorer(
private val context: Context,
@ -56,6 +60,26 @@ class PreferenceBackupRestorer(
}
// end j2k fork differences
// << Yokai-J2K compat
if (key == "extension_installer" && value is IntPreferenceValue) {
val enum = BasePreferences.ExtensionInstaller.migrate(value.value)
preferenceStore.getEnum(key, enum).set(enum)
return@forEach
}
if (key == PreferenceKeys.pagerCutoutBehavior && value is IntPreferenceValue) {
val enum = ReaderPreferences.CutoutBehaviour.migrate(value.value)
preferenceStore.getEnum(key, enum).set(enum)
return@forEach
}
if (key == "landscape_cutout_behavior" && value is IntPreferenceValue) {
val enum = ReaderPreferences.LandscapeCutoutBehaviour.migrate(value.value)
preferenceStore.getEnum(key, enum).set(enum)
return@forEach
}
// >> Yokai-J2K compat
when (value) {
is IntPreferenceValue -> {
if (prefs[key] is Int?) {

View file

@ -1,29 +1,28 @@
package eu.kanade.tachiyomi.data.cache
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.text.format.Formatter
import co.touchlab.kermit.Logger
import coil3.imageLoader
import coil3.memory.MemoryCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.withUIContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.i18n.MR
import yokai.util.lang.getString
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.util.concurrent.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy
import yokai.domain.manga.interactor.GetManga
import yokai.i18n.MR
import yokai.util.lang.getString
/**
* Class used to create cover cache.
@ -41,6 +40,8 @@ class CoverCache(val context: Context) {
private const val ONLINE_COVERS_DIR = "online_covers"
}
private val getManga: GetManga by injectLazy()
/** Cache directory used for cache management.*/
private val cacheDir = getCacheDir(COVERS_DIR)
@ -69,10 +70,12 @@ class CoverCache(val context: Context) {
}
suspend fun deleteOldCovers() {
val db = Injekt.get<DatabaseHelper>()
var deletedSize = 0L
val urls = db.getFavoriteMangas().executeOnIO().mapNotNull {
it.thumbnail_url?.let { url -> return@mapNotNull DiskUtil.hashKeyForDisk(url) }
val urls = getManga.awaitFavorites().mapNotNull {
it.thumbnail_url?.let { url ->
it.updateCoverLastModified()
return@mapNotNull DiskUtil.hashKeyForDisk(url)
}
null
}
val files = cacheDir.listFiles()?.iterator() ?: return
@ -143,7 +146,7 @@ class CoverCache(val context: Context) {
}
}
} catch (e: Exception) {
Logger.e(e)
Logger.e(e) { "Unable to delete unused cover cache" }
}
lastClean = System.currentTimeMillis()
}
@ -156,8 +159,10 @@ class CoverCache(val context: Context) {
* @param manga the manga.
* @return cover image.
*/
fun getCustomCoverFile(manga: Manga): File {
return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(manga.id.toString()))
fun getCustomCoverFile(manga: Manga): File = getCustomCoverFile(manga.id)
fun getCustomCoverFile(mangaId: Long?): File {
return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(mangaId.toString()))
}
/**
@ -169,9 +174,38 @@ class CoverCache(val context: Context) {
*/
@Throws(IOException::class)
fun setCustomCoverToCache(manga: Manga, inputStream: InputStream) {
val maxTextureSize = 4096f
var bitmap = BitmapFactory.decodeStream(inputStream)
if (maxOf(bitmap.width, bitmap.height) > maxTextureSize) {
val widthRatio = bitmap.width / maxTextureSize
val heightRatio = bitmap.height / maxTextureSize
val targetWidth: Float
val targetHeight: Float
if (widthRatio >= heightRatio) {
targetWidth = maxTextureSize
targetHeight = (targetWidth / bitmap.width) * bitmap.height
} else {
targetHeight = maxTextureSize
targetWidth = (targetHeight / bitmap.height) * bitmap.width
}
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, targetWidth.toInt(), targetHeight.toInt(), true)
bitmap.recycle()
bitmap = scaledBitmap
}
getCustomCoverFile(manga).outputStream().use {
inputStream.copyTo(it)
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
@Suppress("DEPRECATION")
bitmap.compress(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
Bitmap.CompressFormat.WEBP_LOSSLESS
else
Bitmap.CompressFormat.WEBP,
100,
it
)
bitmap.recycle()
}
}
@ -185,32 +219,21 @@ class CoverCache(val context: Context) {
val result = getCustomCoverFile(manga).let {
it.exists() && it.delete()
}
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
return result
}
/**
* Returns the cover from cache.
*
* @param thumbnailUrl the thumbnail url.
* @param mangaThumbnailUrl the thumbnail url.
* @return cover image.
*/
fun getCoverFile(manga: Manga): File {
val hashKey = DiskUtil.hashKeyForDisk((manga.thumbnail_url.orEmpty()))
return if (manga.favorite) {
File(cacheDir, hashKey)
} else {
File(onlineCoverDirectory, hashKey)
fun getCoverFile(mangaThumbnailUrl: String?, isOnline: Boolean = false): File? {
return mangaThumbnailUrl?.let {
File(if (!isOnline) cacheDir else onlineCoverDirectory, DiskUtil.hashKeyForDisk(it))
}
}
fun deleteFromCache(name: String?) {
if (name.isNullOrEmpty()) return
val file = getCoverFile(MangaImpl().apply { thumbnail_url = name })
context.imageLoader.memoryCache?.remove(MemoryCache.Key(file.name))
if (file.exists()) file.delete()
}
/**
* Delete the cover file from the disk cache and optional from memory cache
*
@ -225,12 +248,10 @@ class CoverCache(val context: Context) {
if (manga.thumbnail_url.isNullOrEmpty()) return
// Remove file
val file = getCoverFile(manga)
if (deleteCustom) deleteCustomCover(manga)
if (file.exists()) {
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
file.delete()
getCoverFile(manga.thumbnail_url, !manga.favorite)?.let {
if (it.exists()) it.delete()
}
if (deleteCustom) deleteCustomCover(manga)
}
private fun getCacheDir(dir: String): File {

View file

@ -1,26 +0,0 @@
package eu.kanade.tachiyomi.data.coil
import android.content.Context
import coil3.disk.DiskCache
import coil3.disk.directory
/**
* Direct copy of Coil's internal SingletonDiskCache so that [MangaCoverFetcher] can access it.
*/
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 }
}
}
}

View file

@ -4,37 +4,33 @@ import android.graphics.BitmapFactory
import android.widget.ImageView
import androidx.palette.graphics.Palette
import coil3.Image
import coil3.ImageLoader
import coil3.imageLoader
import coil3.memory.MemoryCache
import coil3.request.Disposable
import coil3.request.ImageRequest
import coil3.target.ImageViewTarget
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.system.launchIO
import uy.kohesive.injekt.injectLazy
class LibraryMangaImageTarget(
override val view: ImageView,
val manga: Manga,
private val libraryManga: Manga,
) : ImageViewTarget(view) {
private val coverCache: CoverCache by injectLazy()
override fun onError(error: Image?) {
super.onError(error)
if (manga.favorite) {
if (libraryManga.favorite) {
launchIO {
val file = coverCache.getCoverFile(manga)
val file = coverCache.getCoverFile(libraryManga.thumbnail_url, false)
// if the file exists and the there was still an error then the file is corrupted
if (file.exists()) {
if (file != null && file.exists()) {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.path, options)
if (options.outWidth == -1 || options.outHeight == -1) {
libraryManga.updateCoverLastModified()
file.delete()
view.context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
}
}
}
@ -42,21 +38,6 @@ class LibraryMangaImageTarget(
}
}
@JvmSynthetic
inline fun ImageView.loadManga(
manga: Manga,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {},
): Disposable {
val request = ImageRequest.Builder(context)
.data(manga)
.target(LibraryMangaImageTarget(this, manga))
.apply(builder)
.memoryCacheKey(manga.key())
.build()
return imageLoader.enqueue(request)
}
fun Palette.getBestColor(defaultColor: Int) = getBestColor() ?: defaultColor
fun Palette.getBestColor(): Int? {

View file

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.coil
import android.webkit.MimeTypeMap
import androidx.core.net.toUri
import co.touchlab.kermit.Logger
import coil3.Extras
import coil3.ImageLoader
import coil3.decode.DataSource
import coil3.decode.ImageSource
@ -11,7 +10,6 @@ import coil3.disk.DiskCache
import coil3.fetch.FetchResult
import coil3.fetch.Fetcher
import coil3.fetch.SourceFetchResult
import coil3.getOrDefault
import coil3.request.Options
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.cache.CoverCache
@ -20,6 +18,9 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
import java.io.File
import java.net.HttpURLConnection
import java.util.Date
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -29,67 +30,92 @@ import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import okio.FileSystem
import okio.IOException
import okio.Path.Companion.toOkioPath
import okio.Source
import okio.buffer
import okio.sink
import okio.source
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.net.HttpURLConnection
import java.util.*
import yokai.domain.manga.models.MangaCover
class MangaCoverFetcher(
private val manga: Manga,
private val mangaId: Long?,
private val url: String?,
private val isInLibrary: Boolean,
private val sourceLazy: Lazy<HttpSource?>,
private val options: Options,
private val coverCache: CoverCache,
private val callFactoryLazy: Lazy<Call.Factory>,
private val diskCacheLazy: Lazy<DiskCache>,
private val diskCacheKeyLazy: Lazy<String>,
private val coverFileLazy: Lazy<File?>,
private val customCoverFileLazy: Lazy<File>,
private val imageLoader: ImageLoader,
) : Fetcher {
// For non-custom cover
private val diskCacheKey: String? by lazy { MangaCoverKeyer().key(manga, options) }
private lateinit var url: String
private val diskCacheKey: String
get() = diskCacheKeyLazy.value
val fileScope = CoroutineScope(Job() + Dispatchers.IO)
private val fileScope = CoroutineScope(Job() + Dispatchers.IO)
override suspend fun fetch(): FetchResult {
// diskCacheKey is thumbnail_url
url = manga.thumbnail_url ?: error("No cover specified")
if (options.useCustomCover) {
val customCoverFile = customCoverFileLazy.value
if (customCoverFile.exists()) {
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(customCoverFile))
return fileLoader(customCoverFile)
}
}
if (url == null) error("No cover specified")
return when (getResourceType(url)) {
Type.URL -> httpLoader()
Type.File -> {
setRatioAndColorsInScope(manga, File(url.substringAfter("file://")))
fileLoader(File(url.substringAfter("file://")))
val file = File(url.substringAfter("file://"))
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(file))
fileLoader(file)
}
Type.URI -> {
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromUri(options.context, url.toUri()))
fileUriLoader(url)
}
Type.URI -> fileUriLoader(url)
null -> error("Invalid image")
}
}
private fun fileLoader(file: File): FetchResult {
return SourceFetchResult(
source = ImageSource(
file = file.toOkioPath(),
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey,
),
mimeType = "image/*",
dataSource = DataSource.DISK,
)
}
private fun fileUriLoader(uri: String): FetchResult {
val source = UniFile.fromUri(options.context, uri.toUri())!!
.openInputStream()
.source()
.buffer()
return SourceFetchResult(
source = ImageSource(source = source, fileSystem = FileSystem.SYSTEM),
mimeType = "image/*",
dataSource = DataSource.DISK,
)
}
private suspend fun httpLoader(): FetchResult {
val diskRead = options.diskCachePolicy.readEnabled
val networkRead = options.networkCachePolicy.readEnabled
val onlyCache = !networkRead && diskRead
val shouldFetchRemotely = networkRead && !diskRead && !onlyCache
val useCustomCover = options.extras.getOrDefault(USE_CUSTOM_COVER_KEY)
// Use custom cover if exists
if (!shouldFetchRemotely) {
val customCoverFile by lazy { coverCache.getCustomCoverFile(manga) }
if (useCustomCover && customCoverFile.exists()) {
setRatioAndColorsInScope(manga, customCoverFile)
return fileLoader(customCoverFile)
}
}
val coverFile = coverCache.getCoverFile(manga)
if (!shouldFetchRemotely && coverFile.exists() && options.diskCachePolicy.readEnabled) {
if (!manga.favorite) {
val coverFile = coverFileLazy.value
if (coverFile?.exists() == true && options.diskCachePolicy.readEnabled) {
if (!isInLibrary) {
coverFile.setLastModified(Date().time)
}
setRatioAndColorsInScope(manga, coverFile)
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(coverFile))
return fileLoader(coverFile)
}
var snapshot = readFromDiskCache()
try {
// Fetch from disk cache
@ -97,12 +123,12 @@ class MangaCoverFetcher(
val snapshotCoverCache = moveSnapshotToCoverCache(snapshot, coverFile)
if (snapshotCoverCache != null) {
// Read from cover cache after added to library
setRatioAndColorsInScope(manga, snapshotCoverCache)
setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(snapshotCoverCache))
return fileLoader(snapshotCoverCache)
}
// Read from snapshot
setRatioAndColorsInScope(manga)
setRatioAndColorsInScope(mangaId, url, isInLibrary)
return SourceFetchResult(
source = snapshot.toImageSource(),
mimeType = "image/*",
@ -116,13 +142,13 @@ class MangaCoverFetcher(
try {
// Read from cover cache after library manga cover updated
val responseCoverCache = writeResponseToCoverCache(response, coverFile)
setRatioAndColorsInScope(manga)
setRatioAndColorsInScope(mangaId, url, isInLibrary)
if (responseCoverCache != null) {
return fileLoader(responseCoverCache)
}
// Read from disk cache
snapshot = writeToDiskCache(snapshot, response)
snapshot = writeToDiskCache(response)
if (snapshot != null) {
return SourceFetchResult(
source = snapshot.toImageSource(),
@ -152,14 +178,14 @@ class MangaCoverFetcher(
val response = client.newCall(newRequest()).await()
if (!response.isSuccessful && response.code != HttpURLConnection.HTTP_NOT_MODIFIED) {
response.close()
throw Exception(response.message) // FIXME: Should probably use something else other than generic Exception
throw IOException(response.message)
}
return response
}
private fun newRequest(): Request {
val request = Request.Builder().apply {
url(url)
url(url!!)
val sourceHeaders = sourceLazy.value?.headers
if (sourceHeaders != null)
@ -170,16 +196,17 @@ class MangaCoverFetcher(
val networkRead = options.networkCachePolicy.readEnabled
val onlyCache = !networkRead && diskRead
val forceNetwork = networkRead && !diskRead
val none = !networkRead && !diskRead
when {
!networkRead && diskRead -> {
onlyCache -> {
request.cacheControl(CacheControl.FORCE_CACHE)
}
networkRead && !diskRead -> if (options.diskCachePolicy.writeEnabled) {
forceNetwork -> if (options.diskCachePolicy.writeEnabled) {
request.cacheControl(CacheControl.FORCE_NETWORK)
} else {
request.cacheControl(CACHE_CONTROL_FORCE_NETWORK_NO_CACHE)
}
!networkRead && !diskRead -> {
none -> {
// This causes the request to fail with a 504 Unsatisfiable Request.
request.cacheControl(CACHE_CONTROL_NO_NETWORK_NO_CACHE)
}
@ -191,11 +218,11 @@ class MangaCoverFetcher(
private fun moveSnapshotToCoverCache(snapshot: DiskCache.Snapshot, cacheFile: File?): File? {
if (cacheFile == null) return null
return try {
diskCacheLazy.value.run {
imageLoader.diskCache?.run {
fileSystem.source(snapshot.data).use { input ->
writeSourceToCoverCache(input, cacheFile)
}
remove(diskCacheKey!!)
remove(diskCacheKey)
}
cacheFile.takeIf { it.exists() }
} catch (e: Exception) {
@ -232,27 +259,19 @@ class MangaCoverFetcher(
private fun readFromDiskCache(): DiskCache.Snapshot? {
return if (options.diskCachePolicy.readEnabled) {
diskCacheLazy.value.openSnapshot(diskCacheKey!!)
imageLoader.diskCache?.openSnapshot(diskCacheKey)
} else {
null
}
}
private fun writeToDiskCache(
snapshot: DiskCache.Snapshot?,
response: Response,
): DiskCache.Snapshot? {
if (!options.diskCachePolicy.writeEnabled) {
snapshot?.close()
return null
}
val editor = if (snapshot != null) {
snapshot.closeAndOpenEditor()
} else {
diskCacheLazy.value.openEditor(diskCacheKey!!)
} ?: return null
val diskCache = imageLoader.diskCache
val editor = diskCache?.openEditor(diskCacheKey) ?: return null
try {
diskCacheLazy.value.fileSystem.write(editor.data) {
diskCache.fileSystem.write(editor.data) {
response.body.source().readAll(this)
}
return editor.commitAndOpenSnapshot()
@ -274,9 +293,9 @@ class MangaCoverFetcher(
)
}
private fun setRatioAndColorsInScope(manga: Manga, ogFile: File? = null, force: Boolean = false) {
private fun setRatioAndColorsInScope(mangaId: Long?, mangaThumbnailUrl: String?, isInLibrary: Boolean, ogFile: UniFile? = null, force: Boolean = false) {
fileScope.launch {
MangaCoverMetadata.setRatioAndColors(manga, ogFile, force)
MangaCoverMetadata.setRatioAndColors(mangaId, mangaThumbnailUrl, isInLibrary, ogFile, force)
}
}
@ -295,30 +314,6 @@ class MangaCoverFetcher(
return getMimeTypeFromExtension(extension)
}
private fun fileLoader(file: File): FetchResult {
return SourceFetchResult(
source = ImageSource(
file = file.toOkioPath(),
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey,
),
mimeType = "image/*",
dataSource = DataSource.DISK,
)
}
private fun fileUriLoader(uri: String): FetchResult {
val source = UniFile.fromUri(options.context, uri.toUri())!!
.openInputStream()
.source()
.buffer()
return SourceFetchResult(
source = ImageSource(source = source, fileSystem = FileSystem.SYSTEM),
mimeType = "image/*",
dataSource = DataSource.DISK,
)
}
private fun getResourceType(cover: String?): Type? {
return when {
cover.isNullOrEmpty() -> null
@ -329,17 +324,49 @@ class MangaCoverFetcher(
}
}
class Factory(
class MangaFactory(
private val callFactoryLazy: Lazy<Call.Factory>,
private val diskCacheLazy: Lazy<DiskCache>,
) : Fetcher.Factory<Manga> {
private val coverCache: CoverCache by injectLazy()
private val sourceManager: SourceManager by injectLazy()
override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher {
val source = lazy { sourceManager.get(data.source) as? HttpSource }
return MangaCoverFetcher(data, source, options, coverCache, callFactoryLazy, diskCacheLazy)
return MangaCoverFetcher(
mangaId = data.id,
url = data.thumbnail_url,
isInLibrary = data.favorite,
sourceLazy = lazy { sourceManager.get(data.source) as? HttpSource },
options = options,
callFactoryLazy = callFactoryLazy,
diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! },
coverFileLazy = lazy { coverCache.getCoverFile(data.thumbnail_url, !data.favorite) },
customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data) },
imageLoader = imageLoader,
)
}
}
class MangaCoverFactory(
private val callFactoryLazy: Lazy<Call.Factory>,
) : Fetcher.Factory<MangaCover> {
private val coverCache: CoverCache by injectLazy()
private val sourceManager: SourceManager by injectLazy()
override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher {
return MangaCoverFetcher(
mangaId = data.mangaId,
url = data.url,
isInLibrary = data.inLibrary,
sourceLazy = lazy { sourceManager.get(data.sourceId) as? HttpSource },
options = options,
callFactoryLazy = callFactoryLazy,
diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! },
coverFileLazy = lazy { coverCache.getCoverFile(data.url, !data.inLibrary) },
customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.mangaId) },
imageLoader = imageLoader,
)
}
}
@ -348,8 +375,6 @@ class MangaCoverFetcher(
}
companion object {
val USE_CUSTOM_COVER_KEY = Extras.Key(true)
private val CACHE_CONTROL_FORCE_NETWORK_NO_CACHE = CacheControl.Builder().noCache().noStore().build()
private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build()
}

View file

@ -2,16 +2,34 @@ package eu.kanade.tachiyomi.data.coil
import coil3.key.Keyer
import coil3.request.Options
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.models.hasCustomCover
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.storage.DiskUtil
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.manga.models.MangaCover
class MangaCoverKeyer : Keyer<Manga> {
override fun key(data: Manga, options: Options): String? {
if (data.thumbnail_url.isNullOrBlank()) return null
return if (!data.favorite) {
data.thumbnail_url!!
} else {
DiskUtil.hashKeyForDisk(data.thumbnail_url!!)
class MangaKeyer : Keyer<Manga> {
override fun key(data: Manga, options: Options): String {
val key = when {
data.hasCustomCover() -> data.id
data.favorite -> data.thumbnail_url?.let { DiskUtil.hashKeyForDisk(it) }
else -> data.thumbnail_url
}
return "${key};${data.cover_last_modified}"
}
}
class MangaCoverKeyer(private val coverCache: CoverCache = Injekt.get()) : Keyer<MangaCover> {
override fun key(data: MangaCover, options: Options): String {
val key = when {
coverCache.getCustomCoverFile(data.mangaId).exists() -> data.mangaId
data.inLibrary -> DiskUtil.hashKeyForDisk(data.url)
else -> data.url
}
return "${key};${data.lastModified}"
}
}

View file

@ -50,7 +50,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
if (
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
options.bitmapConfig == Bitmap.Config.HARDWARE &&
maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize
!ImageUtil.isHardwareThresholdExceeded(bitmap)
) {
val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false)
if (hwBitmap != null) {
@ -59,29 +59,6 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
}
}
/*
val maxTextureSize = 4096f
if (maxOf(bitmap.width, bitmap.height) > maxTextureSize) {
val widthRatio = bitmap.width / maxTextureSize
val heightRatio = bitmap.height / maxTextureSize
val targetWidth: Float
val targetHeight: Float
if (widthRatio >= heightRatio) {
targetWidth = maxTextureSize
targetHeight = (targetWidth / bitmap.width) * bitmap.height
} else {
targetHeight = maxTextureSize
targetWidth = (targetHeight / bitmap.height) * bitmap.width
}
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, targetWidth.toInt(), targetHeight.toInt(), true)
bitmap.recycle()
bitmap = scaledBitmap
}
*/
return DecodeResult(
image = bitmap.asImage(),
isSampled = sampleSize > 1,
@ -100,8 +77,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
ImageUtil.findImageType(it)
}
return when (type) {
ImageUtil.ImageType.AVIF, ImageUtil.ImageType.JXL -> true
ImageUtil.ImageType.HEIF -> Build.VERSION.SDK_INT < Build.VERSION_CODES.O
ImageUtil.ImageType.AVIF, ImageUtil.ImageType.JXL, ImageUtil.ImageType.HEIF -> true
else -> false
}
}

View file

@ -42,3 +42,12 @@ val Options.customDecoder: Boolean
get() = getExtra(customDecoderKey)
private val customDecoderKey = Extras.Key(default = false)
val Options.useCustomCover: Boolean
get() = getExtra(useCustomCoverKey)
fun ImageRequest.Builder.useCustomCover(enable: Boolean) = apply {
extras[useCustomCoverKey] = enable
}
private val useCustomCoverKey = Extras.Key(default = true)

View file

@ -1,59 +0,0 @@
package eu.kanade.tachiyomi.data.database
import android.content.Context
import androidx.sqlite.db.SupportSQLiteOpenHelper
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite
import eu.kanade.tachiyomi.data.database.mappers.CategoryTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.ChapterTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.HistoryTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.MangaCategoryTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.MangaTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.SearchMetadataTypeMapping
import eu.kanade.tachiyomi.data.database.mappers.TrackTypeMapping
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.SearchMetadata
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.queries.CategoryQueries
import eu.kanade.tachiyomi.data.database.queries.ChapterQueries
import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
import eu.kanade.tachiyomi.data.database.queries.MangaQueries
import eu.kanade.tachiyomi.data.database.queries.SearchMetadataQueries
import eu.kanade.tachiyomi.data.database.queries.TrackQueries
import eu.kanade.tachiyomi.domain.manga.models.Manga
/**
* This class provides operations to manage the database through its interfaces.
*/
open class DatabaseHelper(
context: Context,
openHelper: SupportSQLiteOpenHelper,
) :
MangaQueries,
ChapterQueries,
TrackQueries,
CategoryQueries,
MangaCategoryQueries,
HistoryQueries,
SearchMetadataQueries {
override val db = DefaultStorIOSQLite.builder()
.sqliteOpenHelper(openHelper)
.addTypeMapping(Manga::class.java, MangaTypeMapping())
.addTypeMapping(Chapter::class.java, ChapterTypeMapping())
.addTypeMapping(Track::class.java, TrackTypeMapping())
.addTypeMapping(Category::class.java, CategoryTypeMapping())
.addTypeMapping(MangaCategory::class.java, MangaCategoryTypeMapping())
.addTypeMapping(SearchMetadata::class.java, SearchMetadataTypeMapping())
.addTypeMapping(History::class.java, HistoryTypeMapping())
.build()
inline fun inTransaction(block: () -> Unit) = db.inTransaction(block)
inline fun <T> inTransactionReturn(block: () -> T): T = db.inTransactionReturn(block)
fun lowLevel() = db.lowLevel()
}

View file

@ -1,24 +0,0 @@
package eu.kanade.tachiyomi.data.database
import com.pushtorefresh.storio.sqlite.StorIOSQLite
inline fun StorIOSQLite.inTransaction(block: () -> Unit) {
lowLevel().beginTransaction()
try {
block()
lowLevel().setTransactionSuccessful()
} finally {
lowLevel().endTransaction()
}
}
inline fun <T> StorIOSQLite.inTransactionReturn(block: () -> T): T {
lowLevel().beginTransaction()
try {
val result = block()
lowLevel().setTransactionSuccessful()
return result
} finally {
lowLevel().endTransaction()
}
}

View file

@ -1,45 +0,0 @@
package eu.kanade.tachiyomi.data.database
import androidx.sqlite.db.SupportSQLiteDatabase
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import co.touchlab.kermit.Logger
import yokai.data.Database
class DbOpenCallback : AndroidSqliteDriver.Callback(Database.Schema) {
companion object {
/**
* Name of the database file.
*/
const val DATABASE_NAME = "tachiyomi.db"
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
setPragma(db, "foreign_keys = ON")
setPragma(db, "journal_mode = WAL")
setPragma(db, "synchronous = NORMAL")
}
private fun setPragma(db: SupportSQLiteDatabase, pragma: String) {
val cursor = db.query("PRAGMA $pragma")
cursor.moveToFirst()
cursor.close()
}
override fun onCreate(db: SupportSQLiteDatabase) {
Logger.d { "Creating new database..." }
super.onCreate(db)
}
override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (oldVersion < newVersion) {
Logger.d { "Upgrading database from $oldVersion to $newVersion" }
super.onUpgrade(db, oldVersion, newVersion)
}
}
override fun onConfigure(db: SupportSQLiteDatabase) {
db.setForeignKeyConstraintsEnabled(true)
}
}

View file

@ -1,8 +0,0 @@
package eu.kanade.tachiyomi.data.database
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite
interface DbProvider {
val db: DefaultStorIOSQLite
}

View file

@ -1,75 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.CategoryImpl
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_FLAGS
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_MANGA_ORDER
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_NAME
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_ORDER
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.TABLE
class CategoryTypeMapping : SQLiteTypeMapping<Category>(
CategoryPutResolver(),
CategoryGetResolver(),
CategoryDeleteResolver(),
)
class CategoryPutResolver : DefaultPutResolver<Category>() {
override fun mapToInsertQuery(obj: Category) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: Category) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: Category) = ContentValues(4).apply {
put(COL_ID, obj.id)
put(COL_NAME, obj.name)
put(COL_ORDER, obj.order)
put(COL_FLAGS, obj.flags)
if (obj.mangaSort != null) {
put(COL_MANGA_ORDER, obj.mangaSort.toString())
} else {
val orderString = obj.mangaOrder.joinToString("/")
put(COL_MANGA_ORDER, orderString)
}
}
}
class CategoryGetResolver : DefaultGetResolver<Category>() {
override fun mapFromCursor(cursor: Cursor): Category = CategoryImpl().also {
it.id = cursor.getInt(cursor.getColumnIndex(COL_ID))
it.name = cursor.getString(cursor.getColumnIndex(COL_NAME))
it.order = cursor.getInt(cursor.getColumnIndex(COL_ORDER))
it.flags = cursor.getInt(cursor.getColumnIndex(COL_FLAGS))
val orderString = cursor.getString(cursor.getColumnIndex(COL_MANGA_ORDER))
val (sort, order) = Category.mangaOrderFromString(orderString)
if (sort != null) it.mangaSort = sort
it.mangaOrder = order
}
}
class CategoryDeleteResolver : DefaultDeleteResolver<Category>() {
override fun mapToDeleteQuery(obj: Category) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -1,90 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_BOOKMARK
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_CHAPTER_NUMBER
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_DATE_FETCH
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_DATE_UPLOAD
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_LAST_PAGE_READ
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_MANGA_ID
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_NAME
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_PAGES_LEFT
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_READ
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_SCANLATOR
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_SOURCE_ORDER
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.COL_URL
import eu.kanade.tachiyomi.data.database.tables.ChapterTable.TABLE
class ChapterTypeMapping : SQLiteTypeMapping<Chapter>(
ChapterPutResolver(),
ChapterGetResolver(),
ChapterDeleteResolver(),
)
class ChapterPutResolver : DefaultPutResolver<Chapter>() {
override fun mapToInsertQuery(obj: Chapter) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: Chapter) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: Chapter) = ContentValues(11).apply {
put(COL_ID, obj.id)
put(COL_MANGA_ID, obj.manga_id)
put(COL_URL, obj.url)
put(COL_NAME, obj.name)
put(COL_READ, obj.read)
put(COL_SCANLATOR, obj.scanlator)
put(COL_BOOKMARK, obj.bookmark)
put(COL_DATE_FETCH, obj.date_fetch)
put(COL_DATE_UPLOAD, obj.date_upload)
put(COL_LAST_PAGE_READ, obj.last_page_read)
put(COL_PAGES_LEFT, obj.pages_left)
put(COL_CHAPTER_NUMBER, obj.chapter_number)
put(COL_SOURCE_ORDER, obj.source_order)
}
}
class ChapterGetResolver : DefaultGetResolver<Chapter>() {
override fun mapFromCursor(cursor: Cursor): Chapter = ChapterImpl().apply {
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
url = cursor.getString(cursor.getColumnIndex(COL_URL))
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
scanlator = cursor.getString(cursor.getColumnIndex(COL_SCANLATOR))
read = cursor.getInt(cursor.getColumnIndex(COL_READ)) == 1
bookmark = cursor.getInt(cursor.getColumnIndex(COL_BOOKMARK)) == 1
date_fetch = cursor.getLong(cursor.getColumnIndex(COL_DATE_FETCH))
date_upload = cursor.getLong(cursor.getColumnIndex(COL_DATE_UPLOAD))
last_page_read = cursor.getInt(cursor.getColumnIndex(COL_LAST_PAGE_READ))
pages_left = cursor.getInt(cursor.getColumnIndex(COL_PAGES_LEFT))
chapter_number = cursor.getFloat(cursor.getColumnIndex(COL_CHAPTER_NUMBER))
source_order = cursor.getInt(cursor.getColumnIndex(COL_SOURCE_ORDER))
}
}
class ChapterDeleteResolver : DefaultDeleteResolver<Chapter>() {
override fun mapToDeleteQuery(obj: Chapter) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -1,63 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.HistoryImpl
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.COL_CHAPTER_ID
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.COL_LAST_READ
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.COL_TIME_READ
import eu.kanade.tachiyomi.data.database.tables.HistoryTable.TABLE
class HistoryTypeMapping : SQLiteTypeMapping<History>(
HistoryPutResolver(),
HistoryGetResolver(),
HistoryDeleteResolver(),
)
open class HistoryPutResolver : DefaultPutResolver<History>() {
override fun mapToInsertQuery(obj: History) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: History) = ContentValues(4).apply {
put(COL_ID, obj.id)
put(COL_CHAPTER_ID, obj.chapter_id)
put(COL_LAST_READ, obj.last_read)
put(COL_TIME_READ, obj.time_read)
}
}
class HistoryGetResolver : DefaultGetResolver<History>() {
override fun mapFromCursor(cursor: Cursor): History = HistoryImpl().apply {
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
chapter_id = cursor.getLong(cursor.getColumnIndex(COL_CHAPTER_ID))
last_read = cursor.getLong(cursor.getColumnIndex(COL_LAST_READ))
time_read = cursor.getLong(cursor.getColumnIndex(COL_TIME_READ))
}
}
class HistoryDeleteResolver : DefaultDeleteResolver<History>() {
override fun mapToDeleteQuery(obj: History) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -1,59 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_CATEGORY_ID
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.COL_MANGA_ID
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable.TABLE
class MangaCategoryTypeMapping : SQLiteTypeMapping<MangaCategory>(
MangaCategoryPutResolver(),
MangaCategoryGetResolver(),
MangaCategoryDeleteResolver(),
)
class MangaCategoryPutResolver : DefaultPutResolver<MangaCategory>() {
override fun mapToInsertQuery(obj: MangaCategory) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: MangaCategory) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: MangaCategory) = ContentValues(3).apply {
put(COL_ID, obj.id)
put(COL_MANGA_ID, obj.manga_id)
put(COL_CATEGORY_ID, obj.category_id)
}
}
class MangaCategoryGetResolver : DefaultGetResolver<MangaCategory>() {
override fun mapFromCursor(cursor: Cursor): MangaCategory = MangaCategory().apply {
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
manga_id = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID))
category_id = cursor.getInt(cursor.getColumnIndex(COL_CATEGORY_ID))
}
}
class MangaCategoryDeleteResolver : DefaultDeleteResolver<MangaCategory>() {
override fun mapToDeleteQuery(obj: MangaCategory) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -1,117 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ARTIST
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_AUTHOR
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_CHAPTER_FLAGS
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DATE_ADDED
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DESCRIPTION
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FAVORITE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FILTERED_SCANLATORS
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_HIDE_TITLE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_INITIALIZED
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_LAST_UPDATE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_SOURCE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_STATUS
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_THUMBNAIL_URL
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_TITLE
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_UPDATE_STRATEGY
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_URL
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_VIEWER
import eu.kanade.tachiyomi.data.database.tables.MangaTable.TABLE
import eu.kanade.tachiyomi.domain.manga.models.Manga
import yokai.data.updateStrategyAdapter
class MangaTypeMapping : SQLiteTypeMapping<Manga>(
MangaPutResolver(),
MangaGetResolver(),
MangaDeleteResolver(),
)
class MangaPutResolver : DefaultPutResolver<Manga>() {
override fun mapToInsertQuery(obj: Manga) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: Manga) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: Manga) = ContentValues(15).apply {
put(COL_ID, obj.id)
put(COL_SOURCE, obj.source)
put(COL_URL, obj.url)
put(COL_ARTIST, obj.originalArtist)
put(COL_AUTHOR, obj.originalAuthor)
put(COL_DESCRIPTION, obj.originalDescription)
put(COL_GENRE, obj.originalGenre)
put(COL_TITLE, obj.originalTitle)
put(COL_STATUS, obj.originalStatus)
put(COL_THUMBNAIL_URL, obj.thumbnail_url)
put(COL_FAVORITE, obj.favorite)
put(COL_LAST_UPDATE, obj.last_update)
put(COL_INITIALIZED, obj.initialized)
put(COL_VIEWER, obj.viewer_flags)
put(COL_HIDE_TITLE, obj.hide_title)
put(COL_CHAPTER_FLAGS, obj.chapter_flags)
put(COL_DATE_ADDED, obj.date_added)
put(COL_FILTERED_SCANLATORS, obj.filtered_scanlators)
put(COL_UPDATE_STRATEGY, obj.update_strategy.let(updateStrategyAdapter::encode).toInt())
}
}
interface BaseMangaGetResolver {
fun mapBaseFromCursor(manga: Manga, cursor: Cursor) = manga.apply {
id = cursor.getLong(cursor.getColumnIndex(COL_ID))
source = cursor.getLong(cursor.getColumnIndex(COL_SOURCE))
url = cursor.getString(cursor.getColumnIndex(COL_URL))
artist = cursor.getString(cursor.getColumnIndex(COL_ARTIST))
author = cursor.getString(cursor.getColumnIndex(COL_AUTHOR))
description = cursor.getString(cursor.getColumnIndex(COL_DESCRIPTION))
genre = cursor.getString(cursor.getColumnIndex(COL_GENRE))
title = cursor.getString(cursor.getColumnIndex(COL_TITLE))
status = cursor.getInt(cursor.getColumnIndex(COL_STATUS))
thumbnail_url = cursor.getString(cursor.getColumnIndex(COL_THUMBNAIL_URL))
favorite = cursor.getInt(cursor.getColumnIndex(COL_FAVORITE)) == 1
last_update = cursor.getLong(cursor.getColumnIndex(COL_LAST_UPDATE))
initialized = cursor.getInt(cursor.getColumnIndex(COL_INITIALIZED)) == 1
viewer_flags = cursor.getInt(cursor.getColumnIndex(COL_VIEWER))
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
hide_title = cursor.getInt(cursor.getColumnIndex(COL_HIDE_TITLE)) == 1
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
filtered_scanlators = cursor.getString(cursor.getColumnIndex(COL_FILTERED_SCANLATORS))
update_strategy = cursor.getInt(cursor.getColumnIndex(COL_UPDATE_STRATEGY)).let {
updateStrategyAdapter.decode(it.toLong())
}
}
}
open class MangaGetResolver : DefaultGetResolver<Manga>(), BaseMangaGetResolver {
override fun mapFromCursor(cursor: Cursor): Manga {
return mapBaseFromCursor(MangaImpl(), cursor)
}
}
class MangaDeleteResolver : DefaultDeleteResolver<Manga>() {
override fun mapToDeleteQuery(obj: Manga) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -1,65 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.content.ContentValues
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.SearchMetadata
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.COL_EXTRA
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.COL_EXTRA_VERSION
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.COL_INDEXED_EXTRA
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.COL_MANGA_ID
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.COL_UPLOADER
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable.TABLE
class SearchMetadataTypeMapping : SQLiteTypeMapping<SearchMetadata>(
SearchMetadataPutResolver(),
SearchMetadataGetResolver(),
SearchMetadataDeleteResolver(),
)
class SearchMetadataPutResolver : DefaultPutResolver<SearchMetadata>() {
override fun mapToInsertQuery(obj: SearchMetadata) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: SearchMetadata) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_MANGA_ID = ?")
.whereArgs(obj.mangaId)
.build()
override fun mapToContentValues(obj: SearchMetadata) = ContentValues(5).apply {
put(COL_MANGA_ID, obj.mangaId)
put(COL_UPLOADER, obj.uploader)
put(COL_EXTRA, obj.extra)
put(COL_INDEXED_EXTRA, obj.indexedExtra)
put(COL_EXTRA_VERSION, obj.extraVersion)
}
}
class SearchMetadataGetResolver : DefaultGetResolver<SearchMetadata>() {
override fun mapFromCursor(cursor: Cursor): SearchMetadata = SearchMetadata(
mangaId = cursor.getLong(cursor.getColumnIndex(COL_MANGA_ID)),
uploader = cursor.getString(cursor.getColumnIndex(COL_UPLOADER)),
extra = cursor.getString(cursor.getColumnIndex(COL_EXTRA)),
indexedExtra = cursor.getString(cursor.getColumnIndex(COL_INDEXED_EXTRA)),
extraVersion = cursor.getInt(cursor.getColumnIndex(COL_EXTRA_VERSION)),
)
}
class SearchMetadataDeleteResolver : DefaultDeleteResolver<SearchMetadata>() {
override fun mapToDeleteQuery(obj: SearchMetadata) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_MANGA_ID = ?")
.whereArgs(obj.mangaId)
.build()
}

View file

@ -1,90 +0,0 @@
package eu.kanade.tachiyomi.data.database.mappers
import android.database.Cursor
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_FINISH_DATE
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LAST_CHAPTER_READ
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_LIBRARY_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MANGA_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_MEDIA_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_SCORE
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_START_DATE
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_STATUS
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_SYNC_ID
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TITLE
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TOTAL_CHAPTERS
import eu.kanade.tachiyomi.data.database.tables.TrackTable.COL_TRACKING_URL
import eu.kanade.tachiyomi.data.database.tables.TrackTable.TABLE
class TrackTypeMapping : SQLiteTypeMapping<Track>(
TrackPutResolver(),
TrackGetResolver(),
TrackDeleteResolver(),
)
class TrackPutResolver : DefaultPutResolver<Track>() {
override fun mapToInsertQuery(obj: Track) = InsertQuery.builder()
.table(TABLE)
.build()
override fun mapToUpdateQuery(obj: Track) = UpdateQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
override fun mapToContentValues(obj: Track) = contentValuesOf(
COL_ID to obj.id,
COL_MANGA_ID to obj.manga_id,
COL_SYNC_ID to obj.sync_id,
COL_MEDIA_ID to obj.media_id,
COL_LIBRARY_ID to obj.library_id,
COL_TITLE to obj.title,
COL_LAST_CHAPTER_READ to obj.last_chapter_read,
COL_TOTAL_CHAPTERS to obj.total_chapters,
COL_STATUS to obj.status,
COL_TRACKING_URL to obj.tracking_url,
COL_SCORE to obj.score,
COL_START_DATE to obj.started_reading_date,
COL_FINISH_DATE to obj.finished_reading_date,
)
}
class TrackGetResolver : DefaultGetResolver<Track>() {
override fun mapFromCursor(cursor: Cursor): Track = TrackImpl().apply {
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID))
manga_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MANGA_ID))
sync_id = cursor.getInt(cursor.getColumnIndexOrThrow(COL_SYNC_ID))
media_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_MEDIA_ID))
library_id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_LIBRARY_ID))
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE))
last_chapter_read = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_LAST_CHAPTER_READ))
total_chapters = cursor.getInt(cursor.getColumnIndexOrThrow(COL_TOTAL_CHAPTERS))
status = cursor.getInt(cursor.getColumnIndexOrThrow(COL_STATUS))
score = cursor.getFloat(cursor.getColumnIndexOrThrow(COL_SCORE))
tracking_url = cursor.getString(cursor.getColumnIndexOrThrow(COL_TRACKING_URL))
started_reading_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_START_DATE))
finished_reading_date = cursor.getLong(cursor.getColumnIndexOrThrow(COL_FINISH_DATE))
}
}
class TrackDeleteResolver : DefaultDeleteResolver<Track>() {
override fun mapToDeleteQuery(obj: Track) = DeleteQuery.builder()
.table(TABLE)
.where("$COL_ID = ?")
.whereArgs(obj.id)
.build()
}

View file

@ -3,9 +3,9 @@ package eu.kanade.tachiyomi.data.database.models
import android.content.Context
import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.ui.library.LibrarySort
import java.io.Serializable
import yokai.i18n.MR
import yokai.util.lang.getString
import java.io.Serializable
interface Category : Serializable {
@ -37,8 +37,7 @@ interface Category : Serializable {
return ((mangaSort?.minus('a') ?: 0) % 2) != 1
}
fun sortingMode(nullAsDND: Boolean = false): LibrarySort? = LibrarySort.valueOf(mangaSort)
?: if (nullAsDND && !isDynamic) LibrarySort.DragAndDrop else null
fun sortingMode(): LibrarySort? = LibrarySort.valueOf(mangaSort)
val isDragAndDrop
get() = (
@ -53,7 +52,24 @@ interface Category : Serializable {
mangaSort = (LibrarySort.valueOf(sort) ?: LibrarySort.Title).categoryValue
}
fun mangaOrderToString(): String =
if (mangaSort != null) mangaSort.toString() else mangaOrder.joinToString("/")
// For dynamic categories
fun dynamicHeaderKey(): String {
if (!isDynamic) throw IllegalStateException("This category is not a dynamic category")
return when {
sourceId != null -> "${name}$sourceSplitter${sourceId}"
langId != null -> "${langId}$langSplitter${name}"
else -> name
}
}
companion object {
const val sourceSplitter = "◘•◘"
const val langSplitter = "⨼⨦⨠"
var lastCategoriesAddedTo = emptySet<Int>()
fun create(name: String): Category = CategoryImpl().apply {

View file

@ -32,10 +32,14 @@ class CategoryImpl : Category {
val category = other as Category
if (isDynamic && category.isDynamic) return dynamicHeaderKey() == category.dynamicHeaderKey()
return name == category.name
}
override fun hashCode(): Int {
if (isDynamic) return dynamicHeaderKey().hashCode()
return name.hashCode()
}
}

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.source.model.SChapter
import java.io.Serializable
import yokai.domain.chapter.models.ChapterUpdate
interface Chapter : SChapter, Serializable {
@ -24,6 +25,15 @@ interface Chapter : SChapter, Serializable {
val isRecognizedNumber: Boolean
get() = chapter_number >= 0f
fun toProgressUpdate() =
ChapterUpdate(
id = this.id!!,
read = this.read,
bookmark = this.bookmark,
lastPageRead = this.last_page_read.toLong(),
pagesLeft = this.pages_left.toLong(),
)
companion object {
fun create(): Chapter = ChapterImpl().apply {
@ -80,4 +90,8 @@ interface Chapter : SChapter, Serializable {
source_order = other.source_order
copyFrom(other as SChapter)
}
fun copy() = ChapterImpl().apply {
copyFrom(this@Chapter)
}
}

View file

@ -4,11 +4,11 @@ import eu.kanade.tachiyomi.source.model.SChapter
fun SChapter.toChapter(): ChapterImpl {
return ChapterImpl().apply {
name = this@SChapter.name
url = this@SChapter.url
date_upload = this@SChapter.date_upload
chapter_number = this@SChapter.chapter_number
scanlator = this@SChapter.scanlator
name = this@toChapter.name
url = this@toChapter.url
date_upload = this@toChapter.date_upload
chapter_number = this@toChapter.chapter_number
scanlator = this@toChapter.scanlator
}
}

View file

@ -38,5 +38,19 @@ interface History : Serializable {
fun create(chapter: Chapter): History = HistoryImpl().apply {
this.chapter_id = chapter.id!!
}
fun create(): History = HistoryImpl()
fun mapper(
id: Long,
chapterId: Long,
lastRead: Long?,
timeRead: Long?,
): History = HistoryImpl().apply {
this.id = id
this.chapter_id = chapterId
lastRead?.let { this.last_read = it }
timeRead?.let { this.time_read = it }
}
}
}

View file

@ -1,54 +1,25 @@
package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.ui.library.LibraryItem
import yokai.data.updateStrategyAdapter
import eu.kanade.tachiyomi.domain.manga.models.Manga
import kotlin.math.roundToInt
data class LibraryManga(
val manga: Manga,
var unread: Int = 0,
var read: Int = 0,
var category: Int = 0,
var bookmarkCount: Int = 0,
var totalChapters: Int = 0,
var latestUpdate: Int = 0,
var lastRead: Int = 0,
var lastFetch: Int = 0,
) : MangaImpl() {
var realMangaCount = 0
get() = if (isBlank()) field else throw IllegalStateException("realMangaCount is only accessible by placeholders")
set(value) {
if (!isBlank()) throw IllegalStateException("realMangaCount can only be set by placeholders")
field = value
}
var latestUpdate: Long = 0,
var lastRead: Long = 0,
var lastFetch: Long = 0,
) {
val hasRead
get() = read > 0
@Transient
var items: List<LibraryItem>? = null
get() = if (isHidden()) field else throw IllegalStateException("items only accessible by placeholders")
set(value) {
if (!isHidden()) throw IllegalStateException("items can only be set by placeholders")
field = value
}
companion object {
fun createBlank(categoryId: Int): LibraryManga = LibraryManga().apply {
title = ""
id = Long.MIN_VALUE
category = categoryId
}
fun createHide(categoryId: Int, title: String, hiddenItems: List<LibraryItem>): LibraryManga =
createBlank(categoryId).apply {
this.title = title
this.status = -1
this.read = hiddenItems.size
this.items = hiddenItems
}
fun mapper(
// manga
id: Long,
source: Long,
url: String,
@ -59,15 +30,17 @@ data class LibraryManga(
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Long,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
hideTitle: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// libraryManga
total: Long,
readCount: Double,
bookmarkCount: Double,
@ -75,33 +48,37 @@ data class LibraryManga(
latestUpdate: Long,
lastRead: Long,
lastFetch: Long,
): LibraryManga = createBlank(categoryId.toInt()).apply {
this.id = id
this.source = source
this.url = url
this.artist = artist
this.author = author
this.description = description
this.genre = genre
this.title = title
this.status = status.toInt()
this.thumbnail_url = thumbnailUrl
this.favorite = favorite > 0
this.last_update = lastUpdate ?: 0L
this.initialized = initialized
this.viewer_flags = viewerFlags.toInt()
this.hide_title = hideTitle > 0
this.chapter_flags = chapterFlags.toInt()
this.date_added = dateAdded ?: 0L
this.filtered_scanlators = filteredScanlators
this.update_strategy = updateStrategy.let(updateStrategyAdapter::decode)
this.read = readCount.roundToInt()
this.unread = maxOf((total - readCount).roundToInt(), 0)
this.totalChapters = readCount.roundToInt()
this.bookmarkCount = bookmarkCount.roundToInt()
this.latestUpdate = latestUpdate.toInt()
this.lastRead = lastRead.toInt()
this.lastFetch = lastFetch.toInt()
}
): LibraryManga = LibraryManga(
manga = Manga.mapper(
id = id,
source = source,
url = url,
artist = artist,
author = author,
description = description,
genre = genre,
title = title,
status = status,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewerFlags,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
coverLastModified = coverLastModified,
),
read = readCount.roundToInt(),
unread = maxOf((total - readCount).roundToInt(), 0),
totalChapters = total.toInt(),
bookmarkCount = bookmarkCount.roundToInt(),
category = categoryId.toInt(),
latestUpdate = latestUpdate,
lastRead = lastRead,
lastFetch = lastFetch,
)
}
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.database.models
import android.content.Context
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.domain.manga.models.Manga.Companion.TYPE_COMIC
@ -12,16 +13,20 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
import eu.kanade.tachiyomi.util.system.withIOContext
import java.util.Locale
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.data.updateStrategyAdapter
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.MangaCover
import yokai.domain.manga.models.MangaUpdate
import yokai.i18n.MR
import yokai.util.lang.getString
import java.util.*
fun Manga.sortDescending(preferences: PreferencesHelper): Boolean =
if (usesLocalSort) sortDescending else preferences.chaptersDescAsDefault().get()
@ -99,13 +104,11 @@ fun Manga.defaultReaderType(): Int {
) {
ReadingModeType.LONG_STRIP.flagValue
} else if (currentTags.any {
tag -> tag == "chinese" || tag == "manhua" || tag == "comic"
tag -> tag == "comic"
} || (
isComicSource(sourceName) &&
!sourceName.contains("tapas", true) &&
currentTags.none { tag -> isMangaTag(tag) }
) || (
sourceName.contains("manhua", true) && currentTags.none { tag -> isMangaTag(tag) }
)
) {
ReadingModeType.LEFT_TO_RIGHT.flagValue
@ -173,15 +176,19 @@ var Manga.dominantCoverColors: Pair<Int, Int>?
MangaCoverMetadata.addCoverColor(this, value.first, value.second)
}
fun Manga.Companion.create(source: Long) = MangaImpl().apply {
this.source = source
}
var Manga.vibrantCoverColor: Int?
get() = MangaCoverMetadata.getVibrantColor(id)
set(value) {
id?.let { MangaCoverMetadata.setVibrantColor(it, value) }
}
fun Manga.Companion.create(pathUrl: String, title: String, source: Long = 0) = MangaImpl().apply {
url = pathUrl
this.title = title
this.source = source
}
fun Manga.Companion.create(url: String, title: String, source: Long = 0) =
MangaImpl(
source = source,
url = url,
).apply {
this.title = title
}
fun Manga.Companion.mapper(
id: Long,
@ -194,32 +201,78 @@ fun Manga.Companion.mapper(
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Long,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
hideTitle: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long
) = create(source).apply {
updateStrategy: Long,
coverLastModified: Long,
) = create(url, title, source).apply {
this.id = id
this.url = url
this.artist = artist
this.author = author
this.description = description
this.genre = genre
this.title = title
this.status = status.toInt()
this.thumbnail_url = thumbnailUrl
this.favorite = favorite > 0
this.favorite = favorite
this.last_update = lastUpdate ?: 0L
this.initialized = initialized
this.viewer_flags = viewerFlags.toInt()
this.chapter_flags = chapterFlags.toInt()
this.hide_title = hideTitle > 0
this.hide_title = hideTitle
this.date_added = dateAdded ?: 0L
this.filtered_scanlators = filteredScanlators
this.update_strategy = updateStrategy.let(updateStrategyAdapter::decode)
this.cover_last_modified = coverLastModified
}
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(this).exists()
}
/**
* Call before updating [Manga.thumbnail_url] to ensure old cover can be cleared from cache
*/
fun Manga.prepareCoverUpdate(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean) {
// Never refresh covers if the new url is null, as the current url has possibly become invalid
val newUrl = remoteManga.thumbnail_url ?: return
// Never refresh covers if the url is empty to avoid "losing" existing covers
if (newUrl.isEmpty()) return
if (!refreshSameUrl && thumbnail_url == newUrl) return
when {
isLocal() -> {
cover_last_modified = System.currentTimeMillis()
}
hasCustomCover(coverCache) -> {
coverCache.deleteFromCache(this, false)
}
else -> {
cover_last_modified = System.currentTimeMillis()
coverCache.deleteFromCache(this, false)
}
}
}
fun Manga.removeCover(coverCache: CoverCache = Injekt.get(), deleteCustom: Boolean = true) {
if (isLocal()) return
cover_last_modified = System.currentTimeMillis()
coverCache.deleteFromCache(this, deleteCustom)
}
suspend fun Manga.updateCoverLastModified(updateManga: UpdateManga = Injekt.get()) {
cover_last_modified = System.currentTimeMillis()
updateManga.await(MangaUpdate(id = id!!, coverLastModified = cover_last_modified))
}
suspend fun MangaCover.updateCoverLastModified(updateManga: UpdateManga = Injekt.get()) {
updateManga.await(MangaUpdate(id = mangaId!!, coverLastModified = System.currentTimeMillis()))
}

View file

@ -2,4 +2,82 @@ package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaChapter(val manga: Manga, val chapter: Chapter)
class MangaChapter(val manga: Manga, val chapter: Chapter) {
companion object {
fun mapper(
// manga
mangaId: Long,
source: Long,
mangaUrl: String,
artist: String?,
author: String?,
description: String?,
genre: String?,
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewer: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// chapter
chapterId: Long,
_mangaId: Long,
chapterUrl: String,
name: String,
scanlator: String?,
read: Boolean,
bookmark: Boolean,
lastPageRead: Long,
pagesLeft: Long,
chapterNumber: Double,
sourceOrder: Long,
dateFetch: Long,
dateUpload: Long,
) = MangaChapter(
Manga.mapper(
id = mangaId,
source = source,
url = mangaUrl,
artist = artist,
author = author,
description = description,
genre = genre,
title = title,
status = status,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewer,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
coverLastModified = coverLastModified,
),
Chapter.mapper(
id = chapterId,
mangaId = _mangaId,
url = chapterUrl,
name = name,
scanlator = scanlator,
read = read,
bookmark = bookmark,
lastPageRead = lastPageRead,
pagesLeft = pagesLeft,
chapterNumber = chapterNumber,
sourceOrder = sourceOrder,
dateFetch = dateFetch,
dateUpload = dateUpload,
),
)
}
}

View file

@ -12,7 +12,114 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga
data class MangaChapterHistory(val manga: Manga, val chapter: Chapter, val history: History, var extraChapters: List<ChapterHistory> = emptyList()) {
companion object {
fun createBlank() = MangaChapterHistory(MangaImpl(), ChapterImpl(), HistoryImpl())
fun createBlank() = MangaChapterHistory(
MangaImpl(null, -1, ""),
ChapterImpl(),
HistoryImpl(),
)
fun mapper(
// manga
mangaId: Long,
source: Long,
mangaUrl: String,
artist: String?,
author: String?,
description: String?,
genre: String?,
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
initialized: Boolean,
viewer: Long,
hideTitle: Boolean,
chapterFlags: Long,
dateAdded: Long?,
filteredScanlators: String?,
updateStrategy: Long,
coverLastModified: Long,
// chapter
chapterId: Long?,
chapterMangaId: Long?,
chapterUrl: String?,
name: String?,
scanlator: String?,
read: Boolean?,
bookmark: Boolean?,
lastPageRead: Long?,
pagesLeft: Long?,
chapterNumber: Double?,
sourceOrder: Long?,
dateFetch: Long?,
dateUpload: Long?,
// history
historyId: Long?,
historyChapterId: Long?,
historyLastRead: Long?,
historyTimeRead: Long?,
): MangaChapterHistory {
val manga = Manga.mapper(
id = mangaId,
source = source,
url = mangaUrl,
artist = artist,
author = author,
description = description,
genre = genre,
title = title,
status = status,
thumbnailUrl = thumbnailUrl,
favorite = favorite,
lastUpdate = lastUpdate,
initialized = initialized,
viewerFlags = viewer,
hideTitle = hideTitle,
chapterFlags = chapterFlags,
dateAdded = dateAdded,
filteredScanlators = filteredScanlators,
updateStrategy = updateStrategy,
coverLastModified = coverLastModified,
)
val chapter = try {
Chapter.mapper(
id = chapterId!!,
mangaId = chapterMangaId!!,
url = chapterUrl!!,
name = name!!,
scanlator = scanlator,
read = read!!,
bookmark = bookmark!!,
lastPageRead = lastPageRead!!,
pagesLeft = pagesLeft!!,
chapterNumber = chapterNumber!!,
sourceOrder = sourceOrder!!,
dateFetch = dateFetch!!,
dateUpload = dateUpload!!,
)
} catch (_: NullPointerException) {
ChapterImpl()
}
val history = try {
History.mapper(
id = historyId!!,
chapterId = historyChapterId!!,
lastRead = historyLastRead,
timeRead = historyTimeRead,
)
} catch (_: NullPointerException) {
HistoryImpl().apply {
historyChapterId?.let { chapter_id = it }
historyLastRead?.let { last_read = it }
historyTimeRead?.let { time_read = it }
}
}
return MangaChapterHistory(manga, chapter, history)
}
}
}

View file

@ -8,13 +8,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import uy.kohesive.injekt.injectLazy
open class MangaImpl : Manga {
override var id: Long? = null
override var source: Long = -1
override lateinit var url: String
open class MangaImpl(
override var id: Long? = null,
override var source: Long = -1,
override var url: String = "",
) : Manga {
private val customMangaManager: CustomMangaManager by injectLazy()
@ -72,7 +70,8 @@ open class MangaImpl : Manga {
override var update_strategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE
override var filtered_scanlators: String? = null
// TODO: It's probably fine to set this to non-null string in the future
override var filtered_scanlators: String? = ""
override lateinit var ogTitle: String
override var ogAuthor: String? = null
@ -81,6 +80,8 @@ open class MangaImpl : Manga {
override var ogGenre: String? = null
override var ogStatus: Int = 0
override var cover_last_modified: Long = 0L
override fun copyFrom(other: SManga) {
if (other is MangaImpl && other::ogTitle.isInitialized &&
other.title.isNotBlank() && other.ogTitle != ogTitle
@ -104,7 +105,7 @@ open class MangaImpl : Manga {
}
override fun hashCode(): Int {
return if (::url.isInitialized) {
return if (url.isNotBlank()) {
url.hashCode()
} else {
(id ?: 0L).hashCode()

View file

@ -1,3 +1,3 @@
package eu.kanade.tachiyomi.data.database.models
data class SourceIdMangaCount(val source: Long, val count: Int)
data class SourceIdMangaCount(val source: Long, val count: Long)

View file

@ -8,7 +8,7 @@ interface Track : Serializable {
var manga_id: Long
var sync_id: Int
var sync_id: Long
var media_id: Long
@ -18,7 +18,7 @@ interface Track : Serializable {
var last_chapter_read: Float
var total_chapters: Int
var total_chapters: Long
var score: Float
@ -39,8 +39,38 @@ interface Track : Serializable {
}
companion object {
fun create(serviceId: Int): Track = TrackImpl().apply {
fun create(serviceId: Long): Track = TrackImpl().apply {
sync_id = serviceId
}
fun mapper(
id: Long,
mangaId: Long,
syncId: Long,
remoteId: Long,
libraryId: Long?,
title: String,
lastChapterRead: Double,
totalChapters: Long,
status: Long,
score: Double,
remoteUrl: String,
startDate: Long,
finishDate: Long,
) = TrackImpl().apply {
this.id = id
this.manga_id = mangaId
this.sync_id = syncId
this.media_id = remoteId
this.library_id = libraryId
this.title = title
this.last_chapter_read = lastChapterRead.toFloat()
this.total_chapters = totalChapters
this.score = score.toFloat()
this.status = status.toInt()
this.started_reading_date = startDate
this.finished_reading_date = finishDate
this.tracking_url = remoteUrl
}
}
}

View file

@ -4,11 +4,11 @@ class TrackImpl : Track {
override var id: Long? = null
override var manga_id: Long = 0
override var manga_id: Long = 0L
override var sync_id: Int = 0
override var sync_id: Long = 0L
override var media_id: Long = 0
override var media_id: Long = 0L
override var library_id: Long? = null
@ -16,7 +16,7 @@ class TrackImpl : Track {
override var last_chapter_read: Float = 0F
override var total_chapters: Int = 0
override var total_chapters: Long = 0L
override var score: Float = 0f
@ -43,7 +43,7 @@ class TrackImpl : Track {
override fun hashCode(): Int {
var result = manga_id.hashCode()
result = 31 * result + sync_id
result = 31 * result + sync_id.hashCode()
result = 31 * result + media_id.hashCode()
return result
}

View file

@ -1,39 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
interface CategoryQueries : DbProvider {
fun getCategories() = db.get()
.listOfObjects(Category::class.java)
.withQuery(
Query.builder()
.table(CategoryTable.TABLE)
.orderBy(CategoryTable.COL_ORDER)
.build(),
)
.prepare()
fun getCategoriesForManga(manga: Manga) = db.get()
.listOfObjects(Category::class.java)
.withQuery(
RawQuery.builder()
.query(getCategoriesForMangaQuery())
.args(manga.id)
.build(),
)
.prepare()
fun insertCategory(category: Category) = db.put().`object`(category).prepare()
fun insertCategories(categories: List<Category>) = db.put().objects(categories).prepare()
fun deleteCategory(category: Category) = db.delete().`object`(category).prepare()
fun deleteCategories(categories: List<Category>) = db.delete().objects(categories).prepare()
}

View file

@ -1,103 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.MangaChapter
import eu.kanade.tachiyomi.data.database.resolvers.ChapterKnownBackupPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterGetResolver
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
import eu.kanade.tachiyomi.util.lang.sqLite
interface ChapterQueries : DbProvider {
fun getChapters(manga: Manga) = getChapters(manga.id)
fun getChapters(mangaId: Long?) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun getRecentChapters(search: String = "", offset: Int, isResuming: Boolean) = db.get()
.listOfObjects(MangaChapter::class.java)
.withQuery(
RawQuery.builder()
.query(getRecentsQuery(search.sqLite, offset, isResuming))
.observesTables(ChapterTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterGetResolver.INSTANCE)
.prepare()
fun getChapter(id: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(id)
.build(),
)
.prepare()
fun getChapter(url: String) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build(),
)
.prepare()
fun getChapters(url: String) = db.get()
.listOfObjects(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(url)
.build(),
)
.prepare()
fun getChapter(url: String, mangaId: Long) = db.get()
.`object`(Chapter::class.java)
.withQuery(
Query.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(url, mangaId)
.build(),
)
.prepare()
fun insertChapter(chapter: Chapter) = db.put().`object`(chapter).prepare()
fun insertChapters(chapters: List<Chapter>) = db.put().objects(chapters).prepare()
fun updateKnownChaptersBackup(chapters: List<Chapter>) = db.put()
.objects(chapters)
.withPutResolver(ChapterKnownBackupPutResolver())
.prepare()
fun updateChapterProgress(chapter: Chapter) = db.put()
.`object`(chapter)
.withPutResolver(ChapterProgressPutResolver())
.prepare()
fun updateChaptersProgress(chapters: List<Chapter>) = db.put()
.objects(chapters)
.withPutResolver(ChapterProgressPutResolver())
.prepare()
}

View file

@ -1,192 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.data.database.resolvers.HistoryUpsertResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterHistoryGetResolver
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
import eu.kanade.tachiyomi.util.lang.sqLite
interface HistoryQueries : DbProvider {
/**
* Insert history into database
* @param history object containing history information
*/
// fun insertHistory(history: History) = db.put().`object`(history).prepare()
// /**
// * Returns history of recent manga containing last read chapter in 25s
// * @param date recent date range
// * @offset offset the db by
// */
// fun getRecentManga(date: Date, offset: Int = 0, search: String = "") = db.get()
// .listOfObjects(MangaChapterHistory::class.java)
// .withQuery(
// RawQuery.builder()
// .query(getRecentMangasQuery(offset, search.sqLite))
// .args(date.time)
// .observesTables(HistoryTable.TABLE)
// .build()
// )
// .withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
// .prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
* @offset offset the db by
*/
fun getHistoryUngrouped(search: String = "", offset: Int, isResuming: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(getRecentHistoryUngrouped(search.sqLite, offset, isResuming))
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
* @offset offset the db by
*/
fun getRecentMangaLimit(search: String = "", offset: Int, isResuming: Boolean) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(getRecentMangasLimitQuery(search.sqLite, offset, isResuming))
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of manga read during period
* @param startDate start date of the period
* @param endDate end date of the period
* @offset offset the db by
*/
fun getHistoryPerPeriod(startDate: Long, endDate: Long) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(getHistoryPerPeriodQuery(startDate, endDate))
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
/**
* Returns history of recent manga containing last read chapter in 25s
* @param date recent date range
* @offset offset the db by
*/
fun getAllRecentsTypes(
search: String = "",
includeRead: Boolean,
endless: Boolean,
offset: Int,
isResuming: Boolean,
) = db.get()
.listOfObjects(MangaChapterHistory::class.java)
.withQuery(
RawQuery.builder()
.query(
getAllRecentsType(
search.sqLite,
includeRead,
endless,
offset,
isResuming,
),
)
// .args(date.time, startDate.time)
.observesTables(HistoryTable.TABLE)
.build(),
)
.withGetResolver(MangaChapterHistoryGetResolver.INSTANCE)
.prepare()
fun getHistoryByMangaId(mangaId: Long) = db.get()
.listOfObjects(History::class.java)
.withQuery(
RawQuery.builder()
.query(getHistoryByMangaId())
.args(mangaId)
.observesTables(HistoryTable.TABLE)
.build(),
)
.prepare()
fun getTotalReadDuration(): Long {
val cursor = db.lowLevel()
.rawQuery(
RawQuery.builder()
.query("SELECT SUM(${HistoryTable.COL_TIME_READ}) FROM ${HistoryTable.TABLE}")
.observesTables(HistoryTable.TABLE)
.build(),
)
cursor.moveToFirst()
return cursor.getLong(0)
}
fun getHistoryByChapterUrl(chapterUrl: String) = db.get()
.`object`(History::class.java)
.withQuery(
RawQuery.builder()
.query(getHistoryByChapterUrl())
.args(chapterUrl)
.observesTables(HistoryTable.TABLE)
.build(),
)
.prepare()
/**
* Updates the history last read.
* Inserts history object if not yet in database
* @param history history object
*/
fun upsertHistoryLastRead(history: History) = db.put()
.`object`(history)
.withPutResolver(HistoryUpsertResolver())
.prepare()
/**
* Updates the history last read.
* Inserts history object if not yet in database
* @param historyList history object list
*/
fun upsertHistoryLastRead(historyList: List<History>) = db.inTransactionReturn {
db.put()
.objects(historyList)
.withPutResolver(HistoryUpsertResolver())
.prepare()
}
fun deleteHistory() = db.delete()
.byQuery(
DeleteQuery.builder()
.table(HistoryTable.TABLE)
.build(),
)
.prepare()
fun deleteHistoryNoLastRead() = db.delete()
.byQuery(
DeleteQuery.builder()
.table(HistoryTable.TABLE)
.where("${HistoryTable.COL_LAST_READ} = ?")
.whereArgs(0)
.build(),
)
.prepare()
}

View file

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.Queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.inTransaction
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
interface MangaCategoryQueries : DbProvider {
fun insertMangaCategory(mangaCategory: MangaCategory) = db.put().`object`(mangaCategory).prepare()
fun insertMangasCategories(mangasCategories: List<MangaCategory>) = db.put().objects(mangasCategories).prepare()
fun deleteOldMangasCategories(mangas: List<Manga>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MangaCategoryTable.TABLE)
.where("${MangaCategoryTable.COL_MANGA_ID} IN (${Queries.placeholders(mangas.size)})")
.whereArgs(*mangas.map { it.id }.toTypedArray())
.build(),
)
.prepare()
fun setMangaCategories(mangasCategories: List<MangaCategory>, mangas: List<Manga>) {
db.inTransaction {
deleteOldMangasCategories(mangas).executeAsBlocking()
insertMangasCategories(mangasCategories).executeAsBlocking()
}
}
}

View file

@ -1,133 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.Queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
import eu.kanade.tachiyomi.data.database.resolvers.MangaDateAddedPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaTitlePutResolver
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
interface MangaQueries : DbProvider {
fun getDuplicateLibraryManga(manga: Manga) = db.get()
.`object`(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = 1 AND LOWER(${MangaTable.COL_TITLE}) = ? AND ${MangaTable.COL_SOURCE} != ?")
.whereArgs(
manga.title.lowercase(),
manga.source,
)
.limit(1)
.build(),
)
.prepare()
fun getFavoriteMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ?")
.whereArgs(1)
.orderBy(MangaTable.COL_TITLE)
.build(),
)
.prepare()
fun getManga(url: String, sourceId: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_URL} = ? AND ${MangaTable.COL_SOURCE} = ?")
.whereArgs(url, sourceId)
.build(),
)
.prepare()
fun getManga(id: Long) = db.get()
.`object`(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(id)
.build(),
)
.prepare()
fun getSourceIdsWithNonLibraryManga() = db.get()
.listOfObjects(SourceIdMangaCount::class.java)
.withQuery(
RawQuery.builder()
.query(getSourceIdsWithNonLibraryMangaQuery())
.observesTables(MangaTable.TABLE)
.build(),
)
.withGetResolver(SourceIdMangaCountGetResolver.INSTANCE)
.prepare()
fun insertManga(manga: Manga) = db.put().`object`(manga).prepare()
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
fun updateMangaFavorite(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaFavoritePutResolver())
.prepare()
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
fun updateMangaAdded(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaDateAddedPutResolver())
.prepare()
// FIXME: Migrate to SQLDelight, on halt: used by StorIO's inTransaction
fun updateMangaTitle(manga: Manga) = db.put()
.`object`(manga)
.withPutResolver(MangaTitlePutResolver())
.prepare()
fun deleteMangasNotInLibraryBySourceIds(sourceIds: List<Long>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_FAVORITE} = ? AND ${MangaTable.COL_SOURCE} IN (${Queries.placeholders(sourceIds.size)})")
.whereArgs(0, *sourceIds.toTypedArray())
.build(),
)
.prepare()
fun deleteMangasNotInLibraryAndNotReadBySourceIds(sourceIds: List<Long>) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(MangaTable.TABLE)
.where(
"""
${MangaTable.COL_FAVORITE} = ? AND ${MangaTable.COL_SOURCE} IN (${Queries.placeholders(sourceIds.size)}) AND ${MangaTable.COL_ID} NOT IN (
SELECT ${ChapterTable.COL_MANGA_ID} FROM ${ChapterTable.TABLE} WHERE ${ChapterTable.COL_READ} = 1 OR ${ChapterTable.COL_LAST_PAGE_READ} != 0
)
""".trimIndent(),
)
.whereArgs(0, *sourceIds.toTypedArray())
.build(),
)
.prepare()
fun getReadNotInLibraryMangas() = db.get()
.listOfObjects(Manga::class.java)
.withQuery(
RawQuery.builder()
.query(getReadMangaNotInLibraryQuery())
.build(),
)
.prepare()
}

View file

@ -1,317 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import eu.kanade.tachiyomi.data.database.resolvers.SourceIdMangaCountGetResolver
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
import eu.kanade.tachiyomi.data.database.tables.CategoryTable as Category
import eu.kanade.tachiyomi.data.database.tables.ChapterTable as Chapter
import eu.kanade.tachiyomi.data.database.tables.HistoryTable as History
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCategory
import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga
// TODO: Migrate to SQLDelight
/**
* Query to get the recent chapters of manga from the library up to a date.
*/
fun getRecentsQuery(search: String, offset: Int, isResuming: Boolean) =
"""
SELECT
M.url AS mangaUrl,
M.*,
C.*
FROM mangas AS M
JOIN chapters AS C
ON M._id = C.manga_id
LEFT JOIN scanlators_view AS S
ON C.manga_id = S.manga_id
AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '/<INVALID>/')
WHERE M.favorite = 1
AND C.date_fetch > M.date_added
AND lower(M.title) LIKE '%$search%'
AND S.name IS NULL
ORDER BY C.date_fetch DESC
${limitAndOffset(true, isResuming, offset)}
"""
fun limitAndOffset(endless: Boolean, isResuming: Boolean, offset: Int): String {
return when {
isResuming && endless && offset > 0 -> "LIMIT $offset"
endless -> "LIMIT ${RecentsPresenter.ENDLESS_LIMIT}\nOFFSET $offset"
else -> "LIMIT ${RecentsPresenter.SHORT_LIMIT}"
}
}
// TODO: Migrate to SQLDelight
/**
* Query to get the recently read chapters of manga from the library up to a date.
* The max_last_read table contains the most recent chapters grouped by manga
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getRecentHistoryUngrouped(
search: String = "",
offset: Int = 0,
isResuming: Boolean,
) =
"""
SELECT
M.url AS mangaUrl,
M.*,
C.*,
H.*
FROM mangas AS M
JOIN chapters AS C
ON M._id = C.manga_id
JOIN history AS H
ON C._id = H.history_chapter_id
AND H.history_last_read > 0
LEFT JOIN scanlators_view AS S
ON C.manga_id = S.manga_id
AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '/<INVALID>/')
WHERE lower(M.title) LIKE '%$search%'
AND S.name IS NULL
ORDER BY H.history_last_read DESC
${limitAndOffset(true, isResuming, offset)}
"""
// TODO: Migrate to SQLDelight
/**
* Query to get the recently read chapters of manga from the library up to a date.
* The max_last_read table contains the most recent chapters grouped by manga
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getRecentMangasLimitQuery(
search: String = "",
offset: Int = 0,
isResuming: Boolean,
) =
"""
SELECT
M.url AS mangaUrl,
M.*,
C.*,
H.*
FROM mangas AS M
JOIN chapters AS C
ON M._id = C.manga_id
JOIN history AS H
ON C._id = H.history_chapter_id
JOIN (
SELECT
C2.manga_id AS manga_id,
C2._id AS history_chapter_id,
MAX(H2.history_last_read) AS history_last_read
FROM chapters AS C2 JOIN history AS H2
ON C2._id = H2.history_chapter_id
GROUP BY C2.manga_id
) AS max_last_read
ON C.manga_id = max_last_read.manga_id
AND max_last_read.history_chapter_id = H.history_chapter_id
AND max_last_read.history_last_read > 0
LEFT JOIN scanlators_view AS S
ON C.manga_id = S.manga_id
AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '/<INVALID>/')
WHERE lower(M.title) LIKE '%$search%'
AND S.name IS NULL
ORDER BY max_last_read.history_last_read DESC
${limitAndOffset(true, isResuming, offset)}
"""
/**
* Query to get the read chapters of manga from the library during the period.
* The max_last_read table contains the most recent chapters grouped by manga
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
*/
fun getHistoryPerPeriodQuery(startDate: Long, endDate: Long) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*, ${History.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
JOIN ${History.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
AND ${History.TABLE}.${History.COL_LAST_READ} >= $startDate
AND ${History.TABLE}.${History.COL_LAST_READ} <= $endDate
ORDER BY ${History.TABLE}.${History.COL_LAST_READ} DESC
"""
/**
* Query to get the recently read manga that has more chapters to read
* The first from checks that there's an unread chapter
* The max_last_read table contains the most recent chapters grouped by manga
* The select statement returns all information of chapters that have the same id as the chapter in max_last_read
* and are read after the given time period
* The Second Union/Select gets recents chapters
* Final Union gets newly added manga
*/
fun getAllRecentsType(
search: String = "",
includeRead: Boolean,
endless: Boolean,
offset: Int = 0,
isResuming: Boolean,
) = """
SELECT * FROM
(SELECT mangas.url as mangaUrl, mangas.*, chapters.*, history.*
FROM (
SELECT mangas.*
FROM mangas
LEFT JOIN (
SELECT manga_id, COUNT(*) AS unread
FROM chapters
WHERE read = 0
GROUP BY manga_id
) AS C
ON _id = C.manga_id
${if (includeRead) "" else "WHERE C.unread > 0"}
GROUP BY _id
ORDER BY title
) AS mangas
JOIN chapters
ON mangas._id = chapters.manga_id
JOIN history
ON chapters._id = history.history_chapter_id
JOIN (
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID}, MAX(${History.TABLE}.${History.COL_LAST_READ}) as ${History.COL_LAST_READ}
FROM ${Chapter.TABLE} JOIN ${History.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS max_last_read
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = max_last_read.${Chapter.COL_MANGA_ID}
AND max_last_read.${History.COL_CHAPTER_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
AND max_last_read.${History.COL_LAST_READ} > 0
AND lower(${Manga.COL_TITLE}) LIKE '%$search%')
UNION
SELECT * FROM
(SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, ${Manga.TABLE}.*, ${Chapter.TABLE}.*,
Null as history_id,
Null as history_chapter_id,
chapters.date_fetch as history_last_read,
Null as history_time_read
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
JOIN (
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID},${Chapter.TABLE}.${Chapter.COL_ID} as ${History.COL_CHAPTER_ID},MAX(${Chapter.TABLE}.${Chapter.COL_DATE_UPLOAD})
FROM ${Chapter.TABLE} JOIN ${Manga.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
WHERE ${Chapter.COL_READ} = 0
GROUP BY ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}) AS newest_chapter
ON ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = newest_chapter.${Chapter.COL_MANGA_ID}
WHERE ${Manga.COL_FAVORITE} = 1
AND newest_chapter.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
AND ${Chapter.COL_DATE_FETCH} > ${Manga.COL_DATE_ADDED}
AND lower(${Manga.COL_TITLE}) LIKE '%$search%')
UNION
SELECT * FROM
(SELECT mangas.url as mangaUrl,
mangas.*,
Null as _id,
Null as manga_id,
Null as url,
Null as name,
Null as read,
Null as scanlator,
Null as bookmark,
Null as date_fetch,
Null as date_upload,
Null as last_page_read,
Null as pages_left,
Null as chapter_number,
Null as source_order,
Null as history_id,
Null as history_chapter_id,
${Manga.TABLE}.${Manga.COL_DATE_ADDED} as history_last_read,
Null as history_time_read
FROM mangas
WHERE ${Manga.COL_FAVORITE} = 1
AND lower(${Manga.COL_TITLE}) LIKE '%$search%')
ORDER BY history_last_read DESC
${limitAndOffset(endless, isResuming, offset)}
"""
fun getHistoryByMangaId() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
ON ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
WHERE ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
"""
fun getHistoryByChapterUrl() =
"""
SELECT ${History.TABLE}.*
FROM ${History.TABLE}
JOIN ${Chapter.TABLE}
ON ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
WHERE ${Chapter.TABLE}.${Chapter.COL_URL} = ? AND ${History.TABLE}.${History.COL_CHAPTER_ID} = ${Chapter.TABLE}.${Chapter.COL_ID}
"""
fun getLastReadMangaQuery() =
"""
SELECT ${Manga.TABLE}.*, MAX(${History.TABLE}.${History.COL_LAST_READ}) AS max
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
JOIN ${History.TABLE}
ON ${Chapter.TABLE}.${Chapter.COL_ID} = ${History.TABLE}.${History.COL_CHAPTER_ID}
WHERE ${Manga.TABLE}.${Manga.COL_FAVORITE} = 1
GROUP BY ${Manga.TABLE}.${Manga.COL_ID}
ORDER BY max DESC
"""
fun getLastFetchedMangaQuery() =
"""
SELECT ${Manga.TABLE}.*, MAX(${Chapter.TABLE}.${Chapter.COL_DATE_FETCH}) AS max
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
WHERE ${Manga.TABLE}.${Manga.COL_FAVORITE} = 1
GROUP BY ${Manga.TABLE}.${Manga.COL_ID}
ORDER BY max DESC
"""
fun getTotalChapterMangaQuery() =
"""
SELECT ${Manga.TABLE}.*
FROM ${Manga.TABLE}
JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
GROUP BY ${Manga.TABLE}.${Manga.COL_ID}
ORDER by COUNT(*)
"""
/**
* Query to get the categories for a manga.
*/
fun getCategoriesForMangaQuery() =
"""
SELECT ${Category.TABLE}.* FROM ${Category.TABLE}
JOIN ${MangaCategory.TABLE} ON ${Category.TABLE}.${Category.COL_ID} =
${MangaCategory.TABLE}.${MangaCategory.COL_CATEGORY_ID}
WHERE ${MangaCategory.COL_MANGA_ID} = ?
"""
/** Query to get the list of sources in the database that have
* non-library manga, and how many
*/
fun getSourceIdsWithNonLibraryMangaQuery() =
"""
SELECT ${Manga.COL_SOURCE}, COUNT(*) as ${SourceIdMangaCountGetResolver.COL_COUNT}
FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 0
GROUP BY ${Manga.COL_SOURCE}
"""
/**
* Query to get manga that are not in library, but have read chapters
*/
fun getReadMangaNotInLibraryQuery() =
"""
SELECT ${Manga.TABLE}.*
FROM ${Manga.TABLE}
WHERE ${Manga.COL_FAVORITE} = 0 AND ${Manga.COL_ID} IN(
SELECT ${Chapter.TABLE}.${Chapter.COL_MANGA_ID} FROM ${Chapter.TABLE} WHERE ${Chapter.COL_READ} = 1 OR ${Chapter.COL_LAST_PAGE_READ} != 0
)
"""

View file

@ -1,10 +1,7 @@
package eu.kanade.tachiyomi.data.database.queries
/* Unused, seems to be a relic of the past?
FIXME: Delete `search_metadata` from sqldelight migration
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.SearchMetadata
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable
package eu.kanade.tachiyomi.data.database.queries
interface SearchMetadataQueries : DbProvider {
@ -50,3 +47,4 @@ interface SearchMetadataQueries : DbProvider {
)
.prepare()
}
*/

View file

@ -1,39 +0,0 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
import com.pushtorefresh.storio.sqlite.queries.Query
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.tables.TrackTable
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.domain.manga.models.Manga
interface TrackQueries : DbProvider {
fun getTracks(manga: Manga) = getTracks(manga.id)
fun getTracks(mangaId: Long?) = db.get()
.listOfObjects(Track::class.java)
.withQuery(
Query.builder()
.table(TrackTable.TABLE)
.where("${TrackTable.COL_MANGA_ID} = ?")
.whereArgs(mangaId)
.build(),
)
.prepare()
fun insertTrack(track: Track) = db.put().`object`(track).prepare()
fun insertTracks(tracks: List<Track>) = db.put().objects(tracks).prepare()
fun deleteTrackForManga(manga: Manga, sync: TrackService) = db.delete()
.byQuery(
DeleteQuery.builder()
.table(TrackTable.TABLE)
.where("${TrackTable.COL_MANGA_ID} = ? AND ${TrackTable.COL_SYNC_ID} = ?")
.whereArgs(manga.id, sync.id)
.build(),
)
.prepare()
}

View file

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
class ChapterBackupPutResolver : PutResolver<Chapter>() {
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(chapter)
val contentValues = mapToContentValues(chapter)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ?")
.whereArgs(chapter.url)
.build()
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
put(ChapterTable.COL_READ, chapter.read)
put(ChapterTable.COL_BOOKMARK, chapter.bookmark)
put(ChapterTable.COL_LAST_PAGE_READ, chapter.last_page_read)
}
}

View file

@ -1,34 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
class ChapterKnownBackupPutResolver : PutResolver<Chapter>() {
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(chapter)
val contentValues = mapToContentValues(chapter)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(chapter.id)
.build()
fun mapToContentValues(chapter: Chapter) =
contentValuesOf(
ChapterTable.COL_READ to chapter.read,
ChapterTable.COL_BOOKMARK to chapter.bookmark,
ChapterTable.COL_LAST_PAGE_READ to chapter.last_page_read,
)
}

View file

@ -1,34 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
class ChapterProgressPutResolver : PutResolver<Chapter>() {
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(chapter)
val contentValues = mapToContentValues(chapter)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(chapter.id)
.build()
fun mapToContentValues(chapter: Chapter) = ContentValues(3).apply {
put(ChapterTable.COL_READ, chapter.read)
put(ChapterTable.COL_BOOKMARK, chapter.bookmark)
put(ChapterTable.COL_LAST_PAGE_READ, chapter.last_page_read)
put(ChapterTable.COL_PAGES_LEFT, chapter.pages_left)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
class ChapterSourceOrderPutResolver : PutResolver<Chapter>() {
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(chapter)
val contentValues = mapToContentValues(chapter)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?")
.whereArgs(chapter.url, chapter.manga_id)
.build()
fun mapToContentValues(chapter: Chapter) = ContentValues(1).apply {
put(ChapterTable.COL_SOURCE_ORDER, chapter.source_order)
}
}

View file

@ -1,51 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.mappers.HistoryPutResolver
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
class HistoryUpsertResolver : HistoryPutResolver() {
/**
* Updates last_read time of chapter
*/
override fun performPut(db: StorIOSQLite, history: History): PutResult {
val updateQuery = mapToUpdateQuery(history)
val cursor = db.lowLevel().query(
Query.builder()
.table(updateQuery.table())
.where(updateQuery.where())
.whereArgs(updateQuery.whereArgs())
.build(),
)
return cursor.use { putCursor ->
if (putCursor.count == 0) {
val insertQuery = mapToInsertQuery(history)
val insertedId = db.lowLevel().insert(insertQuery, mapToContentValues(history))
PutResult.newInsertResult(insertedId, insertQuery.table())
} else {
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, mapToUpdateContentValues(history))
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
}
}
override fun mapToUpdateQuery(obj: History) = UpdateQuery.builder()
.table(HistoryTable.TABLE)
.where("${HistoryTable.COL_CHAPTER_ID} = ?")
.whereArgs(obj.chapter_id)
.build()
private fun mapToUpdateContentValues(history: History) =
contentValuesOf(
HistoryTable.COL_LAST_READ to history.last_read,
HistoryTable.COL_TIME_READ to history.time_read,
)
}

View file

@ -1,27 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import eu.kanade.tachiyomi.data.database.mappers.ChapterGetResolver
import eu.kanade.tachiyomi.data.database.mappers.MangaGetResolver
import eu.kanade.tachiyomi.data.database.models.MangaChapter
class MangaChapterGetResolver : DefaultGetResolver<MangaChapter>() {
companion object {
val INSTANCE = MangaChapterGetResolver()
}
private val mangaGetResolver = MangaGetResolver()
private val chapterGetResolver = ChapterGetResolver()
override fun mapFromCursor(cursor: Cursor): MangaChapter {
val manga = mangaGetResolver.mapFromCursor(cursor)
val chapter = chapterGetResolver.mapFromCursor(cursor)
manga.id = chapter.manga_id
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
return MangaChapter(manga, chapter)
}
}

View file

@ -1,73 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import eu.kanade.tachiyomi.data.database.mappers.ChapterGetResolver
import eu.kanade.tachiyomi.data.database.mappers.HistoryGetResolver
import eu.kanade.tachiyomi.data.database.mappers.MangaGetResolver
import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.data.database.models.HistoryImpl
import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
import eu.kanade.tachiyomi.data.database.tables.HistoryTable
class MangaChapterHistoryGetResolver : DefaultGetResolver<MangaChapterHistory>() {
companion object {
val INSTANCE = MangaChapterHistoryGetResolver()
}
/**
* Manga get resolver
*/
private val mangaGetResolver = MangaGetResolver()
/**
* Chapter get resolver
*/
private val chapterResolver = ChapterGetResolver()
/**
* History get resolver
*/
private val historyGetResolver = HistoryGetResolver()
/**
* Map correct objects from cursor result
*/
override fun mapFromCursor(cursor: Cursor): MangaChapterHistory {
// Get manga object
val manga = mangaGetResolver.mapFromCursor(cursor)
// Get chapter object
val chapter =
if (!cursor.isNull(cursor.getColumnIndex(ChapterTable.COL_MANGA_ID))) {
chapterResolver.mapFromCursor(cursor)
} else {
ChapterImpl()
}
// Get history object
val history =
if (!cursor.isNull(cursor.getColumnIndex(HistoryTable.COL_ID))) {
historyGetResolver.mapFromCursor(cursor)
} else {
HistoryImpl().apply {
last_read = try {
cursor.getLong(cursor.getColumnIndex(HistoryTable.COL_LAST_READ))
} catch (e: Exception) {
0L
}
}
}
// Make certain column conflicts are dealt with
if (chapter.id != null) {
manga.id = chapter.manga_id
manga.url = cursor.getString(cursor.getColumnIndex("mangaUrl"))
}
if (history.id != null) chapter.id = history.chapter_id
// Return result
return MangaChapterHistory(manga, chapter, history)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaDateAddedPutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_DATE_ADDED, manga.date_added)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaFavoritePutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_FAVORITE, manga.favorite)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaFilteredScanlatorsPutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = contentValuesOf(
MangaTable.COL_FILTERED_SCANLATORS to manga.filtered_scanlators,
)
}

View file

@ -1,43 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
import kotlin.reflect.KProperty1
class MangaFlagsPutResolver(private val colName: String, private val fieldGetter: KProperty1<Manga, Int>, private val updateAll: Boolean = false) : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga): UpdateQuery {
val builder = UpdateQuery.builder()
return if (updateAll) {
builder
.table(MangaTable.TABLE)
.build()
} else {
builder
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
}
}
fun mapToContentValues(manga: Manga) =
contentValuesOf(
colName to fieldGetter.get(manga),
)
}

View file

@ -1,36 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaInfoPutResolver() : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_TITLE, manga.originalTitle)
put(MangaTable.COL_GENRE, manga.originalGenre)
put(MangaTable.COL_AUTHOR, manga.originalAuthor)
put(MangaTable.COL_ARTIST, manga.originalArtist)
put(MangaTable.COL_DESCRIPTION, manga.originalDescription)
put(MangaTable.COL_STATUS, manga.originalStatus)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaLastUpdatedPutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_LAST_UPDATE, manga.last_update)
}
}

View file

@ -1,31 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
class MangaTitlePutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_TITLE, manga.title)
}
}

View file

@ -1,23 +0,0 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.annotation.SuppressLint
import android.database.Cursor
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
import eu.kanade.tachiyomi.data.database.models.SourceIdMangaCount
import eu.kanade.tachiyomi.data.database.tables.MangaTable
class SourceIdMangaCountGetResolver : DefaultGetResolver<SourceIdMangaCount>() {
companion object {
val INSTANCE = SourceIdMangaCountGetResolver()
const val COL_COUNT = "manga_count"
}
@SuppressLint("Range")
override fun mapFromCursor(cursor: Cursor): SourceIdMangaCount {
val sourceID = cursor.getLong(cursor.getColumnIndex(MangaTable.COL_SOURCE))
val count = cursor.getInt(cursor.getColumnIndex(COL_COUNT))
return SourceIdMangaCount(sourceID, count)
}
}

View file

@ -1,17 +0,0 @@
package eu.kanade.tachiyomi.data.database.tables
object CategoryTable {
const val TABLE = "categories"
const val COL_ID = "_id"
const val COL_NAME = "name"
const val COL_ORDER = "sort"
const val COL_FLAGS = "flags"
const val COL_MANGA_ORDER = "manga_order"
}

View file

@ -1,33 +0,0 @@
package eu.kanade.tachiyomi.data.database.tables
object ChapterTable {
const val TABLE = "chapters"
const val COL_ID = "_id"
const val COL_MANGA_ID = "manga_id"
const val COL_URL = "url"
const val COL_NAME = "name"
const val COL_READ = "read"
const val COL_SCANLATOR = "scanlator"
const val COL_BOOKMARK = "bookmark"
const val COL_DATE_FETCH = "date_fetch"
const val COL_DATE_UPLOAD = "date_upload"
const val COL_LAST_PAGE_READ = "last_page_read"
const val COL_PAGES_LEFT = "pages_left"
const val COL_CHAPTER_NUMBER = "chapter_number"
const val COL_SOURCE_ORDER = "source_order"
}

View file

@ -1,30 +0,0 @@
package eu.kanade.tachiyomi.data.database.tables
object HistoryTable {
/**
* Table name
*/
const val TABLE = "history"
/**
* Id column name
*/
const val COL_ID = "history_id"
/**
* Chapter id column name
*/
const val COL_CHAPTER_ID = "history_chapter_id"
/**
* Last read column name
*/
const val COL_LAST_READ = "history_last_read"
/**
* Time read column name
*/
const val COL_TIME_READ = "history_time_read"
}

View file

@ -1,13 +0,0 @@
package eu.kanade.tachiyomi.data.database.tables
object MangaCategoryTable {
const val TABLE = "mangas_categories"
const val COL_ID = "_id"
const val COL_MANGA_ID = "manga_id"
const val COL_CATEGORY_ID = "category_id"
}

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