mirror of
https://github.com/mihonapp/mihon.git
synced 2025-07-28 10:25:53 +02:00
Compare commits
529 Commits
Author | SHA1 | Date | |
---|---|---|---|
38d5fc9160 | |||
9454fe4482 | |||
6de06419f8 | |||
fc2f339ea1 | |||
264030d6ec | |||
140083ee39 | |||
2bf7ef5d18 | |||
df9fff60da | |||
aae0e3459c | |||
f7752a98b2 | |||
2ba7ed3280 | |||
c153ac01f5 | |||
47b0e9d7be | |||
d4bf19f957 | |||
01b44c0458 | |||
e1e3ca7a56 | |||
78d2cc75d5 | |||
c550a81598 | |||
0be36a10c3 | |||
e16c3953c7 | |||
f3a2f566c8 | |||
15e3f28aa3 | |||
3bf70b230f | |||
eb3bea8150 | |||
5612ae0149 | |||
dbf6ad2ca7 | |||
d2afbfe4ed | |||
337806d9e1 | |||
443f6e0ae5 | |||
572ee2f02a | |||
ba1343bed8 | |||
9f3d5d13d4 | |||
48166b9b52 | |||
2e2c8d36c1 | |||
788235feec | |||
afa5002988 | |||
9503082d44 | |||
de36357da8 | |||
eb6092bd0c | |||
32d2c2ac1b | |||
4051f180a2 | |||
3ed8a91c7b | |||
87db3f90de | |||
0a4ad89b99 | |||
a72db41bf1 | |||
6b2bba4e54 | |||
7c7af72f8c | |||
c8bb78d91a | |||
2ba3f0612c | |||
f84d9a08b4 | |||
37419cdc26 | |||
481cfedf08 | |||
9b8ab6acc2 | |||
3bddb55385 | |||
2beb89d531 | |||
016f627fb0 | |||
44aab7a243 | |||
a2dc88965b | |||
aa998071a1 | |||
8113b77f1e | |||
6adfa4fd0f | |||
76e0aba70c | |||
f7fbc93833 | |||
85ee9c6686 | |||
c72c07f355 | |||
6984e0465b | |||
3ca989eae8 | |||
cca33481dd | |||
f7c8f1801e | |||
112b68b782 | |||
2dd02b73d6 | |||
369df527b2 | |||
d04eeface9 | |||
dde942df4e | |||
380787a310 | |||
418ba30265 | |||
b3867dd63c | |||
6dd93d70cc | |||
2276abbb23 | |||
be671b42ce | |||
0042cb6582 | |||
1e570bc965 | |||
9cc7d42dd9 | |||
f5c6d2e1a6 | |||
339dc33f58 | |||
223af5508f | |||
d42f776c5c | |||
5c0dc3e05a | |||
bebf80dfae | |||
86dd809f4d | |||
1ff88dd927 | |||
be5d467955 | |||
fcb01b5bcf | |||
844dae1a4d | |||
83fd4746ed | |||
c8ad6cdf31 | |||
fbcc48fefc | |||
6f422745ba | |||
bec549cc44 | |||
c4f235ae07 | |||
8fd1239bea | |||
b56a97bb8e | |||
52036e5664 | |||
29a74509a4 | |||
0e956cbb51 | |||
2baffa62ca | |||
bd7b354198 | |||
70c1a842b2 | |||
001249a89d | |||
c4d2fffb12 | |||
f22767d863 | |||
02af9b1acf | |||
3c611b95fb | |||
fc1c804bfd | |||
abfb72c89c | |||
9c1905ede7 | |||
9f99f038f3 | |||
6c6ea84509 | |||
4ee31bfea5 | |||
03eb756ecb | |||
a45eb5e528 | |||
8f9a325895 | |||
f74071ab0a | |||
7fb3ef48e4 | |||
1837faa573 | |||
518abf032c | |||
7ca64a67c5 | |||
d26c010e57 | |||
607e56a4ec | |||
952a98c180 | |||
45628b14db | |||
5dc6569a68 | |||
fba9bacdc1 | |||
ca968f162e | |||
379d587826 | |||
034ec4cb12 | |||
2481767532 | |||
ab2b734d49 | |||
aac4d6e548 | |||
08ae51ea8c | |||
4387ae5ff3 | |||
d6252ab770 | |||
5ae8095ef1 | |||
ac41bffdc9 | |||
777ae2461e | |||
b2f1719c50 | |||
3f050a83dd | |||
6f4e3f776f | |||
1c47a6b9b3 | |||
f4348df870 | |||
9a34ace09c | |||
124a787cda | |||
b404a71e26 | |||
be124ebe86 | |||
fdb96179c6 | |||
c5994e057b | |||
3f1d28c383 | |||
84b2164787 | |||
200d39e023 | |||
b1b15a93ee | |||
97c81fadb4 | |||
9240eceedc | |||
14ae57d78b | |||
4828c54245 | |||
dca9bf1057 | |||
e8b7c3e24b | |||
af77083660 | |||
36b9caeea8 | |||
fdc1423f3d | |||
8e40146f96 | |||
1c16fc79c2 | |||
31263084ec | |||
2858ef835f | |||
edb8201f74 | |||
854474f85f | |||
04db46fe75 | |||
3f6bd5f010 | |||
a3dfd2efe6 | |||
de8ef6dad7 | |||
8160b47ff5 | |||
c201b341a7 | |||
56fb4f62a1 | |||
0af90999c8 | |||
913ff22132 | |||
04aa5b36a5 | |||
41e2dc7ae8 | |||
88efde8796 | |||
b7849d7146 | |||
602b58f364 | |||
e48dbdbf23 | |||
6ace423e18 | |||
51b68cd25f | |||
ca784cbe32 | |||
4f61b2e4e8 | |||
8c9d12a840 | |||
f63e950910 | |||
f3f2bd41c3 | |||
14d687c5cd | |||
e94c8dac94 | |||
7a2ca4bf4d | |||
4a7613d515 | |||
e65634cb42 | |||
daa47e0493 | |||
cbcd8bd668 | |||
2092c81bad | |||
5a61ca5535 | |||
ddba71df37 | |||
75b5d96601 | |||
77db8873f6 | |||
bff6183cf3 | |||
e620665dda | |||
c0f9de88e7 | |||
80cdebcdf4 | |||
9e2f97eeb8 | |||
e132cc405f | |||
2674b84974 | |||
f34702d4fc | |||
7823966ddf | |||
2d41bf5589 | |||
d8fe7d32ca | |||
2f86f25d5b | |||
239c38982c | |||
36e40c0997 | |||
40754659a9 | |||
a41ea8a61d | |||
5c249dd790 | |||
e17f70f722 | |||
e57638a49c | |||
0ce1cf22cd | |||
4ed2062cab | |||
f6ec53cdde | |||
b37357f909 | |||
f58a05e918 | |||
cf02119da5 | |||
5e2a3ee927 | |||
3b8ed3059a | |||
4182ae89a0 | |||
f3226fb278 | |||
30a6e3a6a1 | |||
2e78bceb30 | |||
a5838387b1 | |||
aa1714b2ac | |||
f696f209c6 | |||
9fa22f0b37 | |||
6d8cfd5f30 | |||
af57e124f2 | |||
8e8ee69bba | |||
e9d69a83fe | |||
6a80305d6c | |||
119bcbf8ed | |||
87fe64468c | |||
bdce3c39f1 | |||
15d999229f | |||
1edd55c981 | |||
777a071f4a | |||
71b558cb34 | |||
46003ec251 | |||
e8fdfaad64 | |||
1f7574bd4f | |||
da62c7a21a | |||
0870cffba1 | |||
8632ba85ee | |||
116579d38c | |||
098f925519 | |||
9f5db70572 | |||
1f286f1a35 | |||
7ab7f5ac37 | |||
e567250b17 | |||
095da924b9 | |||
b9da98b527 | |||
af8696cb90 | |||
de5a64aa73 | |||
9b944092c7 | |||
0cb1794a44 | |||
2f243fae11 | |||
d2e5c78074 | |||
5912d6b08f | |||
99b550ae0d | |||
8d187f7865 | |||
653d5d3e25 | |||
1dca9363a4 | |||
fffa6a462d | |||
ce497003e3 | |||
84ea5166de | |||
0392ef0d18 | |||
16392adcbb | |||
f603db3f3f | |||
ab546e0884 | |||
d3306e8cfe | |||
aebb86794a | |||
9a62e4fba3 | |||
5955c9c311 | |||
fb9423028e | |||
8e9396a9cf | |||
1df87eabf2 | |||
ca7391bbf3 | |||
550f1197e8 | |||
dbcc4a7d71 | |||
e473c7f09f | |||
263e467cde | |||
7ec2108812 | |||
e55e5f6f64 | |||
28dca3b7b8 | |||
70cd688ac2 | |||
21145144cd | |||
134e4648a9 | |||
fa6dba6cc7 | |||
8a51d56c59 | |||
47ee2b45a8 | |||
a2f7d47a0a | |||
4e5cbbc96b | |||
b720f34267 | |||
c6a1412f18 | |||
6290cf222d | |||
a3d438e2f5 | |||
80461d883f | |||
c3e7bb12f4 | |||
2ad98520aa | |||
f1ce205d00 | |||
5289830a84 | |||
dd932e1362 | |||
e85ce6456a | |||
9a3ffe2ea6 | |||
213effa169 | |||
25570147a1 | |||
e82a2f5f9f | |||
935c0c7e2e | |||
2ad462b4d8 | |||
7fd8f65352 | |||
b152e3881b | |||
f27ca3b1b2 | |||
843daa5304 | |||
f080a4937e | |||
8bba926891 | |||
5378c4c5d6 | |||
c94d212ef4 | |||
4c43a0ef66 | |||
ea0fe2414e | |||
015620711d | |||
6d4267b3bb | |||
2a01a2ac6b | |||
9a6559b013 | |||
a7509b3a3c | |||
2755d1f35e | |||
6a8a9c6bbf | |||
7862088b94 | |||
35f8eda8c5 | |||
fa6fa1f53a | |||
c348fac78f | |||
ab06720966 | |||
42ebf017e4 | |||
3910ffdd9e | |||
0bfacf5570 | |||
1e28999e13 | |||
1ee54d74a4 | |||
a1a52ae81a | |||
56e66e041d | |||
13656959ae | |||
20e4cb26d6 | |||
e448e40406 | |||
aed53d3bdc | |||
d77f2f429d | |||
c3fd2df6f5 | |||
f5a41e9693 | |||
34bf5c6f87 | |||
6abaa47f5b | |||
c9fddf9e38 | |||
555d2f834f | |||
6b3423a12b | |||
7c6fd026a3 | |||
86fbd20665 | |||
8c8d65d3c7 | |||
f1660beafc | |||
72222ad86d | |||
0265c16eb2 | |||
666d6aa117 | |||
6965e59a64 | |||
e020ae5ed5 | |||
05071b4205 | |||
da20d00481 | |||
8c437ceecf | |||
9672ea8b1b | |||
ba9cfd867c | |||
4b4e468510 | |||
e75488f5d9 | |||
3838dbcf08 | |||
b3ca097e5a | |||
70c2443e82 | |||
c0a888807b | |||
34930920a5 | |||
6682b5dd39 | |||
3c5f4a317a | |||
6a2bfd5e87 | |||
ef6cad58fe | |||
7e9340aa7f | |||
3f2c8e9ef6 | |||
a29870c01e | |||
583aa430ba | |||
0ea0138a73 | |||
59bedb33ff | |||
ebee275110 | |||
015d9b3bd0 | |||
f2ccfb0817 | |||
1b60c5f0f4 | |||
0a91b57f67 | |||
bcdf17fe27 | |||
a08e03f5cb | |||
f087135876 | |||
f66f52c244 | |||
0d6f426dbd | |||
edd7d0522c | |||
4ae9dbe524 | |||
402e579a69 | |||
d0e64d3a66 | |||
154f4d327c | |||
d8b9a9f593 | |||
b7e091d5d0 | |||
31e052ac15 | |||
60480686da | |||
d6ba3c8249 | |||
c56f4665ef | |||
b51a0a38bd | |||
f72b6e4d7c | |||
84984ef7e1 | |||
9f48def1e2 | |||
e83bfb0d35 | |||
0301362430 | |||
9d5978aca0 | |||
4bfc5e7b51 | |||
5859a8bbf6 | |||
802a2c5c1e | |||
c1c1746985 | |||
4fcbd80a8e | |||
16969193c7 | |||
55637ddfe1 | |||
e50358dc4b | |||
1e40199b7d | |||
410b918b77 | |||
a4f5dfab1a | |||
085147b15b | |||
085ad8d446 | |||
9254079957 | |||
7974a1fc0c | |||
1521c35941 | |||
2247f6004a | |||
c15f3f2fd5 | |||
21020e1797 | |||
7edecae57f | |||
07f963d5ae | |||
ab02568ac6 | |||
617bf491ee | |||
840b647b4b | |||
1b0bbb8440 | |||
95d4df9ca8 | |||
fb86c470f6 | |||
5aec8f8018 | |||
e183cbb231 | |||
6bdb37be65 | |||
7ff95e21ba | |||
96c236e5c3 | |||
72f3756a3b | |||
0780385d2e | |||
31e9273b1f | |||
088e37b2d8 | |||
5b88f1bd94 | |||
18beb20aac | |||
d5d7065e75 | |||
78271a54a4 | |||
9bff20cb1a | |||
2ccff8cdde | |||
0da7ad6f1a | |||
170daf9fb2 | |||
139663acfc | |||
1581b876cf | |||
0f4de03d7a | |||
ddbe8efbc5 | |||
63146e717b | |||
b2d22f86c6 | |||
79f46b25f6 | |||
aa498360db | |||
f03f998b21 | |||
4811cf07cd | |||
b71c793fad | |||
47e5421527 | |||
5dc6501688 | |||
cc09230e26 | |||
9c4d2b087f | |||
4bcba0503a | |||
cf33f250cc | |||
915a967151 | |||
9cc0c4e035 | |||
222e111806 | |||
8489b0dd8b | |||
88ed634978 | |||
32188f9f65 | |||
05efc4ebeb | |||
65bfa083f2 | |||
b8a9998bbd | |||
d736bec003 | |||
348b23a9fd | |||
121b2ec829 | |||
1dd130df9e | |||
e17d87f357 | |||
de75561402 | |||
58085336a5 | |||
89ea0a271b | |||
e3f33e24f5 | |||
9fd1419142 | |||
cb06898430 | |||
39407407f2 | |||
a024218410 | |||
26815c7356 | |||
e0deeb8008 | |||
38d6ab80ce | |||
78e66fd8d3 | |||
26aa126ecb | |||
e4a65656e7 | |||
6018aa99e2 | |||
99fd2731f5 | |||
e34043f1fe | |||
3c3a1cd448 | |||
0a2df21c5b | |||
277be02682 | |||
1849715418 | |||
dc6d4f9917 | |||
a9d98e5048 | |||
653940613d | |||
23a2d816e4 | |||
8a3a9146db |
.github
.gitignore.idea
CHANGELOG.mdCONTRIBUTING.mdREADME.mdapp
.gitignorebuild.gradle.ktsproguard-rules.pro
build.gradle.ktssrc
debug
dev
java
mihon
core
firebase
main
AndroidManifest.xmlic_launcher-web.png
java
eu
kanade
core
domain
DomainModule.kt
base
chapter
download
interactor
extension
interactor
manga
model
source
interactor
service
track
interactor
model
service
store
ui
presentation
browse
BrowseSourceScreen.ktExtensionDetailsScreen.ktExtensionFilterScreen.ktExtensionsScreen.ktGlobalSearchScreen.ktMigrateSourceScreen.ktSourcesFilterScreen.ktSourcesScreen.kt
components
category
components
crash
history
library
manga
more
MoreScreen.ktNewUpdateScreen.kt
onboarding
settings
Preference.ktPreferenceItem.kt
screen
SettingsAdvancedScreen.ktSettingsAppearanceScreen.ktSettingsBrowseScreen.ktSettingsDataScreen.ktSettingsDownloadScreen.ktSettingsLibraryScreen.ktSettingsReaderScreen.ktSettingsSearchScreen.ktSettingsSecurityScreen.ktSettingsTrackingScreen.kt
about
advanced
appearance
browse
data
debug
widget
stats
components
reader
theme
TachiyomiTheme.kt
colorscheme
track
TrackInfoDialogHome.ktTrackInfoDialogHomePreviewProvider.ktTrackInfoDialogSelector.ktTrackerSearch.ktTrackerSearchPreviewProvider.kt
components
updates
util
webview
tachiyomi
App.ktAppInfo.ktMigrations.kt
crash
data
backup
BackupDecoder.ktBackupNotifier.kt
create
models
restore
cache
coil
database
download
library
notification
saver
track
BaseTracker.ktTracker.ktTrackerManager.kt
anilist
bangumi
kavita
kitsu
komga
mangaupdates
model
myanimelist
shikimori
suwayomi
updater
di
extension
source
ui
base
browse
extension
ExtensionFilterScreen.ktExtensionFilterScreenModel.ktExtensionsScreenModel.ktExtensionsTab.kt
details
migration
manga
search
sources
source
deeplink
download
history
home
library
main
manga
more
reader
ReaderActivity.ktReaderNavigationOverlayView.ktReaderViewModel.ktSaveImageNotifier.kt
loader
ArchivePageLoader.ktChapterLoader.ktDirectoryPageLoader.ktDownloadPageLoader.ktEpubPageLoader.ktHttpPageLoader.ktRarPageLoader.kt
model
setting
viewer
security
setting
stats
updates
webview
util
test
mihon
core
designsystem
utils
migration
feature
res
standard
test
java
mihon
core
migration
buildSrc
.gitignorebuild.gradle.ktssettings.gradle.kts
src
core-metadata
core
.gitignore
archive
common
build.gradle.kts
src
main
AndroidManifest.xml
kotlin
eu
kanade
tachiyomi
src
main
java
data
.gitignorebuild.gradle.kts
src
main
domain
.gitignorebuild.gradle.kts
gradle.propertiessrc
main
AndroidManifest.xml
java
mihon
domain
chapter
interactor
extensionrepo
exception
interactor
CreateExtensionRepo.ktDeleteExtensionRepo.ktGetExtensionRepo.ktGetExtensionRepoCount.ktReplaceExtensionRepo.ktUpdateExtensionRepo.kt
model
repository
service
upcoming
interactor
tachiyomi
domain
backup
service
category
interactor
chapter
interactor
GetChapter.ktGetChaptersByMangaId.ktSetMangaDefaultChapterFlags.ktShouldUpdateDbChapter.ktUpdateChapter.kt
model
service
download
service
library
manga
interactor
model
repository
release
interactor
source
storage
service
track
test
java
tachiyomi
domain
chapter
service
library
model
release
interactor
gradle
gradlewgradlew.bati18n
.gitignoreREADME.mdbuild.gradle.kts
src
androidMain
commonMain
moko-resources
am
ar
base
be
bg
bn
ca
ceb
cs
cv
da
de
el
eo
es
eu
fa
fi
fil
fr
gl
he
hi
hr
hu
in
it
ja
jv
ka-rGE
kk
km
kn
ko
lt
lv
ml
mr
ms
nb-rNO
ne
nl
nn
pl
pt-rBR
pt
ro
ru
sa
sah
sc
sdh
sk
sq
sr
sv
te
th
tr
uk
uz
vi
zh-rCN
zh-rTW
resources
MR
macrobenchmark
presentation-core
.gitignorebuild.gradle.kts
src
main
AndroidManifest.xml
java
tachiyomi
presentation
core
components
i18n
icons
util
res
presentation-widget
settings.gradle.ktssource-api
source-local
.gitignorebuild.gradle.kts
src
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
|||||||
ko_fi: inorichi
|
|
34
.github/ISSUE_TEMPLATE.md
vendored
34
.github/ISSUE_TEMPLATE.md
vendored
@ -1,34 +0,0 @@
|
|||||||
**PLEASE READ THIS**
|
|
||||||
|
|
||||||
I acknowledge that:
|
|
||||||
|
|
||||||
- I have updated:
|
|
||||||
- To the latest version of the app (stable is v0.15.3)
|
|
||||||
- All extensions
|
|
||||||
- I have gone through the FAQ (https://mihon.app/docs/faq/general) and troubleshooting guide (https://mihon.app/docs/guides/troubleshooting/)
|
|
||||||
- If this is an issue with an official extension, that I should be opening an issue in https://github.com/tachiyomiorg/extensions
|
|
||||||
- I have searched the existing issues and this is new ticket **NOT** a duplicate or related to another open or closed issue
|
|
||||||
- I will fill out the title and the information in this template
|
|
||||||
|
|
||||||
Note that the issue will be automatically closed if you do not fill out the title or requested information.
|
|
||||||
|
|
||||||
**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Device information
|
|
||||||
* Tachiyomi version: ?
|
|
||||||
* Android version: ?
|
|
||||||
* Device: ?
|
|
||||||
|
|
||||||
## Steps to reproduce
|
|
||||||
1. First step
|
|
||||||
2. Second step
|
|
||||||
|
|
||||||
## Issue/Request
|
|
||||||
?
|
|
||||||
|
|
||||||
## Other details
|
|
||||||
Additional details and attachments.
|
|
||||||
|
|
||||||
If you're experiencing crashes, share the crash logs from More → Settings → Advanced → Dump crash logs.
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: ⚠️ Extension/source issue
|
- name: 🖥️ Mihon website
|
||||||
url: https://github.com/tachiyomiorg/extensions/issues/new/choose
|
|
||||||
about: Issues and requests for official extensions and sources should be opened in the extensions repository instead
|
|
||||||
- name: 📦 Tachiyomi extensions
|
|
||||||
url: https://mihon.app/extensions/
|
|
||||||
about: List of all available extensions with download links
|
|
||||||
- name: 🖥️ Tachiyomi website
|
|
||||||
url: https://mihon.app/
|
url: https://mihon.app/
|
||||||
about: Guides, troubleshooting, and answers to common questions
|
about: Guides, troubleshooting, and answers to common questions
|
||||||
|
14
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
14
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
name: 🐞 Issue report
|
name: 🐞 Issue report
|
||||||
description: Report an issue in Tachiyomi
|
description: Report an issue in Mihon
|
||||||
labels: [Bug]
|
labels: [Bug]
|
||||||
body:
|
body:
|
||||||
|
|
||||||
@ -48,12 +48,12 @@ body:
|
|||||||
You can paste the crash logs in plain text or upload it as an attachment.
|
You can paste the crash logs in plain text or upload it as an attachment.
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: tachiyomi-version
|
id: mihon-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Tachiyomi version
|
label: Mihon version
|
||||||
description: You can find your Tachiyomi version in **More → About**.
|
description: You can find your Mihon version in **More → About**.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example: "0.15.3"
|
Example: "0.17.0"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -94,11 +94,9 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have written a short but informative title.
|
- label: I have written a short but informative title.
|
||||||
required: true
|
required: true
|
||||||
- label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose).
|
|
||||||
required: true
|
|
||||||
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
- label: I have updated the app to version **[0.17.0](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated all installed extensions.
|
- label: I have updated all installed extensions.
|
||||||
required: true
|
required: true
|
||||||
|
8
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
8
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
name: ⭐ Feature request
|
name: ⭐ Feature request
|
||||||
description: Suggest a feature to improve Tachiyomi
|
description: Suggest a feature to improve Mihon
|
||||||
labels: [Feature request]
|
labels: [Feature request]
|
||||||
body:
|
body:
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ body:
|
|||||||
id: feature-description
|
id: feature-description
|
||||||
attributes:
|
attributes:
|
||||||
label: Describe your suggested feature
|
label: Describe your suggested feature
|
||||||
description: How can Tachiyomi be improved?
|
description: How can Mihon be improved?
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example:
|
Example:
|
||||||
"It should work like this..."
|
"It should work like this..."
|
||||||
@ -31,9 +31,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have written a short but informative title.
|
- label: I have written a short but informative title.
|
||||||
required: true
|
required: true
|
||||||
- label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose).
|
- label: I have updated the app to version **[0.17.0](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||||
required: true
|
|
||||||
- label: I have updated the app to version **[0.15.3](https://github.com/mihonapp/mihon/releases/latest)**.
|
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
|
BIN
.github/assets/logo.png
vendored
Normal file
BIN
.github/assets/logo.png
vendored
Normal file
Binary file not shown.
After ![]() (image error) Size: 7.5 KiB |
10
.github/mergify.yml
vendored
10
.github/mergify.yml
vendored
@ -1,10 +0,0 @@
|
|||||||
#pull_request_rules:
|
|
||||||
# - name: Automatically merge translations
|
|
||||||
# conditions:
|
|
||||||
# - "author = weblate"
|
|
||||||
# - "-conflict"
|
|
||||||
# - "current-day-of-week = Sat"
|
|
||||||
# - "created-at < 1 day ago"
|
|
||||||
# actions:
|
|
||||||
# merge:
|
|
||||||
# method: squash
|
|
BIN
.github/readme-images/app-icon.png
vendored
BIN
.github/readme-images/app-icon.png
vendored
Binary file not shown.
Before ![]() (image error) Size: 1.1 KiB |
16
.github/renovate.json5
vendored
16
.github/renovate.json5
vendored
@ -1,17 +1,13 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["config:base"],
|
||||||
"config:base"
|
"labels": ["Dependencies"],
|
||||||
],
|
"semanticCommits": "disabled",
|
||||||
"schedule": ["every sunday"],
|
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
// Compiler plugins are tightly coupled to Kotlin version
|
"groupName": "GitHub Actions",
|
||||||
"groupName": "Kotlin",
|
"matchManagers": ["github-actions"],
|
||||||
"matchPackagePrefixes": [
|
"pinDigests": true,
|
||||||
"androidx.compose.compiler",
|
|
||||||
"org.jetbrains.kotlin",
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
36
.github/workflows/build_pull_request.yml
vendored
36
.github/workflows/build_pull_request.yml
vendored
@ -1,10 +1,13 @@
|
|||||||
name: PR build check
|
name: PR build check
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths:
|
||||||
- '**.md'
|
- '**'
|
||||||
- 'i18n/src/commonMain/resources/**/strings.xml'
|
- '!**.md'
|
||||||
- 'i18n/src/commonMain/resources/**/plurals.xml'
|
- '!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'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
@ -20,21 +23,34 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
|
|
||||||
- name: Dependency Review
|
- name: Dependency Review
|
||||||
uses: actions/dependency-review-action@v3
|
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
|
|
||||||
|
- name: Set up gradle
|
||||||
|
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
|
|
||||||
- name: Build app and run unit tests
|
- name: Build app and run unit tests
|
||||||
uses: gradle/gradle-command-action@v2
|
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
||||||
|
|
||||||
|
- name: Upload APK
|
||||||
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest
|
name: arm64-v8a-${{ github.sha }}
|
||||||
|
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
|
- name: Upload mapping
|
||||||
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
|
with:
|
||||||
|
name: mapping-${{ github.sha }}
|
||||||
|
path: app/build/outputs/mapping/standardRelease
|
||||||
|
29
.github/workflows/build_push.yml
vendored
29
.github/workflows/build_push.yml
vendored
@ -17,25 +17,38 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
|
|
||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
run: |
|
run: |
|
||||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4.5.0
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: adopt
|
||||||
|
|
||||||
|
- name: Set up gradle
|
||||||
|
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
|
||||||
|
|
||||||
- name: Build app and run unit tests
|
- name: Build app and run unit tests
|
||||||
uses: gradle/gradle-command-action@v2
|
run: ./gradlew spotlessCheck assembleStandardRelease testReleaseUnitTest testStandardReleaseUnitTest
|
||||||
|
|
||||||
|
- name: Upload APK
|
||||||
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest
|
name: arm64-v8a-${{ github.sha }}
|
||||||
|
path: app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
|
- name: Upload mapping
|
||||||
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
|
with:
|
||||||
|
name: mapping-${{ github.sha }}
|
||||||
|
path: app/build/outputs/mapping/standardRelease
|
||||||
|
|
||||||
# Sign APK and create release for tags
|
# Sign APK and create release for tags
|
||||||
|
|
||||||
@ -47,7 +60,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Sign APK
|
- name: Sign APK
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
uses: r0adkll/sign-android-release@v1
|
uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
|
||||||
with:
|
with:
|
||||||
releaseDirectory: app/build/outputs/apk/standard/release
|
releaseDirectory: app/build/outputs/apk/standard/release
|
||||||
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
||||||
@ -82,7 +95,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ env.VERSION_TAG }}
|
tag_name: ${{ env.VERSION_TAG }}
|
||||||
name: Mihon ${{ env.VERSION_TAG }}
|
name: Mihon ${{ env.VERSION_TAG }}
|
||||||
@ -99,7 +112,7 @@ jobs:
|
|||||||
| x86 | ${{ env.APK_X86_SHA }} |
|
| x86 | ${{ env.APK_X86_SHA }} |
|
||||||
| x86_64 | ${{ env.APK_X86_64_SHA }} |
|
| x86_64 | ${{ env.APK_X86_64_SHA }} |
|
||||||
|
|
||||||
If you are unsure which version to choose then go with mihon-${{ env.VERSION_TAG }}.apk
|
## If you are unsure which version to choose then go with mihon-${{ env.VERSION_TAG }}.apk
|
||||||
files: |
|
files: |
|
||||||
mihon-${{ env.VERSION_TAG }}.apk
|
mihon-${{ env.VERSION_TAG }}.apk
|
||||||
mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
||||||
|
45
.github/workflows/issue_moderator.yml
vendored
45
.github/workflows/issue_moderator.yml
vendored
@ -1,45 +0,0 @@
|
|||||||
name: Issue moderator
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened, edited, reopened]
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
moderate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Moderate issues
|
|
||||||
uses: tachiyomiorg/issue-moderator-action@v2
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
duplicate-label: Duplicate
|
|
||||||
|
|
||||||
auto-close-rules: |
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "body",
|
|
||||||
"regex": ".*DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT.*",
|
|
||||||
"message": "The acknowledgment section was not removed."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "body",
|
|
||||||
"regex": ".*\\* (Tachiyomi version|Android version|Device): \\?.*",
|
|
||||||
"message": "Requested information in the template was not filled out."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "both",
|
|
||||||
"regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$",
|
|
||||||
"ignoreCase": true,
|
|
||||||
"message": "Tachiyomi does not support anime, and has no plans to support anime. In addition Tachiyomi is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "both",
|
|
||||||
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cloud ?fl?are.*",
|
|
||||||
"ignoreCase": true,
|
|
||||||
"labels": ["Cloudflare protected"],
|
|
||||||
"message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
auto-close-ignore-label: do-not-autoclose
|
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v5
|
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
issue-inactive-days: '2'
|
issue-inactive-days: '2'
|
||||||
|
23
.gitignore
vendored
23
.gitignore
vendored
@ -1,17 +1,16 @@
|
|||||||
|
# Build files
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
.kotlin
|
||||||
/.idea/workspace.xml
|
build
|
||||||
.DS_Store
|
|
||||||
|
# IDE files
|
||||||
|
*.iml
|
||||||
.idea/*
|
.idea/*
|
||||||
!.idea/icon.png
|
!.idea/icon.png
|
||||||
*iml
|
/captures
|
||||||
*.iml
|
|
||||||
|
|
||||||
# Built files
|
# Configuration files
|
||||||
*/build
|
local.properties
|
||||||
/build
|
|
||||||
*.apk
|
|
||||||
app/**/output.json
|
|
||||||
|
|
||||||
# Unnecessary file
|
# macOS specific files
|
||||||
*.swp
|
.DS_Store
|
||||||
|
BIN
.idea/icon.png
generated
BIN
.idea/icon.png
generated
Binary file not shown.
Before ![]() (image error) Size: 14 KiB After ![]() (image error) Size: 62 KiB ![]() ![]() |
268
CHANGELOG.md
Normal file
268
CHANGELOG.md
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is a modified version of [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
- `Added` - for new features.
|
||||||
|
- `Changed ` - for changes in existing functionality.
|
||||||
|
- `Improved` - for enhancement or optimization in existing functionality.
|
||||||
|
- `Removed` - for now removed features.
|
||||||
|
- `Fixed` - for any bug fixes.
|
||||||
|
- `Other` - for technical stuff.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.17.0] - 2024-10-26
|
||||||
|
### Added
|
||||||
|
- Option to disable reader zoom out ([@Splintorien](https://github.com/Splintorien)) ([#302](https://github.com/mihonapp/mihon/pull/302))
|
||||||
|
- Source name and tracker urls to app generated `ComicInfo.xml` file ([@Shamicen](https://github.com/Shamicen)) ([#459](https://github.com/mihonapp/mihon/pull/459))
|
||||||
|
- Option to migrate in Duplicate entry dialog ([@sirlag](https://github.com/sirlag)) ([#492](https://github.com/mihonapp/mihon/pull/492))
|
||||||
|
- Upcoming screen to visualize expected update dates ([@sirlag](https://github.com/sirlag)) ([#420](https://github.com/mihonapp/mihon/pull/420))
|
||||||
|
- Only show upcoming updates in the future ([@sirlag](https://github.com/sirlag)) ([#606](https://github.com/mihonapp/mihon/pull/606))
|
||||||
|
- Add Quantity Badge to Upcoming Screen ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1250](https://github.com/mihonapp/mihon/pull/1250))
|
||||||
|
- Crash screen error message to the top of the crash log generated from that screen ([@FooIbar](https://github.com/FooIbar)) ([#742](https://github.com/mihonapp/mihon/pull/742))
|
||||||
|
- Support for 7Zip and RAR5 archives ([@FooIbar](https://github.com/FooIbar)) ([#949](https://github.com/mihonapp/mihon/pull/949))
|
||||||
|
- Extra configuration options to e-ink page flashes ([@sirlag](https://github.com/sirlag)) ([#625](https://github.com/mihonapp/mihon/pull/625))
|
||||||
|
- 8-bit+ AVIF image support ([@WerctFourth](https://github.com/WerctFourth)) ([#971](https://github.com/mihonapp/mihon/pull/971))
|
||||||
|
- Smart update dialog message when no predicted released date exists ([@Animeboynz](https://github.com/Animeboynz)) ([#977](https://github.com/mihonapp/mihon/pull/977))
|
||||||
|
- Option to copy reader panel to clipboard ([@Animeboynz](https://github.com/Animeboynz)) ([#1003](https://github.com/mihonapp/mihon/pull/1003))
|
||||||
|
- Copy Tracker URL option to tracker sheet ([@mm12](https://github.com/mm12)) ([#1101](https://github.com/mihonapp/mihon/pull/1101))
|
||||||
|
- A button to exclude all scanlators in exclude scanlators dialog ([@AntsyLich](https://github.com/AntsyLich)) ([`84b2164`](https://github.com/mihonapp/mihon/commit/84b2164787a795f3fd757c325cbfb6ef660ac3a3))
|
||||||
|
- Open in browser option to reader menu ([@mm12](https://github.com/mm12)) ([#1110](https://github.com/mihonapp/mihon/pull/1110))
|
||||||
|
- Reorder reader menu overflow items ([@AntsyLich](https://github.com/AntsyLich)) ([`788235f`](https://github.com/mihonapp/mihon/commit/788235feeca241228eac0561339dd07b5ea0b77d))
|
||||||
|
- Option to skip downloading duplicate read chapters ([@shabnix](https://github.com/shabnix)) ([#1125](https://github.com/mihonapp/mihon/pull/1125))
|
||||||
|
- Add confirmation dialog when adding repo via URI ([@Animeboynz](https://github.com/Animeboynz)) ([#1158](https://github.com/mihonapp/mihon/pull/1158))
|
||||||
|
- Add "show entry" action to download notifications ([@mm12](https://github.com/mm12), [@AntsyLich](https://github.com/AntsyLich)) ([#1159](https://github.com/mihonapp/mihon/pull/1159))
|
||||||
|
- Option to update trackers when chapter marked as read ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1177](https://github.com/mihonapp/mihon/pull/1177), [#1365](https://github.com/mihonapp/mihon/pull/1365), [#1374](https://github.com/mihonapp/mihon/pull/1374))
|
||||||
|
- Toast to restart app when User-Agent is changed ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#1204](https://github.com/mihonapp/mihon/pull/1204))
|
||||||
|
- Added more profile compilation status (p) ([`c8bb78d`](https://github.com/mihonapp/mihon/commit/c8bb78d91afc2824baaca999f0095559c49d1306))
|
||||||
|
- Add option to opt out of Analytics and Crashlytics ([@Animeboynz](https://github.com/Animeboynz)) ([#1237](https://github.com/mihonapp/mihon/pull/1237))
|
||||||
|
- Rework Firebase setup ([@AntsyLich](https://github.com/AntsyLich)) ([`15e3f28`](https://github.com/mihonapp/mihon/commit/15e3f28aa36bec3c31f212c572ab57ce960cc862))
|
||||||
|
- Added random library sort ([@jackhamilton](https://github.com/jackhamilton)) ([#1317](https://github.com/mihonapp/mihon/pull/1317))
|
||||||
|
- Make sure random library sort is at the bottom ([@AntsyLich](https://github.com/AntsyLich)) ([`2e2c8d3`](https://github.com/mihonapp/mihon/commit/2e2c8d36c1e23bf274c7c19f1242e14b0c7afbc1))
|
||||||
|
- Confirmation dialog when removing privately installed extensions ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1320](https://github.com/mihonapp/mihon/pull/1320))
|
||||||
|
- Option to backup non-library read entries ([@Animeboynz](https://github.com/Animeboynz), [@jobobby04](https://github.com/jobobby04), [@AntsyLich](https://github.com/AntsyLich)) ([#1324](https://github.com/mihonapp/mihon/pull/1324))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Read archive files from memory instead of temporarily extracting to internal storage ([@FooIbar](https://github.com/FooIbar)) ([#326](https://github.com/mihonapp/mihon/pull/326))
|
||||||
|
- Fix dual page split ([@FooIbar](https://github.com/FooIbar)) ([#485](https://github.com/mihonapp/mihon/pull/485))
|
||||||
|
- Bump default user agent ([@AntsyLich](https://github.com/AntsyLich)) ([`8160b47`](https://github.com/mihonapp/mihon/commit/8160b47ff5fbbd9b32caeb462b5be881fabd3449))
|
||||||
|
- Wait for sources to be initialized before performing source related tasks ([@jobobby04](https://github.com/jobobby04)) ([`a08e03f`](https://github.com/mihonapp/mihon/commit/a08e03f5cbf3f4e6be1de35f97ef8ebb26a1210e))
|
||||||
|
- Duplicate entry dialog UI ([@sirlag](https://github.com/sirlag)) ([#492](https://github.com/mihonapp/mihon/pull/492))
|
||||||
|
- Extension trust system
|
||||||
|
- Store extension repo details from `repo.json` in database ([@sirlag](https://github.com/sirlag)) ([#506](https://github.com/mihonapp/mihon/pull/506))
|
||||||
|
- Fix extension repo migration not triggering ([@AntsyLich](https://github.com/AntsyLich)) ([`9672ea8`](https://github.com/mihonapp/mihon/commit/9672ea8b1b06f464800e310c96e060ead182f7ca))
|
||||||
|
- Refactor the ExtensionRepoService to use DTOs ([@MajorTanya](https://github.com/MajorTanya)) ([#573](https://github.com/mihonapp/mihon/pull/573))
|
||||||
|
- Fix extension repo name is used to construct URL instead of baseUrl ([@MajorTanya](https://github.com/MajorTanya)) ([#572](https://github.com/mihonapp/mihon/pull/572))
|
||||||
|
- Fix crash with `TypeReference` issue when creating extension repo ([@AntsyLich](https://github.com/AntsyLich)) ([#574](https://github.com/mihonapp/mihon/pull/574), [`e020ae5`](https://github.com/mihonapp/mihon/commit/e020ae5ed558e80742ef0ad8bfa0f69af0959d5a))
|
||||||
|
- Fix mishap in [`e020ae5`](https://github.com/mihonapp/mihon/commit/e020ae5ed558e80742ef0ad8bfa0f69af0959d5a) ([@AntsyLich](https://github.com/AntsyLich)) ([`6965e59`](https://github.com/mihonapp/mihon/commit/6965e59a643c67a2bf81b3c69ec70268e5da5797))
|
||||||
|
- Backup and Restore ([@Animeboynz](https://github.com/Animeboynz)) ([#1057](https://github.com/mihonapp/mihon/pull/1057))
|
||||||
|
- Trust extension by repo ([@AntsyLich](https://github.com/AntsyLich)) ([#570](https://github.com/mihonapp/mihon/pull/570))-
|
||||||
|
- From M2 ripple to M3 ([@FooIbar](https://github.com/FooIbar)) ([#675](https://github.com/mihonapp/mihon/pull/675))
|
||||||
|
- Increased continue reading button size ([@AntsyLich](https://github.com/AntsyLich), [@Animeboynz](https://github.com/Animeboynz)) ([`e17f70f`](https://github.com/mihonapp/mihon/commit/e17f70f7226ea031fc1f962c9dfea3e404ba53ad))
|
||||||
|
- Global search "Has result" choice is now sticky ([@AntsyLich](https://github.com/AntsyLich)) ([`5a61ca5`](https://github.com/mihonapp/mihon/commit/5a61ca5535fe0d9e8e7bcb9e665ba2f9cb0cf649))
|
||||||
|
- Make category backup/restore not dependant on library backup ([@AntsyLich](https://github.com/AntsyLich)) ([`56fb4f6`](https://github.com/mihonapp/mihon/commit/56fb4f62a152e87a71892aa68c78cac51a2c8596))
|
||||||
|
- Rename backup restore error log file ([@AntsyLich](https://github.com/AntsyLich)) ([`2858ef8`](https://github.com/mihonapp/mihon/commit/2858ef835fec8d7278b1d0cad1b5664104d1e4b0))
|
||||||
|
- Keyboard type in add extension repo dialog ([@xbjfk](https://github.com/xbjfk)) ([#764](https://github.com/mihonapp/mihon/pull/764))
|
||||||
|
- Adjust collapse/open animation on manga description ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`1c16fc7`](https://github.com/mihonapp/mihon/commit/1c16fc79c2ac4c4be30308fed84ffb371dab5902))
|
||||||
|
- Kitsu domain to `kitsu.app` ([@MajorTanya](https://github.com/MajorTanya)) ([#1106](https://github.com/mihonapp/mihon/pull/1106))
|
||||||
|
- Respect privacy settings in extension update notification ([@Animeboynz](https://github.com/Animeboynz)) ([#1156](https://github.com/mihonapp/mihon/pull/1156))
|
||||||
|
- Hide keyboard when a Tracker SearchResultItem is clicked ([@Animeboynz](https://github.com/Animeboynz)) ([#1168](https://github.com/mihonapp/mihon/pull/1168))
|
||||||
|
- Enable 'Split Tall Images' by default ([@Smol-Ame](https://github.com/Smol-Ame)) ([#1185](https://github.com/mihonapp/mihon/pull/1185))
|
||||||
|
- Ignore "intent://" urls on webview ([@bapeey](https://github.com/bapeey)) ([#1193](https://github.com/mihonapp/mihon/pull/1193))
|
||||||
|
- Make reader chapter navigator slightly wider on small screens (p) ([#1202](https://github.com/mihonapp/mihon/pull/1202))
|
||||||
|
- Re-enable fetching chapters list for entries with licenced status ([@Animeboynz](https://github.com/Animeboynz)) ([#1230](https://github.com/mihonapp/mihon/pull/1230))
|
||||||
|
- Change casing for Extention Repos String ([@Animeboynz](https://github.com/Animeboynz)) ([#1248](https://github.com/mihonapp/mihon/pull/1248))
|
||||||
|
- Retain remote last chapter read if it's higher than the local one for EnhancedTracker ([@brewkunz](https://github.com/brewkunz)) ([#1301](https://github.com/mihonapp/mihon/pull/1301))
|
||||||
|
- Adjust expandable fab animation (p) ([`eb6092b`](https://github.com/mihonapp/mihon/commit/eb6092bd0cfa09694985a8bafdd8bbf2815190a1))
|
||||||
|
- "Invalidate downloads index" to "Reindex downloads" ([@AntsyLich](https://github.com/AntsyLich)) ([`d2afbfe`](https://github.com/mihonapp/mihon/commit/d2afbfe4ede283076aae40633c79c3f90b4390e7))
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Reader performance
|
||||||
|
- Avoid unnecessary copying when processing reader image ([@FooIbar](https://github.com/FooIbar)) ([#691](https://github.com/mihonapp/mihon/pull/691))
|
||||||
|
- Significantly improve performance when loading extremely long images in long strip mode ([@FooIbar](https://github.com/FooIbar)) ([#692](https://github.com/mihonapp/mihon/pull/692))
|
||||||
|
- Use `Bitmap.Config.HARDWARE` if possible to improve image loading speed ([@wwww-wwww](https://github.com/wwww-wwww)) ([#687](https://github.com/mihonapp/mihon/pull/687))
|
||||||
|
- Improve preloading in long strip mode ([@FooIbar](https://github.com/FooIbar)) ([#1076](https://github.com/mihonapp/mihon/pull/1076))
|
||||||
|
- Performance when looking up specific files ([@raxod502](https://github.com/raxod502)) ([#728](https://github.com/mihonapp/mihon/pull/728))
|
||||||
|
- Chapter number parsing ([@Naputt1](https://github.com/Naputt1)) ([`6a80305`](https://github.com/mihonapp/mihon/commit/6a80305d6c572da6c08c0c69f5c25ff26ecf7383))
|
||||||
|
- Error message on restoring if backup decoding fails ([@vetleledaal](https://github.com/vetleledaal)) ([#1056](https://github.com/mihonapp/mihon/pull/1056))
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Legacy download folder names no longer supported ([@AntsyLich](https://github.com/AntsyLich)) ([`e55e5f6`](https://github.com/mihonapp/mihon/commit/e55e5f6f64f872475d370d6ce0c186e2601776e4))
|
||||||
|
- Remove legacy broken source and history backup ([@AntsyLich](https://github.com/AntsyLich)) ([`518abf0`](https://github.com/mihonapp/mihon/commit/518abf032ccb9bb45d197927be2a5faca4167d29))
|
||||||
|
- Remove more unnecessary permissions from Firebase dependency ([@AntsyLich](https://github.com/AntsyLich)) ([`02af9b1`](https://github.com/mihonapp/mihon/commit/02af9b1acf9f590d29560bc3fc90d206e8e6e1af))
|
||||||
|
- Fix mishap in `02af9b1` ([@AntsyLich](https://github.com/AntsyLich)) ([`f22767d`](https://github.com/mihonapp/mihon/commit/f22767d863a0fa001f93f24092cd5ade87350502))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Extracting `ComicInfo.xml` from local source archives ([@FooIbar](https://github.com/FooIbar)) ([#325](https://github.com/mihonapp/mihon/pull/325))
|
||||||
|
- Chapter download indicator ([@ivaniskandar](https://github.com/ivaniskandar)) ([`d8b9a9f`](https://github.com/mihonapp/mihon/commit/d8b9a9f593911569ff2bceb49b4f020978d0d2e1))
|
||||||
|
- Issues with shizuku in a multi user setup ([@Redjard](https://github.com/Redjard)) ([#494](https://github.com/mihonapp/mihon/pull/494))
|
||||||
|
- Fix reader page image not being decoded until it's visible ([@FooIbar](https://github.com/FooIbar)) ([#563](https://github.com/mihonapp/mihon/pull/563))
|
||||||
|
- Reader chapter progress slider visuals ([@FooIbar](https://github.com/FooIbar)) ([#674](https://github.com/mihonapp/mihon/pull/674))
|
||||||
|
- Extension being marked as not installed instead of untrusted after updating with private installer ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
|
||||||
|
- Extension update counter not updating due to extension being marked as untrusted ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
|
||||||
|
- `Key "extension-XXX-YYY" was already used` crash ([@AntsyLich](https://github.com/AntsyLich)) ([`2114514`](https://github.com/mihonapp/mihon/commit/21145144cdf550aa775047603e06e261951ebc42))
|
||||||
|
- Navigation layout tap zones shifting after zooming out in webtoon readers ([@FooIbar](https://github.com/FooIbar)) ([#767](https://github.com/mihonapp/mihon/pull/767))
|
||||||
|
- Some extension not loading due to missing classes ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#783](https://github.com/mihonapp/mihon/pull/783))
|
||||||
|
- Theme colors in accordance to upstream changes ([@CrepeTF](https://github.com/CrepeTF), [@AntsyLich](https://github.com/AntsyLich)) ([#766](https://github.com/mihonapp/mihon/pull/766), [#963](https://github.com/mihonapp/mihon/pull/963), [#976](https://github.com/mihonapp/mihon/pull/976), [9a34ace](https://github.com/mihonapp/mihon/commit/9a34ace09c66274e6c2b3f9446058a0fa99d4bd0))
|
||||||
|
- Crash when requesting folder access on non-conforming devices ([@mainrs](https://github.com/mainrs)) ([#726](https://github.com/mihonapp/mihon/pull/726))
|
||||||
|
- Fix unexpected skips in strong skipping mode ([@FooIbar](https://github.com/FooIbar)) ([#940](https://github.com/mihonapp/mihon/pull/940))
|
||||||
|
- Bugged color for Date/Scanlator in chapter list for read chapters ([@ivaniskandar](https://github.com/ivaniskandar)) ([`15d9992`](https://github.com/mihonapp/mihon/commit/15d999229fcce865001d5fa77d0163e6e80e38db))
|
||||||
|
- Categories having same `order` after restoring backup ([@Cologler](https://github.com/Cologler)) ([`119bcbf`](https://github.com/mihonapp/mihon/commit/119bcbf8ed2415664922ea77fadf0da1165d1732))
|
||||||
|
- Filter by "Tracking" temporarily stuck after signing out of tracker ([@AntsyLich](https://github.com/AntsyLich)) ([#987](https://github.com/mihonapp/mihon/pull/987))
|
||||||
|
- Fix login prompts despite being logged in to trackers in Manga screen ([@AntsyLich](https://github.com/AntsyLich)) ([`cbcd8bd`](https://github.com/mihonapp/mihon/commit/cbcd8bd6682023f728568f2b44da26124618aed7))
|
||||||
|
- JXL image downloading and loading ([@FooIbar](https://github.com/FooIbar)) ([#993](https://github.com/mihonapp/mihon/pull/993))
|
||||||
|
- Crash when using `%` in category name ([@Animeboynz](https://github.com/Animeboynz), [@FooIbar](https://github.com/FooIbar)) ([#1030](https://github.com/mihonapp/mihon/pull/1030))
|
||||||
|
- Fix item disappearing when fast scrolling ([@cuong-tran](https://github.com/cuong-tran)) ([#1035](https://github.com/mihonapp/mihon/pull/1035))
|
||||||
|
- Library is backed up while being disabled ([@AntsyLich](https://github.com/AntsyLich)) ([`56fb4f6`](https://github.com/mihonapp/mihon/commit/56fb4f62a152e87a71892aa68c78cac51a2c8596))
|
||||||
|
- Crash on list with only sticky header ([@cuong-tran](https://github.com/cuong-tran)) ([#1083](https://github.com/mihonapp/mihon/pull/1083))
|
||||||
|
- Crash when trying to clear cookies of some source ([@FooIbar](https://github.com/FooIbar)) ([#1084](https://github.com/mihonapp/mihon/pull/1084))
|
||||||
|
- MAL search results not showing start dates ([@MajorTanya](https://github.com/MajorTanya)) ([#1098](https://github.com/mihonapp/mihon/pull/1098))
|
||||||
|
- Android SDK 35 API collision ([@AntsyLich](https://github.com/AntsyLich)) ([`fdb9617`](https://github.com/mihonapp/mihon/commit/fdb96179c6373eb0a8e7d6daea671a315d5ce5f0))
|
||||||
|
- Manga next update calculation when considering custom fetch interval ([@cuong-tran](https://github.com/cuong-tran)) ([#1206](https://github.com/mihonapp/mihon/pull/1206))
|
||||||
|
- WheelPicker Manual Input ([@Animeboynz](https://github.com/Animeboynz)) ([#1209](https://github.com/mihonapp/mihon/pull/1209))
|
||||||
|
- EnhancedTracker not auto binding when adding manga to library ([@brewkunz](https://github.com/brewkunz)) ([#1298](https://github.com/mihonapp/mihon/pull/1298))
|
||||||
|
- Step count in settings slider ([@abdurisaq](https://github.com/abdurisaq)) ([#1356](https://github.com/mihonapp/mihon/pull/1356))
|
||||||
|
- Freezing in some screens due to blocking call ([@cuong-tran](https://github.com/cuong-tran)) ([#1364](https://github.com/mihonapp/mihon/pull/1364))
|
||||||
|
- Crash when removing non-existent tracked entry from tracker ([@cuong-tran](https://github.com/cuong-tran)) ([#1380](https://github.com/mihonapp/mihon/pull/1380))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- Code cleanup
|
||||||
|
- Minor refactor of theming when expressions ([@MajorTanya](https://github.com/MajorTanya)) ([#396](https://github.com/mihonapp/mihon/pull/396))
|
||||||
|
- Inside `WorkerInfoScreen` ([@AntsyLich](https://github.com/AntsyLich)) ([`5aec8f8`](https://github.com/mihonapp/mihon/commit/5aec8f8018236a38106483da08f9cbc28261ac9b))
|
||||||
|
- Inside `ChapterDownloadIndicator`, `MangaChapterListItem` ([@AntsyLich](https://github.com/AntsyLich)) ([`b7e091d`](https://github.com/mihonapp/mihon/commit/b7e091d5d039e00cababc7daf555280df6cf9c03))
|
||||||
|
- MangaCoverFetcher ([@ivaniskandar](https://github.com/ivaniskandar)) ([`1365695`](https://github.com/mihonapp/mihon/commit/13656959ae0606736f6ca9eb62699dc23e467c2f))
|
||||||
|
- Cleanup `LibraryScreenModel` `LibraryMap.applySort` and some more ([@AntsyLich](https://github.com/AntsyLich)) ([`2beb89d`](https://github.com/mihonapp/mihon/commit/2beb89d53163a6d288f8acdebe0f5d26fea8ab3e))
|
||||||
|
- Address `overridePendingTransition` deprecation ([@MajorTanya](https://github.com/MajorTanya)) ([#410](https://github.com/mihonapp/mihon/pull/410))
|
||||||
|
- Prioritize extension classes and files over app ([@beer-psi](https://github.com/beer-psi)) ([#433](https://github.com/mihonapp/mihon/pull/433))
|
||||||
|
- Use compose pager implementation ([@ivaniskandar](https://github.com/ivaniskandar)) ([`84984ef`](https://github.com/mihonapp/mihon/commit/84984ef7e1d7242924120cd2f171cb9dd75bc916))
|
||||||
|
- Switch to coil3 from coil2 ([@ivaniskandar](https://github.com/ivaniskandar)) ([`f72b6e4`](https://github.com/mihonapp/mihon/commit/f72b6e4d7c1f2f93d705402e4d80c94160bef54d))
|
||||||
|
- Fix GIF not playing ([@jobobby04](https://github.com/jobobby04)) ([`59bedb3`](https://github.com/mihonapp/mihon/commit/59bedb33ff59ad5db1df2e93567a2266fb63eacc))
|
||||||
|
- Accommodate db for sync support ([@kaiserbh](https://github.com/kaiserbh)) ([#450](https://github.com/mihonapp/mihon/pull/450))
|
||||||
|
- Fix webtoon last visible item position calculation ([@FooIbar](https://github.com/FooIbar)) ([#562](https://github.com/mihonapp/mihon/pull/562))
|
||||||
|
- Migrate from `com.google.accompanist:accompanist-webview` to `io.github.kevinnzou:compose-webview` ([@sirlag](https://github.com/sirlag)) ([#569](https://github.com/mihonapp/mihon/pull/569))
|
||||||
|
- Rewrite migrations ([@ghostbear](https://github.com/ghostbear)) ([#577](https://github.com/mihonapp/mihon/pull/577))
|
||||||
|
- Further improve migration ([@ghostbear](https://github.com/ghostbear)) ([#588](https://github.com/mihonapp/mihon/pull/588))
|
||||||
|
- Fix migrations not running ([@ghostbear](https://github.com/ghostbear)) ([#604](https://github.com/mihonapp/mihon/pull/604))
|
||||||
|
- Fix MigratorTest after updating to Kotlin 2 ([@cuong-tran](https://github.com/cuong-tran)) ([#896](https://github.com/mihonapp/mihon/pull/896))
|
||||||
|
- Add MigratorTest to build script ([@cuong-tran](https://github.com/cuong-tran)) ([#896](https://github.com/mihonapp/mihon/pull/896))
|
||||||
|
- Fix UI freeze after migration ([@AntsyLich](https://github.com/AntsyLich)) ([`3f1d28c`](https://github.com/mihonapp/mihon/commit/3f1d28c3833e6b868152149ed02b3fb8c54eccef))
|
||||||
|
- Fix some migrations never running ([@MajorTanya](https://github.com/MajorTanya), [@AntsyLich](https://github.com/AntsyLich)) ([#1030](https://github.com/mihonapp/mihon/pull/1030))
|
||||||
|
- Add ProGuard rule to keep `mihon` namespace classes ([@MajorTanya](https://github.com/MajorTanya)) ([#605](https://github.com/mihonapp/mihon/pull/605))
|
||||||
|
- Use gradle plugins to share build configuration instead of subprojects ([@AntsyLich](https://github.com/AntsyLich)) ([`e448e40`](https://github.com/mihonapp/mihon/commit/e448e40406e8d9916120a278e42829a6f1b25a7a))
|
||||||
|
- Remove dependency on compose material 2 components ([@AntsyLich](https://github.com/AntsyLich)) ([`fb94230`](https://github.com/mihonapp/mihon/commit/fb9423028eb017c110cb805f2d0601e5b02e50f9))
|
||||||
|
- Upload PR build artifacts to GitHub ([@FooIbar](https://github.com/FooIbar)) ([#941](https://github.com/mihonapp/mihon/pull/941))
|
||||||
|
- Refactor archive support with libarchive ([@FooIbar](https://github.com/FooIbar)) ([#949](https://github.com/mihonapp/mihon/pull/949))
|
||||||
|
- Add safeguard to prevent ArchiveInputStream from being closed twice ([@null2264](https://github.com/null2264)) ([#967](https://github.com/mihonapp/mihon/pull/967))
|
||||||
|
- Move archive related code to :core:archive ([@AntsyLich](https://github.com/AntsyLich)) ([`bd7b354`](https://github.com/mihonapp/mihon/commit/bd7b35419861df6d426d6ec0a188391910d0f615))
|
||||||
|
- Replace detekt with ktlint via spotless ([@AntsyLich](https://github.com/AntsyLich)) ([#1130](https://github.com/mihonapp/mihon/pull/1130), [#1136](https://github.com/mihonapp/mihon/pull/1136), [#1138](https://github.com/mihonapp/mihon/pull/1138))
|
||||||
|
- Refrain from running spotless on weblate files ([@AntsyLich](https://github.com/AntsyLich)) ([`32d2c2a`](https://github.com/mihonapp/mihon/commit/32d2c2ac1bc224cbda2f09a4023d7d120ea0e954))
|
||||||
|
- Use feature flags in compose compiler plugin ([@AntsyLich](https://github.com/AntsyLich)) ([`8f9a325`](https://github.com/mihonapp/mihon/commit/8f9a325895bb7b94c2ec92dd969094fc30b3b5e2))- PagerPageHolder: lazy init loading indicator ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`a45eb5e`](https://github.com/mihonapp/mihon/commit/a45eb5e5288159dbbbbb5f92140ce0dd32a8f3ab))
|
||||||
|
- Collect MangaScreen state with lifecycle ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`03eb756`](https://github.com/mihonapp/mihon/commit/03eb756ecba0692d88d3a76254afc4c157fa225b))
|
||||||
|
- Add stable marker to Manga data class ([@AntsyLich](https://github.com/AntsyLich), [@ivaniskandar](https://github.com/ivaniskandar)) ([`03eb756`](https://github.com/mihonapp/mihon/commit/03eb756ecba0692d88d3a76254afc4c157fa225b))
|
||||||
|
- Use DTOs to parse tracking API responses ([@MajorTanya](https://github.com/MajorTanya)) ([#1103](https://github.com/mihonapp/mihon/pull/1103))
|
||||||
|
- Fix Kitsu ratingTwenty being typed as String ([@MajorTanya](https://github.com/MajorTanya)) ([#1191](https://github.com/mihonapp/mihon/pull/1191))
|
||||||
|
- Fix Kitsu `synopsis` nullability ([@MajorTanya](https://github.com/MajorTanya)) ([#1233](https://github.com/mihonapp/mihon/pull/1233))
|
||||||
|
- Fix AniList `ALSearchItem.status` nullibility ([@Secozzi](https://github.com/Secozzi)) ([#1297](https://github.com/mihonapp/mihon/pull/1297))
|
||||||
|
- Migrate some classpaths to gradle plugins ([@AntsyLich](https://github.com/AntsyLich)) ([`fc1c804`](https://github.com/mihonapp/mihon/commit/fc1c804bfda1d76c0399bbb6214e75b3def951cc))
|
||||||
|
- Add crashlytics to standard builds ([@AntsyLich](https://github.com/AntsyLich)) ([`3c611b9`](https://github.com/mihonapp/mihon/commit/3c611b95fb79e5ac972019b76c7b24f46a3087fd))
|
||||||
|
- Switch to stable compose ([@AntsyLich](https://github.com/AntsyLich)) ([`2baffa6`](https://github.com/mihonapp/mihon/commit/2baffa62cade1abd978d5fd03151b47fc87fd31e))
|
||||||
|
- Switch from inorichi injekt to kohesive Injekt ([@AntsyLich](https://github.com/AntsyLich)) ([#1205](https://github.com/mihonapp/mihon/pull/1205))
|
||||||
|
- Use custom injekt register with inorichi patch ([@AntsyLich](https://github.com/AntsyLich)) ([`83fd474`](https://github.com/mihonapp/mihon/commit/83fd4746eda1b99f35292b0c2211e606a421b3eb))
|
||||||
|
- Use TextFieldState in BasicTextField where applicable (p) ([#1201](https://github.com/mihonapp/mihon/pull/1201))
|
||||||
|
- Bump NDK version ([@AntsyLich](https://github.com/AntsyLich)) ([#1203](https://github.com/mihonapp/mihon/pull/1203))
|
||||||
|
- Move firebase permission removal to standard flavor ([@AntsyLich](https://github.com/AntsyLich)) ([`be671b4`](https://github.com/mihonapp/mihon/commit/be671b42cefd70180644e01bb065a18cb7701bf9))
|
||||||
|
- Adjust distinct checker in WidgetManager and run on default dispatcher (p) ([`9b8ab6a`](https://github.com/mihonapp/mihon/commit/9b8ab6acc25a5f99c9c5eebf9cc250975931c57c))
|
||||||
|
- Update resources exclusion rules (p) ([`481cfed`](https://github.com/mihonapp/mihon/commit/481cfedf08576cecfbb35616837bd8f627d8f959))
|
||||||
|
- Bump compile sdk to 35 (p) ([`37419cd`](https://github.com/mihonapp/mihon/commit/37419cdc26c2b5c4f8583fc2ba439b08fab42856))
|
||||||
|
- ChapterNavigator: dispatch page change only when needed (p) ([`f84d9a0`](https://github.com/mihonapp/mihon/commit/f84d9a08b4af768b1e9920c43cc445c86f5427fc))
|
||||||
|
- Remove usage of deprecated accompanist SystemUiController ([@AntsyLich](https://github.com/AntsyLich)) ([`2ba3f06`](https://github.com/mihonapp/mihon/commit/2ba3f0612c08c7021fed2f6d96cd538da2f34a13))
|
||||||
|
- Run PR check when base strings are changed ([@AntsyLich](https://github.com/AntsyLich)) ([`4051f18`](https://github.com/mihonapp/mihon/commit/4051f180a2e36e8a2cde6c55f0bea7952fdc4704))
|
||||||
|
- Fix PR build check ([@AntsyLich](https://github.com/AntsyLich)) ([`9503082`](https://github.com/mihonapp/mihon/commit/9503082d44b5bd868ee1bfc42741dc978d1d9047))
|
||||||
|
- Cleanup .gitignore files ([@AntsyLich](https://github.com/AntsyLich)) ([`afa5002`](https://github.com/mihonapp/mihon/commit/afa50029882655af8d5eea40aed7644fce4564d8))
|
||||||
|
- Pass uncaught exception to default handler in GlobalExceptionHandler (so it's reported to crashlytics) ([@AntsyLich](https://github.com/AntsyLich)) ([`f3a2f56`](https://github.com/mihonapp/mihon/commit/f3a2f566c8a09ab862758ae69b43da2a2cd8f1db))
|
||||||
|
|
||||||
|
## [v0.16.5] - 2024-04-09
|
||||||
|
### Added
|
||||||
|
- Relative date for up to a week in the future ([@sirlag](https://github.com/sirlag)) ([#415](https://github.com/mihonapp/mihon/pull/415))
|
||||||
|
- Advance setting to install custom color profiles ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Permanently enable 32-bit color mode ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Wrong dates in Updates and History tab due to time zone issues ([@sirlag](https://github.com/sirlag)) ([#402](https://github.com/mihonapp/mihon/pull/402))
|
||||||
|
- Fix extra date header introduced by parent PR ([@sirlag](https://github.com/sirlag)) ([#415](https://github.com/mihonapp/mihon/pull/415))
|
||||||
|
- Fix build time in about screen displayed in UTC ([@AntsyLich](https://github.com/AntsyLich)) ([`aed53d3`](https://github.com/mihonapp/mihon/commit/aed53d3bdc85ce0e899fbb90b9f9cad0f1b86480))
|
||||||
|
- App infinitely retries tracker update instead of failing after 3 tries ([@MajorTanya](https://github.com/MajorTanya)) ([#411](https://github.com/mihonapp/mihon/pull/411))
|
||||||
|
- Crash on Pixel devices (was introduced due to compose update) ([`ab06720`](https://github.com/mihonapp/mihon/commit/ab067209661eceefc04c65f6bdbfcaa8a1264651))
|
||||||
|
- Crash when opening some heif/heic images ([@az4521](https://github.com/az4521)) ([#466](https://github.com/mihonapp/mihon/pull/466))
|
||||||
|
- Crash when putting app in background while track date selection dialog is open ([@ivaniskandar](https://github.com/ivaniskandar)) ([`c348fac`](https://github.com/mihonapp/mihon/commit/c348fac78fac479fb123bd617c01c78b9ca851d5))
|
||||||
|
- Dates for saved images not following the specification (fixes date issue mainly on Samsung devices) ([@MajorTanya](https://github.com/MajorTanya)) ([#552](https://github.com/mihonapp/mihon/pull/552))
|
||||||
|
- Colors getting distorted when opening CMYK jpeg images ([@wwww-wwww](https://github.com/wwww-wwww)) ([#523](https://github.com/mihonapp/mihon/pull/523))
|
||||||
|
|
||||||
|
## [v0.16.4] - 2024-02-27
|
||||||
|
### Changed
|
||||||
|
- Don't include custom user agent for MAL (circumvents MAL block) ([@AntsyLich](https://github.com/AntsyLich)) ([`085ad8d`](https://github.com/mihonapp/mihon/commit/085ad8d44637c375a8ed24aba3a6f75f5b0cc9ee))
|
||||||
|
|
||||||
|
## [v0.16.3] - 2024-01-30
|
||||||
|
### Added
|
||||||
|
- Copy extension debug info when clicking logo or name in the extension details screen ([@MajorTanya](https://github.com/MajorTanya)) ([#271](https://github.com/mihonapp/mihon/pull/271))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Hide display cutoff setting in reader settings sheet if fullscreen is disabled ([@Riztard](https://github.com/Riztard)) ([#241](https://github.com/mihonapp/mihon/pull/241))
|
||||||
|
- Library update error filename to `mihon_update_errors.txt` from `tachiyomi_update_errors.txt` ([@mjishnu](https://github.com/mjishnu)) ([#253](https://github.com/mihonapp/mihon/pull/253))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bottom sheet UI issues on non-tablet devices ([@theolm](https://github.com/theolm)) ([#182](https://github.com/mihonapp/mihon/pull/182))
|
||||||
|
- Crash when switching screen while a list is scrolling ([@theolm](https://github.com/theolm)) ([#272](https://github.com/mihonapp/mihon/pull/272))
|
||||||
|
- Newly installed extensions not being recognized by Mihon ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#275](https://github.com/mihonapp/mihon/pull/275))
|
||||||
|
- Failing to refresh MAL token being inferred as token expiration ([@AntsyLich](https://github.com/AntsyLich)) ([`0f4de03`](https://github.com/mihonapp/mihon/commit/0f4de03d7a77b52490dc9a95e96a308b93b26e4f))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- Add `detekt` (kotlin code analyzer) to the project ([@theolm](https://github.com/theolm)) ([#216](https://github.com/mihonapp/mihon/pull/216))
|
||||||
|
|
||||||
|
## [v0.16.2] - 2024-01-28
|
||||||
|
### Changed
|
||||||
|
- Backup now contains scanlator filter of a series ([@jobobby04](https://github.com/jobobby04)) ([#166](https://github.com/mihonapp/mihon/pull/166))
|
||||||
|
- App icon scaling ([@AntsyLich](https://github.com/AntsyLich)) ([`26815c7`](https://github.com/mihonapp/mihon/commit/26815c7356111394665467c1e81255ac9ee33c1a))
|
||||||
|
- Tracker OAuth client to Mihon's (fixes login issue for Shikimori tracker) ([@AntsyLich](https://github.com/AntsyLich)) ([`e3f33e2`](https://github.com/mihonapp/mihon/commit/e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5))
|
||||||
|
- Tracker user agents ([@AntsyLich](https://github.com/AntsyLich), [@kitsumed](https://github.com/kitsumed)) ([`e3f33e2`](https://github.com/mihonapp/mihon/commit/e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5))
|
||||||
|
- Crash log filename to `mihon_crash_logs.txt` from `tachiyomi_crash_logs.txt` ([@MajorTanya](https://github.com/MajorTanya)) ([#234](https://github.com/mihonapp/mihon/pull/234))
|
||||||
|
- Don't try to refresh MAL token after refresh token expires ([@AntsyLich](https://github.com/AntsyLich)) ([`32188f9`](https://github.com/mihonapp/mihon/commit/32188f9f65009a18250674ef1bd6e57d351c1fba))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- "Flash screen on page change" making the screen full black ([@AntsyLich](https://github.com/AntsyLich)) ([`38d6ab8`](https://github.com/mihonapp/mihon/commit/38d6ab80ce868707829dbc81de4170afe3c2f2a5))
|
||||||
|
- Faulty MangaUpdates score in database ([@AntsyLich](https://github.com/AntsyLich) ([`a024218`](https://github.com/mihonapp/mihon/commit/a024218410953a389b8af4880fa7ae6cc30124a2)
|
||||||
|
- Updating extension not reflecting correctly ([@AntsyLich](https://github.com/AntsyLich)) ([`cb06898`](https://github.com/mihonapp/mihon/commit/cb068984303f811692531bf6f14902ae118d8ac7))
|
||||||
|
- Inconsistent button height in "Data and storage" for some languages ([@theolm](https://github.com/theolm)) ([#202](https://github.com/mihonapp/mihon/pull/202))
|
||||||
|
- Chapter not being marked as read locally when refreshing Enhanced Trackers ([@Secozzi](https://github.com/Secozzi)) ([#219](https://github.com/mihonapp/mihon/pull/219))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- Make `last_modified_at` field in database be `0` on insert ([@kaiserbh](https://github.com/kaiserbh)) ([#113](https://github.com/mihonapp/mihon/pull/113))
|
||||||
|
- Remove usage of `.not()` where possible in code ([@AntsyLich](https://github.com/AntsyLich)) ([`3940740`](https://github.com/mihonapp/mihon/commit/39407407f282dbb7fa972b12053c26b3e3bd66d8))
|
||||||
|
- Use type-safe project accessors ([@theolm](https://github.com/theolm)) ([#194](https://github.com/mihonapp/mihon/pull/194))
|
||||||
|
- Legacy tracker model properties now has the same type as the domain ones ([@AntsyLich](https://github.com/AntsyLich)) ([#245](https://github.com/mihonapp/mihon/pull/245))
|
||||||
|
|
||||||
|
## [v0.16.1] - 2024-01-18
|
||||||
|
### Changed
|
||||||
|
- Branding to Mihon (for references we missed) ([@AntsyLich](https://github.com/AntsyLich)) ([`6539406`](https://github.com/mihonapp/mihon/commit/653940613d661eb371aab3b3c3a8181e4e308c43))
|
||||||
|
- Preview builds are now called Beta builds ([@AntsyLich](https://github.com/AntsyLich)) ([`3c3a1cd`](https://github.com/mihonapp/mihon/commit/3c3a1cd448ab1f653ddd12b2afe0cba38968d1b9))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- App icon not following the [specification](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive) ([@AntsyLich](https://github.com/AntsyLich)) ([`1849715`](https://github.com/mihonapp/mihon/commit/18497154183356bb0d469b27827f9f7d6b7a3130))
|
||||||
|
- MangaUpdates default score being set to -1.0 ([@AntsyLich](https://github.com/AntsyLich)) ([`99fd273`](https://github.com/mihonapp/mihon/commit/99fd2731f5d9d374700e89fa67d4d5bf611bbafa))
|
||||||
|
|
||||||
|
## [v0.16.0] - 2024-01-16
|
||||||
|
### Changed
|
||||||
|
- Branding to Mihon ([@AntsyLich](https://github.com/AntsyLich))
|
||||||
|
- Minimum supported Android version to 8 ([@AntsyLich](https://github.com/AntsyLich)) ([`dfb3091`](https://github.com/mihonapp/mihon/commit/dfb3091e380dda3e9bfb64bf5c9a685cf3a03d0e))
|
||||||
|
|
||||||
|
[unreleased]: https://github.com/mihonapp/mihon/compare/v0.17.0...main
|
||||||
|
[v0.17.0]: https://github.com/mihonapp/mihon/compare/v0.16.5...v0.17.0
|
||||||
|
[v0.16.5]: https://github.com/mihonapp/mihon/compare/v0.16.4...v0.16.5
|
||||||
|
[v0.16.4]: https://github.com/mihonapp/mihon/compare/v0.16.3...v0.16.4
|
||||||
|
[v0.16.3]: https://github.com/mihonapp/mihon/compare/v0.16.2...v0.16.3
|
||||||
|
[v0.16.2]: https://github.com/mihonapp/mihon/compare/v0.16.1...v0.16.2
|
||||||
|
[v0.16.1]: https://github.com/mihonapp/mihon/compare/v0.16.0...v0.16.1
|
||||||
|
[v0.16.0]: https://github.com/mihonapp/mihon/compare/a9c7cbf...v0.16.0
|
@ -24,10 +24,6 @@ Before you start, please note that the ability to use following technologies is
|
|||||||
- [Android Studio](https://developer.android.com/studio)
|
- [Android Studio](https://developer.android.com/studio)
|
||||||
- Emulator or phone with developer options enabled to test changes.
|
- Emulator or phone with developer options enabled to test changes.
|
||||||
|
|
||||||
## Linting
|
|
||||||
|
|
||||||
To auto-fix some linting errors, run the `ktlintFormat` Gradle task.
|
|
||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
|
|
||||||
- Join [the Discord server](https://discord.gg/mihon) for online help and to ask questions while developing.
|
- Join [the Discord server](https://discord.gg/mihon) for online help and to ask questions while developing.
|
||||||
|
127
README.md
127
README.md
@ -1,89 +1,86 @@
|
|||||||
| Build | Stable | Weekly Preview | Support Server |
|
<div align="center">
|
||||||
|-------|----------|---------|---------|
|
|
||||||
| [](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml) | [](https://github.com/mihonapp/mihon/releases) | [](https://github.com/mihonapp/mihon-preview/releases) | [](https://discord.gg/mihon) |
|
|
||||||
|
|
||||||
# Mihon
|
<a href="https://mihon.app">
|
||||||
Mihon is a free and open source manga reader for Android 8.0 and above.
|
<img src="./.github/assets/logo.png" alt="Mihon logo" title="Mihon logo" width="80"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
# Mihon [App](#)
|
||||||
|
|
||||||
|
### Full-featured reader
|
||||||
|
Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.
|
||||||
|
|
||||||
|
[](https://discord.gg/mihon)
|
||||||
|
[](https://github.com/mihonapp/mihon/releases)
|
||||||
|
|
||||||
|
[](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml)
|
||||||
|
[](/LICENSE)
|
||||||
|
[](https://hosted.weblate.org/engage/mihon/)
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
[](https://github.com/mihonapp/mihon/releases)
|
||||||
|
[](https://github.com/mihonapp/mihon-preview/releases)
|
||||||
|
|
||||||
|
*Requires Android 8.0 or higher.*
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Features include:
|
<div align="left">
|
||||||
* Online reading from a variety of sources
|
|
||||||
* Local reading of downloaded content
|
* Local reading of content.
|
||||||
* A configurable reader with multiple viewers, reading directions and other settings.
|
* A configurable reader with multiple viewers, reading directions and other settings.
|
||||||
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support
|
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.app/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
|
||||||
* Categories to organize your library
|
* Categories to organize your library.
|
||||||
* Light and dark themes
|
* Light and dark themes.
|
||||||
* Schedule updating your library for new chapters
|
* Schedule updating your library for new chapters.
|
||||||
* Create backups locally to read offline or to your desired cloud service
|
* Create backups locally to read offline or to your desired cloud service.
|
||||||
|
* Plus much more...
|
||||||
|
|
||||||
## Download
|
</div>
|
||||||
Get the app from our [releases page](https://github.com/mihonapp/mihon/releases).
|
|
||||||
|
|
||||||
If you want to try new features before they get to the stable release, you can download the preview version [here](https://github.com/mihonapp/mihon-preview/releases).
|
## Contributing
|
||||||
|
|
||||||
## Issues, Feature Requests and Contributing
|
[Code of conduct](./CODE_OF_CONDUCT.md) · [Contributing guide](./CONTRIBUTING.md)
|
||||||
|
|
||||||
Please make sure to read the full guidelines. Your issue may be closed without warning if you do not.
|
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||||
|
|
||||||
<details><summary>Issues</summary>
|
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues); if you got any questions, join our [Discord server](https://discord.gg/mihon).
|
||||||
|
|
||||||
1. **Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).**
|
|
||||||
2. If you are unsure, ask here: [](https://discord.gg/mihon)
|
|
||||||
|
|
||||||
</details>
|
### Repositories
|
||||||
|
|
||||||
<details><summary>Bugs</summary>
|
[](https://github.com/mihonapp/website/)
|
||||||
|
[](https://github.com/mihonapp/bitmap.kt/)
|
||||||
|
|
||||||
* Include version (More → About → Version)
|
### Credits
|
||||||
* If not latest, try updating, it may have already been solved
|
|
||||||
* Preview version is equal to the number of commits as seen on the main page
|
|
||||||
* Include steps to reproduce (if not obvious from description)
|
|
||||||
* Include screenshot (if needed)
|
|
||||||
* If it could be device-dependent, try reproducing on another device (if possible)
|
|
||||||
* Don't group unrelated requests into one issue
|
|
||||||
|
|
||||||
</details>
|
Thank you to all the people who have contributed!
|
||||||
|
|
||||||
<details><summary>Feature Requests</summary>
|
<a href="https://github.com/mihonapp/mihon/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=mihonapp/mihon" alt="Mihon app contributors" title="Mihon app contributors" width="800"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
|
### Disclaimer
|
||||||
* Include screenshot (if needed)
|
|
||||||
|
|
||||||
Source requests are not accepted.
|
The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>Contributing</summary>
|
### License
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
<pre>
|
||||||
</details>
|
Copyright © 2015 Javier Tomás
|
||||||
|
Copyright © 2024 The Mihon Open Source Project
|
||||||
|
|
||||||
<details><summary>Code of Conduct</summary>
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
</details>
|
|
||||||
|
|
||||||
## FAQ
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
</pre>
|
||||||
|
|
||||||
[See our website.](https://mihon.app/)
|
</div>
|
||||||
You can also reach out to us on [Discord](https://discord.gg/mihon).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright 2015 Javier Tomás
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
The developer of this application does not have any affiliation with the content providers available.
|
|
||||||
|
3
app/.gitignore
vendored
3
app/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/build
|
|
||||||
*iml
|
|
||||||
*.iml
|
|
@ -1,20 +1,26 @@
|
|||||||
|
import mihon.buildlogic.getBuildTime
|
||||||
|
import mihon.buildlogic.getCommitCount
|
||||||
|
import mihon.buildlogic.getGitSha
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("mihon.android.application")
|
||||||
id("com.mikepenz.aboutlibraries.plugin")
|
id("mihon.android.application.compose")
|
||||||
kotlin("android")
|
|
||||||
kotlin("plugin.serialization")
|
|
||||||
id("com.github.zellius.shortcut-helper")
|
id("com.github.zellius.shortcut-helper")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
alias(libs.plugins.aboutLibraries)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
|
if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
|
||||||
apply<com.google.gms.googleservices.GoogleServicesPlugin>()
|
pluginManager.apply {
|
||||||
|
apply(libs.plugins.google.services.get().pluginId)
|
||||||
|
apply(libs.plugins.firebase.crashlytics.get().pluginId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcutHelper.setFilePath("./shortcuts.xml")
|
shortcutHelper.setFilePath("./shortcuts.xml")
|
||||||
|
|
||||||
val SUPPORTED_ABIS = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "eu.kanade.tachiyomi"
|
namespace = "eu.kanade.tachiyomi"
|
||||||
@ -22,8 +28,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "app.mihon"
|
applicationId = "app.mihon"
|
||||||
|
|
||||||
versionCode = 1
|
versionCode = 8
|
||||||
versionName = "0.16.0"
|
versionName = "0.17.0"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
@ -32,7 +38,7 @@ android {
|
|||||||
buildConfigField("boolean", "PREVIEW", "false")
|
buildConfigField("boolean", "PREVIEW", "false")
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters += SUPPORTED_ABIS
|
abiFilters += supportedAbis
|
||||||
}
|
}
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@ -42,7 +48,7 @@ android {
|
|||||||
abi {
|
abi {
|
||||||
isEnable = true
|
isEnable = true
|
||||||
reset()
|
reset()
|
||||||
include(*SUPPORTED_ABIS.toTypedArray())
|
include(*supportedAbis.toTypedArray())
|
||||||
isUniversalApk = true
|
isUniversalApk = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,13 +108,16 @@ android {
|
|||||||
packaging {
|
packaging {
|
||||||
resources.excludes.addAll(
|
resources.excludes.addAll(
|
||||||
listOf(
|
listOf(
|
||||||
|
"kotlin-tooling-metadata.json",
|
||||||
"META-INF/DEPENDENCIES",
|
"META-INF/DEPENDENCIES",
|
||||||
"LICENSE.txt",
|
"LICENSE.txt",
|
||||||
"META-INF/LICENSE",
|
"META-INF/LICENSE",
|
||||||
"META-INF/LICENSE.txt",
|
"META-INF/**/LICENSE.txt",
|
||||||
|
"META-INF/*.properties",
|
||||||
|
"META-INF/**/*.properties",
|
||||||
"META-INF/README.md",
|
"META-INF/README.md",
|
||||||
"META-INF/NOTICE",
|
"META-INF/NOTICE",
|
||||||
"META-INF/*.kotlin_module",
|
"META-INF/*.version",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -119,7 +128,6 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
compose = true
|
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
|
|
||||||
// Disable some unused things
|
// Disable some unused things
|
||||||
@ -132,38 +140,32 @@ android {
|
|||||||
abortOnError = false
|
abortOnError = false
|
||||||
checkReleaseBuilds = false
|
checkReleaseBuilds = false
|
||||||
}
|
}
|
||||||
|
|
||||||
composeOptions {
|
|
||||||
kotlinCompilerExtensionVersion = compose.versions.compiler.get()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":i18n"))
|
implementation(projects.i18n)
|
||||||
implementation(project(":core"))
|
implementation(projects.core.archive)
|
||||||
implementation(project(":core-metadata"))
|
implementation(projects.core.common)
|
||||||
implementation(project(":source-api"))
|
implementation(projects.coreMetadata)
|
||||||
implementation(project(":source-local"))
|
implementation(projects.sourceApi)
|
||||||
implementation(project(":data"))
|
implementation(projects.sourceLocal)
|
||||||
implementation(project(":domain"))
|
implementation(projects.data)
|
||||||
implementation(project(":presentation-core"))
|
implementation(projects.domain)
|
||||||
implementation(project(":presentation-widget"))
|
implementation(projects.presentationCore)
|
||||||
|
implementation(projects.presentationWidget)
|
||||||
|
|
||||||
// Compose
|
// Compose
|
||||||
implementation(platform(compose.bom))
|
|
||||||
implementation(compose.activity)
|
implementation(compose.activity)
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.material3.core)
|
implementation(compose.material3.core)
|
||||||
implementation(compose.material.core)
|
|
||||||
implementation(compose.material.icons)
|
implementation(compose.material.icons)
|
||||||
implementation(compose.animation)
|
implementation(compose.animation)
|
||||||
implementation(compose.animation.graphics)
|
implementation(compose.animation.graphics)
|
||||||
debugImplementation(compose.ui.tooling)
|
debugImplementation(compose.ui.tooling)
|
||||||
implementation(compose.ui.tooling.preview)
|
implementation(compose.ui.tooling.preview)
|
||||||
implementation(compose.ui.util)
|
implementation(compose.ui.util)
|
||||||
implementation(compose.accompanist.webview)
|
|
||||||
implementation(compose.accompanist.systemuicontroller)
|
implementation(androidx.interpolator)
|
||||||
lintChecks(compose.lintchecks)
|
|
||||||
|
|
||||||
implementation(androidx.paging.runtime)
|
implementation(androidx.paging.runtime)
|
||||||
implementation(androidx.paging.compose)
|
implementation(androidx.paging.compose)
|
||||||
@ -209,13 +211,12 @@ dependencies {
|
|||||||
// Disk
|
// Disk
|
||||||
implementation(libs.disklrucache)
|
implementation(libs.disklrucache)
|
||||||
implementation(libs.unifile)
|
implementation(libs.unifile)
|
||||||
implementation(libs.junrar)
|
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
implementation(libs.preferencektx)
|
implementation(libs.preferencektx)
|
||||||
|
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
implementation(libs.injekt.core)
|
implementation(libs.injekt)
|
||||||
|
|
||||||
// Image loading
|
// Image loading
|
||||||
implementation(platform(libs.coil.bom))
|
implementation(platform(libs.coil.bom))
|
||||||
@ -238,12 +239,16 @@ dependencies {
|
|||||||
implementation(libs.bundles.voyager)
|
implementation(libs.bundles.voyager)
|
||||||
implementation(libs.compose.materialmotion)
|
implementation(libs.compose.materialmotion)
|
||||||
implementation(libs.swipe)
|
implementation(libs.swipe)
|
||||||
|
implementation(libs.compose.webview)
|
||||||
|
implementation(libs.compose.grid)
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
|
|
||||||
// Crash reports/analytics
|
// Crash reports/analytics
|
||||||
|
"standardImplementation"(platform(libs.firebase.bom))
|
||||||
"standardImplementation"(libs.firebase.analytics)
|
"standardImplementation"(libs.firebase.analytics)
|
||||||
|
"standardImplementation"(libs.firebase.crashlytics)
|
||||||
|
|
||||||
// Shizuku
|
// Shizuku
|
||||||
implementation(libs.bundles.shizuku)
|
implementation(libs.bundles.shizuku)
|
||||||
@ -254,6 +259,8 @@ dependencies {
|
|||||||
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
||||||
// debugImplementation(libs.leakcanary.android)
|
// debugImplementation(libs.leakcanary.android)
|
||||||
implementation(libs.leakcanary.plumber)
|
implementation(libs.leakcanary.plumber)
|
||||||
|
|
||||||
|
testImplementation(kotlinx.coroutines.test)
|
||||||
}
|
}
|
||||||
|
|
||||||
androidComponents {
|
androidComponents {
|
||||||
@ -275,7 +282,7 @@ androidComponents {
|
|||||||
tasks {
|
tasks {
|
||||||
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
||||||
withType<KotlinCompile> {
|
withType<KotlinCompile> {
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
compilerOptions.freeCompilerArgs.addAll(
|
||||||
"-Xcontext-receivers",
|
"-Xcontext-receivers",
|
||||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||||
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
|
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
|
||||||
@ -285,26 +292,12 @@ tasks {
|
|||||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||||
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
||||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
"-opt-in=coil3.annotation.ExperimentalCoilApi",
|
||||||
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
|
|
||||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||||
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||||
)
|
)
|
||||||
|
|
||||||
if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") {
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
|
||||||
"-P",
|
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
|
||||||
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
|
|
||||||
)
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
|
||||||
"-P",
|
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
|
||||||
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
-keep,allowoptimization class eu.kanade.**
|
-keep,allowoptimization class eu.kanade.**
|
||||||
-keep,allowoptimization class tachiyomi.**
|
-keep,allowoptimization class tachiyomi.**
|
||||||
|
-keep,allowoptimization class mihon.**
|
||||||
|
|
||||||
# Keep common dependencies used in extensions
|
# Keep common dependencies used in extensions
|
||||||
-keep,allowoptimization class androidx.preference.** { public protected *; }
|
-keep,allowoptimization class androidx.preference.** { public protected *; }
|
||||||
@ -43,6 +44,10 @@
|
|||||||
-dontnote rx.internal.util.PlatformDependent
|
-dontnote rx.internal.util.PlatformDependent
|
||||||
##---------------End: proguard configuration for RxJava 1.x ----------
|
##---------------End: proguard configuration for RxJava 1.x ----------
|
||||||
|
|
||||||
|
##---------------Begin: proguard configuration for okhttp ----------
|
||||||
|
-keepclasseswithmembers class okhttp3.MultipartBody$Builder { *; }
|
||||||
|
##---------------End: proguard configuration for okhttp ----------
|
||||||
|
|
||||||
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.** # core serialization annotations
|
-dontnote kotlinx.serialization.** # core serialization annotations
|
||||||
|
@ -3,18 +3,20 @@
|
|||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="432"
|
android:viewportWidth="432"
|
||||||
android:viewportHeight="432">
|
android:viewportHeight="432">
|
||||||
<group android:scaleX="0.67"
|
<group>
|
||||||
android:scaleY="0.67"
|
<clip-path
|
||||||
android:translateX="71.28"
|
android:pathData="M0,0h432v432h-432z"/>
|
||||||
android:translateY="71.28">
|
<path
|
||||||
|
android:pathData="M0,0h432v432h-432z"
|
||||||
|
android:fillColor="#FAFAFA"/>
|
||||||
<path
|
<path
|
||||||
android:pathData="M0,0h432v432h-432z"
|
android:pathData="M0,0h432v432h-432z"
|
||||||
android:fillColor="#2E3943"/>
|
android:fillColor="#2E3943"/>
|
||||||
<path
|
<path
|
||||||
android:pathData="M377,216C377,302.16 304.92,372 216,372C127.08,372 55,302.16 55,216C55,129.84 127.08,60 216,60C304.92,60 377,129.84 377,216Z"
|
android:pathData="M322.13,215.5C322.13,272.66 274.64,319 216.07,319C157.49,319 110,272.66 110,215.5C110,158.34 157.49,112 216.07,112C274.64,112 322.13,158.34 322.13,215.5Z"
|
||||||
android:fillColor="#F2FAFF"/>
|
android:fillColor="#F2FAFF"/>
|
||||||
<path
|
<path
|
||||||
android:pathData="M216,342.75C288.25,342.75 346.81,286 346.81,216C346.81,146 288.25,89.25 216,89.25C143.75,89.25 85.19,146 85.19,216C85.19,286 143.75,342.75 216,342.75ZM216,372C304.92,372 377,302.16 377,216C377,129.84 304.92,60 216,60C127.08,60 55,129.84 55,216C55,302.16 127.08,372 216,372Z"
|
android:pathData="M216.07,299.59C263.66,299.59 302.24,261.94 302.24,215.5C302.24,169.06 263.66,131.41 216.07,131.41C168.47,131.41 129.89,169.06 129.89,215.5C129.89,261.94 168.47,299.59 216.07,299.59ZM216.07,319C274.64,319 322.13,272.66 322.13,215.5C322.13,158.34 274.64,112 216.07,112C157.49,112 110,158.34 110,215.5C110,272.66 157.49,319 216.07,319Z"
|
||||||
android:fillColor="#7EBBED"
|
android:fillColor="#7EBBED"
|
||||||
android:fillType="evenOdd"/>
|
android:fillType="evenOdd"/>
|
||||||
</group>
|
</group>
|
||||||
|
@ -3,12 +3,7 @@
|
|||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="432"
|
android:viewportWidth="432"
|
||||||
android:viewportHeight="432">
|
android:viewportHeight="432">
|
||||||
<group android:scaleX="0.67"
|
<path
|
||||||
android:scaleY="0.67"
|
android:pathData="M182.03,188.7L181.33,172.69C183.42,173.09 185.91,173.19 191.57,173.19C198.44,173.19 207.49,172.79 212.16,172.19C214.15,171.99 214.95,171.7 216.24,171L226.98,180.15C225.98,181.54 225.68,182.14 224.59,184.92C223.7,187.11 219.62,199.74 218.03,205.11C225.39,206.6 229.46,207.7 235.03,209.98C235.73,205.11 235.83,202.52 235.83,193.67C235.83,191.39 235.73,190.09 235.43,188.01L252.74,188.6C252.24,190.99 252.14,191.98 252.04,195.86C251.64,205.21 251.24,209.68 250.25,216.45C257.11,219.93 257.11,219.93 260.59,221.82C262.38,222.81 262.78,223.01 263.97,223.41L258.2,242.01C255.42,239.52 251.54,236.83 245.87,233.65C240.9,245.49 232.65,254.14 220.12,261C215.94,255.43 212.76,252.05 207.68,248.07C215.04,244.59 218.43,242.4 222.3,238.72C226.08,235.04 228.57,231.46 230.96,226.09C224.59,223.21 220.51,221.92 213.45,220.43C209.38,232.56 206.09,240.32 203.21,244.99C199.33,251.25 194.06,254.54 187.99,254.54C183.32,254.54 178.55,252.45 175.07,248.87C171.09,244.79 169,239.12 169,232.56C169,222.81 173.67,214.36 181.83,209.09C187.1,205.71 192.67,204.21 201.52,203.72C203.31,197.85 204.8,192.78 206.19,187.11C201.82,187.51 196.35,187.81 189.68,188.1C186.1,188.2 184.91,188.3 182.03,188.7ZM197.14,218.93C192.47,219.73 189.68,221.22 187.2,224.4C185.31,226.59 184.41,229.18 184.41,231.96C184.41,235.04 185.91,237.33 187.8,237.33C190.08,237.33 192.67,232.16 197.14,218.93Z"
|
||||||
android:translateX="71.28"
|
android:fillColor="#031019"/>
|
||||||
android:translateY="71.28">
|
|
||||||
<path
|
|
||||||
android:pathData="M162.3,173.59L161.22,148.63C164.47,149.25 168.35,149.41 177.18,149.41C187.87,149.41 201.98,148.79 209.26,147.86C212.36,147.55 213.6,147.09 215.61,146L232.35,160.26C230.8,162.43 230.34,163.36 228.63,167.7C227.24,171.11 220.88,190.79 218.4,199.16C229.87,201.48 236.22,203.18 244.9,206.75C245.99,199.16 246.14,195.13 246.14,181.33C246.14,177.77 245.99,175.76 245.52,172.5L272.49,173.43C271.71,177.15 271.56,178.7 271.4,184.74C270.78,199.31 270.16,206.29 268.61,216.82C279.31,222.25 279.31,222.25 284.73,225.19C287.52,226.74 288.14,227.05 290,227.67L281.01,256.65C276.67,252.78 270.63,248.59 261.8,243.63C254.05,262.08 241.18,275.56 221.66,286.25C215.15,277.57 210.19,272.3 202.29,266.11C213.75,260.68 219.02,257.27 225.07,251.54C230.96,245.8 234.83,240.22 238.55,231.85C228.63,227.36 222.28,225.35 211.27,223.02C204.92,241.93 199.8,254.02 195.31,261.3C189.27,271.06 181.05,276.18 171.6,276.18C164.32,276.18 156.88,272.92 151.45,267.35C145.25,260.99 142,252.16 142,241.93C142,226.74 149.28,213.57 161.99,205.35C170.21,200.09 178.88,197.76 192.68,196.99C195.47,187.84 197.79,179.94 199.96,171.11C193.14,171.73 184.62,172.19 174.24,172.65C168.66,172.81 166.8,172.96 162.3,173.59ZM185.86,220.7C178.57,221.94 174.24,224.26 170.36,229.22C167.42,232.63 166.02,236.66 166.02,241C166.02,245.8 168.35,249.37 171.29,249.37C174.85,249.37 178.88,241.31 185.86,220.7Z"
|
|
||||||
android:fillColor="#031019"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
</vector>
|
||||||
|
11
app/src/dev/java/mihon/core/firebase/FirebaseConfig.kt
Normal file
11
app/src/dev/java/mihon/core/firebase/FirebaseConfig.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package mihon.core.firebase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
object FirebaseConfig {
|
||||||
|
fun init(context: Context) = Unit
|
||||||
|
|
||||||
|
fun setAnalyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
|
||||||
|
fun setCrashlyticsEnabled(enabled: Boolean) = Unit
|
||||||
|
}
|
@ -33,11 +33,6 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
<!-- Remove permission from Firebase dependency -->
|
|
||||||
<uses-permission
|
|
||||||
android:name="com.google.android.gms.permission.AD_ID"
|
|
||||||
tools:node="remove" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
@ -236,11 +231,6 @@
|
|||||||
android:name="android.webkit.WebView.MetricsOptOut"
|
android:name="android.webkit.WebView.MetricsOptOut"
|
||||||
android:value="true" />
|
android:value="true" />
|
||||||
|
|
||||||
<!-- Disable advertising ID collection for Firebase -->
|
|
||||||
<meta-data
|
|
||||||
android:name="google_analytics_adid_collection_enabled"
|
|
||||||
android:value="false" />
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
Binary file not shown.
Before ![]() (image error) Size: 22 KiB |
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.core.preference
|
package eu.kanade.core.preference
|
||||||
|
|
||||||
import androidx.compose.ui.state.ToggleableState
|
import androidx.compose.ui.state.ToggleableState
|
||||||
import tachiyomi.core.preference.CheckboxState
|
import tachiyomi.core.common.preference.CheckboxState
|
||||||
|
|
||||||
fun <T> CheckboxState.TriState<T>.asToggleableState() = when (this) {
|
fun <T> CheckboxState.TriState<T>.asToggleableState() = when (this) {
|
||||||
is CheckboxState.TriState.Exclude -> ToggleableState.Indeterminate
|
is CheckboxState.TriState.Exclude -> ToggleableState.Indeterminate
|
||||||
|
@ -5,7 +5,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
|
||||||
class PreferenceMutableState<T>(
|
class PreferenceMutableState<T>(
|
||||||
private val preference: Preference<T>,
|
private val preference: Preference<T>,
|
||||||
|
@ -5,7 +5,7 @@ import kotlin.contracts.ExperimentalContracts
|
|||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
fun <T : R, R : Any> List<T>.insertSeparators(
|
fun <T : R, R : Any> List<T>.insertSeparators(
|
||||||
generator: (T?, T?) -> R?,
|
generator: (before: T?, after: T?) -> R?,
|
||||||
): List<R> {
|
): List<R> {
|
||||||
if (isEmpty()) return emptyList()
|
if (isEmpty()) return emptyList()
|
||||||
val newList = mutableListOf<R>()
|
val newList = mutableListOf<R>()
|
||||||
@ -19,6 +19,24 @@ fun <T : R, R : Any> List<T>.insertSeparators(
|
|||||||
return newList
|
return newList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to [eu.kanade.core.util.insertSeparators] but iterates from last to first element
|
||||||
|
*/
|
||||||
|
fun <T : R, R : Any> List<T>.insertSeparatorsReversed(
|
||||||
|
generator: (before: T?, after: T?) -> R?,
|
||||||
|
): List<R> {
|
||||||
|
if (isEmpty()) return emptyList()
|
||||||
|
val newList = mutableListOf<R>()
|
||||||
|
for (i in size downTo 0) {
|
||||||
|
val after = getOrNull(i)
|
||||||
|
after?.let(newList::add)
|
||||||
|
val before = getOrNull(i - 1)
|
||||||
|
val separator = generator.invoke(before, after)
|
||||||
|
separator?.let(newList::add)
|
||||||
|
}
|
||||||
|
return newList.asReversed()
|
||||||
|
}
|
||||||
|
|
||||||
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
|
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
add(value)
|
add(value)
|
||||||
|
13
app/src/main/java/eu/kanade/core/util/SourceUtil.kt
Normal file
13
app/src/main/java/eu/kanade/core/util/SourceUtil.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package eu.kanade.core.util
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ifSourcesLoaded(): Boolean {
|
||||||
|
return remember { Injekt.get<SourceManager>().isInitialized }.collectAsState().value
|
||||||
|
}
|
@ -4,10 +4,7 @@ import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
|
|||||||
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
import eu.kanade.domain.download.interactor.DeleteDownload
|
import eu.kanade.domain.download.interactor.DeleteDownload
|
||||||
import eu.kanade.domain.extension.interactor.CreateExtensionRepo
|
|
||||||
import eu.kanade.domain.extension.interactor.DeleteExtensionRepo
|
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionRepos
|
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionSources
|
import eu.kanade.domain.extension.interactor.GetExtensionSources
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionsByType
|
import eu.kanade.domain.extension.interactor.GetExtensionsByType
|
||||||
import eu.kanade.domain.extension.interactor.TrustExtension
|
import eu.kanade.domain.extension.interactor.TrustExtension
|
||||||
@ -26,6 +23,17 @@ import eu.kanade.domain.track.interactor.AddTracks
|
|||||||
import eu.kanade.domain.track.interactor.RefreshTracks
|
import eu.kanade.domain.track.interactor.RefreshTracks
|
||||||
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
|
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
|
||||||
import eu.kanade.domain.track.interactor.TrackChapter
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
|
import mihon.data.repository.ExtensionRepoRepositoryImpl
|
||||||
|
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
||||||
|
import mihon.domain.extensionrepo.interactor.CreateExtensionRepo
|
||||||
|
import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo
|
||||||
|
import mihon.domain.extensionrepo.interactor.GetExtensionRepo
|
||||||
|
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
|
||||||
|
import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
|
||||||
|
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
|
||||||
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
|
import mihon.domain.extensionrepo.service.ExtensionRepoService
|
||||||
|
import mihon.domain.upcoming.interactor.GetUpcomingManga
|
||||||
import tachiyomi.data.category.CategoryRepositoryImpl
|
import tachiyomi.data.category.CategoryRepositoryImpl
|
||||||
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
||||||
import tachiyomi.data.history.HistoryRepositoryImpl
|
import tachiyomi.data.history.HistoryRepositoryImpl
|
||||||
@ -111,6 +119,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { GetMangaByUrlAndSourceId(get()) }
|
addFactory { GetMangaByUrlAndSourceId(get()) }
|
||||||
addFactory { GetManga(get()) }
|
addFactory { GetManga(get()) }
|
||||||
addFactory { GetNextChapters(get(), get(), get()) }
|
addFactory { GetNextChapters(get(), get(), get()) }
|
||||||
|
addFactory { GetUpcomingManga(get()) }
|
||||||
addFactory { ResetViewerFlags(get()) }
|
addFactory { ResetViewerFlags(get()) }
|
||||||
addFactory { SetMangaChapterFlags(get()) }
|
addFactory { SetMangaChapterFlags(get()) }
|
||||||
addFactory { FetchInterval(get()) }
|
addFactory { FetchInterval(get()) }
|
||||||
@ -144,6 +153,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { ShouldUpdateDbChapter() }
|
addFactory { ShouldUpdateDbChapter() }
|
||||||
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) }
|
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) }
|
||||||
addFactory { GetAvailableScanlators(get()) }
|
addFactory { GetAvailableScanlators(get()) }
|
||||||
|
addFactory { FilterChaptersForDownload(get(), get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||||
addFactory { GetHistory(get()) }
|
addFactory { GetHistory(get()) }
|
||||||
@ -171,10 +181,15 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
addFactory { TrustExtension(get()) }
|
addFactory { TrustExtension(get(), get()) }
|
||||||
|
|
||||||
addFactory { CreateExtensionRepo(get()) }
|
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||||
|
addFactory { ExtensionRepoService(get(), get()) }
|
||||||
|
addFactory { GetExtensionRepo(get()) }
|
||||||
|
addFactory { GetExtensionRepoCount(get()) }
|
||||||
|
addFactory { CreateExtensionRepo(get(), get()) }
|
||||||
addFactory { DeleteExtensionRepo(get()) }
|
addFactory { DeleteExtensionRepo(get()) }
|
||||||
addFactory { GetExtensionRepos(get()) }
|
addFactory { ReplaceExtensionRepo(get()) }
|
||||||
|
addFactory { UpdateExtensionRepo(get(), get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package eu.kanade.domain.base
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
class BasePreferences(
|
class BasePreferences(
|
||||||
@ -28,4 +28,6 @@ class BasePreferences(
|
|||||||
SHIZUKU(MR.strings.ext_installer_shizuku, false),
|
SHIZUKU(MR.strings.ext_installer_shizuku, false),
|
||||||
PRIVATE(MR.strings.ext_installer_private, false),
|
PRIVATE(MR.strings.ext_installer_private, false),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@ import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
|
|||||||
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
|
||||||
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.preference.getEnum
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
|
||||||
class ExtensionInstallerPreference(
|
class ExtensionInstallerPreference(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
@ -2,8 +2,8 @@ package eu.kanade.domain.chapter.interactor
|
|||||||
|
|
||||||
import eu.kanade.domain.download.interactor.DeleteDownload
|
import eu.kanade.domain.download.interactor.DeleteDownload
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.model.ChapterUpdate
|
import tachiyomi.domain.chapter.model.ChapterUpdate
|
||||||
import tachiyomi.domain.chapter.repository.ChapterRepository
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
|
@ -110,7 +110,10 @@ class SyncChaptersWithSource(
|
|||||||
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
||||||
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
||||||
downloadManager.isChapterDownloaded(
|
downloadManager.isChapterDownloaded(
|
||||||
dbChapter.name, dbChapter.scanlator, manga.title, manga.source,
|
dbChapter.name,
|
||||||
|
dbChapter.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (shouldRenameChapter) {
|
if (shouldRenameChapter) {
|
||||||
@ -163,7 +166,7 @@ class SyncChaptersWithSource(
|
|||||||
var updatedToAdd = newChapters.map { toAddItem ->
|
var updatedToAdd = newChapters.map { toAddItem ->
|
||||||
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||||
|
|
||||||
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||||
|
|
||||||
chapter = chapter.copy(
|
chapter = chapter.copy(
|
||||||
read = chapter.chapterNumber in deletedReadChapterNumbers,
|
read = chapter.chapterNumber in deletedReadChapterNumbers,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.download.interactor
|
package eu.kanade.domain.download.interactor
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package eu.kanade.domain.extension.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import tachiyomi.core.preference.plusAssign
|
|
||||||
|
|
||||||
class CreateExtensionRepo(private val preferences: SourcePreferences) {
|
|
||||||
|
|
||||||
fun await(name: String): Result {
|
|
||||||
// Do not allow invalid formats
|
|
||||||
if (!name.matches(repoRegex)) {
|
|
||||||
return Result.InvalidUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.extensionRepos() += name.removeSuffix("/index.min.json")
|
|
||||||
|
|
||||||
return Result.Success
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface Result {
|
|
||||||
data object InvalidUrl : Result
|
|
||||||
data object Success : Result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val repoRegex = """^https://.*/index\.min\.json$""".toRegex()
|
|
@ -1,11 +0,0 @@
|
|||||||
package eu.kanade.domain.extension.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import tachiyomi.core.preference.minusAssign
|
|
||||||
|
|
||||||
class DeleteExtensionRepo(private val preferences: SourcePreferences) {
|
|
||||||
|
|
||||||
fun await(repo: String) {
|
|
||||||
preferences.extensionRepos() -= repo
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package eu.kanade.domain.extension.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
class GetExtensionRepos(private val preferences: SourcePreferences) {
|
|
||||||
|
|
||||||
fun subscribe(): Flow<Set<String>> {
|
|
||||||
return preferences.extensionRepos().changes()
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ class GetExtensionSources(
|
|||||||
ExtensionSourceItem(
|
ExtensionSourceItem(
|
||||||
source = source,
|
source = source,
|
||||||
enabled = source.isEnabled(),
|
enabled = source.isEnabled(),
|
||||||
labelAsName = isMultiSource && isMultiLangSingleSource.not(),
|
labelAsName = isMultiSource && !isMultiLangSingleSource,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,11 @@ class GetExtensionsByType(
|
|||||||
extensionManager.installedExtensionsFlow,
|
extensionManager.installedExtensionsFlow,
|
||||||
extensionManager.untrustedExtensionsFlow,
|
extensionManager.untrustedExtensionsFlow,
|
||||||
extensionManager.availableExtensionsFlow,
|
extensionManager.availableExtensionsFlow,
|
||||||
) { _activeLanguages, _installed, _untrusted, _available ->
|
) { enabledLanguages, _installed, _untrusted, _available ->
|
||||||
val (updates, installed) = _installed
|
val (updates, installed) = _installed
|
||||||
.filter { (showNsfwSources || it.isNsfw.not()) }
|
.filter { (showNsfwSources || !it.isNsfw) }
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareBy<Extension.Installed> { it.isObsolete.not() }
|
compareBy<Extension.Installed> { !it.isObsolete }
|
||||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||||
)
|
)
|
||||||
.partition { it.hasUpdate }
|
.partition { it.hasUpdate }
|
||||||
@ -36,13 +36,13 @@ class GetExtensionsByType(
|
|||||||
.filter { extension ->
|
.filter { extension ->
|
||||||
_installed.none { it.pkgName == extension.pkgName } &&
|
_installed.none { it.pkgName == extension.pkgName } &&
|
||||||
_untrusted.none { it.pkgName == extension.pkgName } &&
|
_untrusted.none { it.pkgName == extension.pkgName } &&
|
||||||
(showNsfwSources || extension.isNsfw.not())
|
(showNsfwSources || !extension.isNsfw)
|
||||||
}
|
}
|
||||||
.flatMap { ext ->
|
.flatMap { ext ->
|
||||||
if (ext.sources.isEmpty()) {
|
if (ext.sources.isEmpty()) {
|
||||||
return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList()
|
return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList()
|
||||||
}
|
}
|
||||||
ext.sources.filter { it.lang in _activeLanguages }
|
ext.sources.filter { it.lang in enabledLanguages }
|
||||||
.map {
|
.map {
|
||||||
ext.copy(
|
ext.copy(
|
||||||
name = it.name,
|
name = it.name,
|
||||||
|
@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import tachiyomi.core.preference.getAndSet
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
class TrustExtension(
|
class TrustExtension(
|
||||||
|
private val extensionRepoRepository: ExtensionRepoRepository,
|
||||||
private val preferences: SourcePreferences,
|
private val preferences: SourcePreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean {
|
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
|
||||||
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash"
|
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
|
||||||
return key in preferences.trustedExtensions().get()
|
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
|
||||||
|
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
|
||||||
@ -19,9 +22,7 @@ class TrustExtension(
|
|||||||
// Remove previously trusted versions
|
// Remove previously trusted versions
|
||||||
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
||||||
|
|
||||||
removed.also {
|
removed.also { it += "$pkgName:$versionCode:$signatureHash" }
|
||||||
it += "$pkgName:$versionCode:$signatureHash"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
|
|||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||||
|
import tachiyomi.core.common.preference.TriState
|
||||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||||
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
|
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
|
||||||
import tachiyomi.core.preference.TriState
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -95,7 +95,13 @@ fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
|||||||
/**
|
/**
|
||||||
* Creates a ComicInfo instance based on the manga and chapter metadata.
|
* Creates a ComicInfo instance based on the manga and chapter metadata.
|
||||||
*/
|
*/
|
||||||
fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: List<String>?) = ComicInfo(
|
fun getComicInfo(
|
||||||
|
manga: Manga,
|
||||||
|
chapter: Chapter,
|
||||||
|
urls: List<String>,
|
||||||
|
categories: List<String>?,
|
||||||
|
sourceName: String,
|
||||||
|
) = ComicInfo(
|
||||||
title = ComicInfo.Title(chapter.name),
|
title = ComicInfo.Title(chapter.name),
|
||||||
series = ComicInfo.Series(manga.title),
|
series = ComicInfo.Series(manga.title),
|
||||||
number = chapter.chapterNumber.takeIf { it >= 0 }?.let {
|
number = chapter.chapterNumber.takeIf { it >= 0 }?.let {
|
||||||
@ -105,7 +111,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories:
|
|||||||
ComicInfo.Number(it.toString())
|
ComicInfo.Number(it.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
web = ComicInfo.Web(chapterUrl),
|
web = ComicInfo.Web(urls.joinToString(" ")),
|
||||||
summary = manga.description?.let { ComicInfo.Summary(it) },
|
summary = manga.description?.let { ComicInfo.Summary(it) },
|
||||||
writer = manga.author?.let { ComicInfo.Writer(it) },
|
writer = manga.author?.let { ComicInfo.Writer(it) },
|
||||||
penciller = manga.artist?.let { ComicInfo.Penciller(it) },
|
penciller = manga.artist?.let { ComicInfo.Penciller(it) },
|
||||||
@ -115,6 +121,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories:
|
|||||||
ComicInfoPublishingStatus.toComicInfoValue(manga.status),
|
ComicInfoPublishingStatus.toComicInfoValue(manga.status),
|
||||||
),
|
),
|
||||||
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
|
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
|
||||||
|
source = ComicInfo.SourceMihon(sourceName),
|
||||||
inker = null,
|
inker = null,
|
||||||
colorist = null,
|
colorist = null,
|
||||||
letterer = null,
|
letterer = null,
|
||||||
|
@ -3,7 +3,7 @@ package eu.kanade.domain.source.interactor
|
|||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import tachiyomi.core.util.lang.compareToWithCollator
|
import tachiyomi.core.common.util.lang.compareToWithCollator
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.domain.source.repository.SourceRepository
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
@ -34,15 +34,15 @@ class GetSourcesWithFavoriteCount(
|
|||||||
when (sorting) {
|
when (sorting) {
|
||||||
SetMigrateSorting.Mode.ALPHABETICAL -> {
|
SetMigrateSorting.Mode.ALPHABETICAL -> {
|
||||||
when {
|
when {
|
||||||
a.first.isStub && b.first.isStub.not() -> -1
|
a.first.isStub && !b.first.isStub -> -1
|
||||||
b.first.isStub && a.first.isStub.not() -> 1
|
b.first.isStub && !a.first.isStub -> 1
|
||||||
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
|
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetMigrateSorting.Mode.TOTAL -> {
|
SetMigrateSorting.Mode.TOTAL -> {
|
||||||
when {
|
when {
|
||||||
a.first.isStub && b.first.isStub.not() -> -1
|
a.first.isStub && !b.first.isStub -> -1
|
||||||
b.first.isStub && a.first.isStub.not() -> 1
|
b.first.isStub && !a.first.isStub -> 1
|
||||||
else -> a.second.compareTo(b.second)
|
else -> a.second.compareTo(b.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import tachiyomi.core.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
class ToggleLanguage(
|
class ToggleLanguage(
|
||||||
val preferences: SourcePreferences,
|
val preferences: SourcePreferences,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import tachiyomi.core.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
|
|
||||||
class ToggleSource(
|
class ToggleSource(
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import tachiyomi.core.preference.getAndSet
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
|
|
||||||
class ToggleSourcePin(
|
class ToggleSourcePin(
|
||||||
|
@ -2,9 +2,9 @@ package eu.kanade.domain.source.service
|
|||||||
|
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.preference.getEnum
|
import tachiyomi.core.common.preference.getEnum
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
|
|
||||||
class SourcePreferences(
|
class SourcePreferences(
|
||||||
@ -48,4 +48,9 @@ class SourcePreferences(
|
|||||||
Preference.appStateKey("trusted_extensions"),
|
Preference.appStateKey("trusted_extensions"),
|
||||||
emptySet(),
|
emptySet(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun globalSearchFilterState() = preferenceStore.getBoolean(
|
||||||
|
Preference.appStateKey("has_filters_toggle_state"),
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,26 @@ import eu.kanade.domain.track.model.toDomainTrack
|
|||||||
import eu.kanade.tachiyomi.data.database.models.Track
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
||||||
import eu.kanade.tachiyomi.data.track.Tracker
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
import tachiyomi.domain.history.interactor.GetHistory
|
import tachiyomi.domain.history.interactor.GetHistory
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
|
||||||
import tachiyomi.domain.track.interactor.InsertTrack
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
class AddTracks(
|
class AddTracks(
|
||||||
private val getTracks: GetTracks,
|
|
||||||
private val insertTrack: InsertTrack,
|
private val insertTrack: InsertTrack,
|
||||||
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
||||||
private val getChaptersByMangaId: GetChaptersByMangaId,
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
private val trackerManager: TrackerManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// TODO: update all trackers based on common data
|
// TODO: update all trackers based on common data
|
||||||
@ -79,7 +79,7 @@ class AddTracks(
|
|||||||
|
|
||||||
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
|
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
|
||||||
withIOContext {
|
withIOContext {
|
||||||
getTracks.await(manga.id)
|
trackerManager.loggedInTrackers()
|
||||||
.filterIsInstance<EnhancedTracker>()
|
.filterIsInstance<EnhancedTracker>()
|
||||||
.filter { it.accept(source) }
|
.filter { it.accept(source) }
|
||||||
.forEach { service ->
|
.forEach { service ->
|
||||||
@ -87,11 +87,11 @@ class AddTracks(
|
|||||||
service.match(manga)?.let { track ->
|
service.match(manga)?.let { track ->
|
||||||
track.manga_id = manga.id
|
track.manga_id = manga.id
|
||||||
(service as Tracker).bind(track)
|
(service as Tracker).bind(track)
|
||||||
insertTrack.await(track.toDomainTrack()!!)
|
insertTrack.await(track.toDomainTrack(idRequired = false)!!)
|
||||||
|
|
||||||
syncChapterProgressWithTrack.await(
|
syncChapterProgressWithTrack.await(
|
||||||
manga.id,
|
manga.id,
|
||||||
track.toDomainTrack()!!,
|
track.toDomainTrack(idRequired = false)!!,
|
||||||
service,
|
service,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ class RefreshTracks(
|
|||||||
.map { (track, service) ->
|
.map { (track, service) ->
|
||||||
async {
|
async {
|
||||||
return@async try {
|
return@async try {
|
||||||
val updatedTrack = service!!.refresh(track.toDbTrack())
|
val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
|
||||||
insertTrack.await(updatedTrack.toDomainTrack()!!)
|
insertTrack.await(updatedTrack)
|
||||||
syncChapterProgressWithTrack.await(mangaId, track, service)
|
syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
|
||||||
null
|
null
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
service to e
|
service to e
|
||||||
|
@ -4,12 +4,13 @@ import eu.kanade.domain.track.model.toDbTrack
|
|||||||
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
||||||
import eu.kanade.tachiyomi.data.track.Tracker
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
import tachiyomi.domain.chapter.model.toChapterUpdate
|
import tachiyomi.domain.chapter.model.toChapterUpdate
|
||||||
import tachiyomi.domain.track.interactor.InsertTrack
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
import tachiyomi.domain.track.model.Track
|
import tachiyomi.domain.track.model.Track
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
class SyncChapterProgressWithTrack(
|
class SyncChapterProgressWithTrack(
|
||||||
private val updateChapter: UpdateChapter,
|
private val updateChapter: UpdateChapter,
|
||||||
@ -36,7 +37,8 @@ class SyncChapterProgressWithTrack(
|
|||||||
|
|
||||||
// only take into account continuous reading
|
// only take into account continuous reading
|
||||||
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
|
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
|
||||||
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
|
val lastRead = max(remoteTrack.lastChapterRead, localLastRead.toDouble())
|
||||||
|
val updatedTrack = remoteTrack.copy(lastChapterRead = lastRead)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tracker.update(updatedTrack.toDbTrack())
|
tracker.update(updatedTrack.toDbTrack())
|
||||||
|
@ -9,8 +9,8 @@ import eu.kanade.tachiyomi.data.track.TrackerManager
|
|||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
import tachiyomi.domain.track.interactor.InsertTrack
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ class TrackChapter(
|
|||||||
private val delayedTrackingStore: DelayedTrackingStore,
|
private val delayedTrackingStore: DelayedTrackingStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) {
|
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) {
|
||||||
withNonCancellableContext {
|
withNonCancellableContext {
|
||||||
val tracks = getTracks.await(mangaId)
|
val tracks = getTracks.await(mangaId)
|
||||||
if (tracks.isEmpty()) return@withNonCancellableContext
|
if (tracks.isEmpty()) return@withNonCancellableContext
|
||||||
@ -43,7 +43,9 @@ class TrackChapter(
|
|||||||
delayedTrackingStore.remove(track.id)
|
delayedTrackingStore.remove(track.id)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
delayedTrackingStore.add(track.id, chapterNumber)
|
delayedTrackingStore.add(track.id, chapterNumber)
|
||||||
DelayedTrackingUpdateJob.setupTask(context)
|
if (setupJobOnFailure) {
|
||||||
|
DelayedTrackingUpdateJob.setupTask(context)
|
||||||
|
}
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package eu.kanade.domain.track.model
|
||||||
|
|
||||||
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
|
enum class AutoTrackState(val titleRes: StringResource) {
|
||||||
|
ALWAYS(MR.strings.lock_always),
|
||||||
|
ASK(MR.strings.default_category_summary),
|
||||||
|
NEVER(MR.strings.lock_never),
|
||||||
|
}
|
@ -19,30 +19,28 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
|
|||||||
it.remote_id = remoteId
|
it.remote_id = remoteId
|
||||||
it.library_id = libraryId
|
it.library_id = libraryId
|
||||||
it.title = title
|
it.title = title
|
||||||
it.last_chapter_read = lastChapterRead.toFloat()
|
it.last_chapter_read = lastChapterRead
|
||||||
it.total_chapters = totalChapters.toInt()
|
it.total_chapters = totalChapters
|
||||||
it.status = status.toInt()
|
it.status = status
|
||||||
it.score = score.toFloat()
|
it.score = score
|
||||||
it.tracking_url = remoteUrl
|
it.tracking_url = remoteUrl
|
||||||
it.started_reading_date = startDate
|
it.started_reading_date = startDate
|
||||||
it.finished_reading_date = finishDate
|
it.finished_reading_date = finishDate
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||||
val trackId = id ?: if (idRequired.not()) -1 else return null
|
val trackId = id ?: if (!idRequired) -1 else return null
|
||||||
return Track(
|
return Track(
|
||||||
id = trackId,
|
id = trackId,
|
||||||
mangaId = manga_id,
|
mangaId = manga_id,
|
||||||
trackerId = tracker_id.toLong(),
|
trackerId = tracker_id,
|
||||||
remoteId = remote_id,
|
remoteId = remote_id,
|
||||||
libraryId = library_id,
|
libraryId = library_id,
|
||||||
title = title,
|
title = title,
|
||||||
lastChapterRead = last_chapter_read.toDouble(),
|
lastChapterRead = last_chapter_read,
|
||||||
totalChapters = total_chapters.toLong(),
|
totalChapters = total_chapters,
|
||||||
status = status.toLong(),
|
status = status,
|
||||||
// Jank workaround due to precision issues while converting
|
score = score,
|
||||||
// See https://github.com/tachiyomiorg/tachiyomi/issues/10343
|
|
||||||
score = score.toString().toDouble(),
|
|
||||||
remoteUrl = tracking_url,
|
remoteUrl = tracking_url,
|
||||||
startDate = started_reading_date,
|
startDate = started_reading_date,
|
||||||
finishDate = finished_reading_date,
|
finishDate = finished_reading_date,
|
||||||
|
@ -12,8 +12,8 @@ import eu.kanade.domain.track.interactor.TrackChapter
|
|||||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
import eu.kanade.tachiyomi.util.system.workManager
|
import eu.kanade.tachiyomi.util.system.workManager
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -45,7 +45,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke
|
|||||||
logcat(LogPriority.DEBUG) {
|
logcat(LogPriority.DEBUG) {
|
||||||
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
|
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
|
||||||
}
|
}
|
||||||
trackChapter.await(context, track.mangaId, track.lastChapterRead)
|
trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package eu.kanade.domain.track.service
|
package eu.kanade.domain.track.service
|
||||||
|
|
||||||
|
import eu.kanade.domain.track.model.AutoTrackState
|
||||||
import eu.kanade.tachiyomi.data.track.Tracker
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
import eu.kanade.tachiyomi.data.track.anilist.Anilist
|
||||||
import tachiyomi.core.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
|
||||||
class TrackPreferences(
|
class TrackPreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
@ -19,9 +21,15 @@ class TrackPreferences(
|
|||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun trackAuthExpired(tracker: Tracker) = preferenceStore.getBoolean(
|
||||||
|
Preference.privateKey("pref_tracker_auth_expired_${tracker.id}"),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
fun setCredentials(tracker: Tracker, username: String, password: String) {
|
fun setCredentials(tracker: Tracker, username: String, password: String) {
|
||||||
trackUsername(tracker).set(username)
|
trackUsername(tracker).set(username)
|
||||||
trackPassword(tracker).set(password)
|
trackPassword(tracker).set(password)
|
||||||
|
trackAuthExpired(tracker).set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
|
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
|
||||||
@ -29,4 +37,9 @@ class TrackPreferences(
|
|||||||
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
|
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
|
||||||
|
|
||||||
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
|
||||||
|
|
||||||
|
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum(
|
||||||
|
"pref_auto_update_manga_on_mark_read",
|
||||||
|
AutoTrackState.ALWAYS,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package eu.kanade.domain.track.store
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
|
||||||
class DelayedTrackingStore(context: Context) {
|
class DelayedTrackingStore(context: Context) {
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@ import eu.kanade.domain.ui.model.TabletUiMode
|
|||||||
import eu.kanade.domain.ui.model.ThemeMode
|
import eu.kanade.domain.ui.model.ThemeMode
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.preference.getEnum
|
import tachiyomi.core.common.preference.getEnum
|
||||||
import java.text.DateFormat
|
import java.time.format.DateTimeFormatter
|
||||||
import java.text.SimpleDateFormat
|
import java.time.format.FormatStyle
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class UiPreferences(
|
class UiPreferences(
|
||||||
@ -19,7 +19,11 @@ class UiPreferences(
|
|||||||
|
|
||||||
fun appTheme() = preferenceStore.getEnum(
|
fun appTheme() = preferenceStore.getEnum(
|
||||||
"pref_app_theme",
|
"pref_app_theme",
|
||||||
if (DeviceUtil.isDynamicColorAvailable) { AppTheme.MONET } else { AppTheme.DEFAULT },
|
if (DeviceUtil.isDynamicColorAvailable) {
|
||||||
|
AppTheme.MONET
|
||||||
|
} else {
|
||||||
|
AppTheme.DEFAULT
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
|
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
|
||||||
@ -31,9 +35,9 @@ class UiPreferences(
|
|||||||
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
|
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun dateFormat(format: String): DateFormat = when (format) {
|
fun dateFormat(format: String): DateTimeFormatter = when (format) {
|
||||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
"" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
|
||||||
else -> SimpleDateFormat(format, Locale.getDefault())
|
else -> DateTimeFormatter.ofPattern(format, Locale.getDefault())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import eu.kanade.presentation.util.formattedMessage
|
|||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.model.StubSource
|
import tachiyomi.domain.source.model.StubSource
|
||||||
|
@ -5,7 +5,6 @@ import android.net.Uri
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@ -53,6 +52,7 @@ import eu.kanade.tachiyomi.extension.model.Extension
|
|||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
|
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@ -190,7 +190,7 @@ private fun ExtensionDetails(
|
|||||||
key = { it.source.id },
|
key = { it.source.id },
|
||||||
) { source ->
|
) { source ->
|
||||||
SourceSwitchPreference(
|
SourceSwitchPreference(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
source = source,
|
source = source,
|
||||||
onClickSourcePreferences = onClickSourcePreferences,
|
onClickSourcePreferences = onClickSourcePreferences,
|
||||||
onClickSource = onClickSource,
|
onClickSource = onClickSource,
|
||||||
@ -224,7 +224,31 @@ private fun DetailsHeader(
|
|||||||
end = MaterialTheme.padding.medium,
|
end = MaterialTheme.padding.medium,
|
||||||
top = MaterialTheme.padding.medium,
|
top = MaterialTheme.padding.medium,
|
||||||
bottom = MaterialTheme.padding.small,
|
bottom = MaterialTheme.padding.small,
|
||||||
),
|
)
|
||||||
|
.clickable {
|
||||||
|
val extDebugInfo = buildString {
|
||||||
|
append(
|
||||||
|
"""
|
||||||
|
Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName})
|
||||||
|
Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode})
|
||||||
|
NSFW: ${extension.isNsfw}
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (extension is Extension.Installed) {
|
||||||
|
append("\n\n")
|
||||||
|
append(
|
||||||
|
"""
|
||||||
|
Update available: ${extension.hasUpdate}
|
||||||
|
Obsolete: ${extension.isObsolete}
|
||||||
|
Shared: ${extension.isShared}
|
||||||
|
Repository: ${extension.repoUrl}
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.copyToClipboard("Extension Debug information", extDebugInfo)
|
||||||
|
},
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
ExtensionIcon(
|
ExtensionIcon(
|
||||||
@ -329,10 +353,8 @@ private fun InfoText(
|
|||||||
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
|
primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
|
||||||
|
|
||||||
val clickableModifier = if (onClick != null) {
|
val clickableModifier = if (onClick != null) {
|
||||||
Modifier.clickable(interactionSource, indication = null) { onClick() }
|
Modifier.clickable(interactionSource = null, indication = null, onClick = onClick)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ private fun ExtensionFilterContent(
|
|||||||
) {
|
) {
|
||||||
items(state.languages) { language ->
|
items(state.languages) { language ->
|
||||||
SwitchPreferenceWidget(
|
SwitchPreferenceWidget(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
title = LocaleHelper.getSourceDisplayName(language, context),
|
title = LocaleHelper.getSourceDisplayName(language, context),
|
||||||
checked = language in state.enabledLanguages,
|
checked = language in state.enabledLanguages,
|
||||||
onCheckedChanged = { onClickLang(language) },
|
onCheckedChanged = { onClickLang(language) },
|
||||||
|
@ -48,6 +48,7 @@ import eu.kanade.presentation.browse.components.ExtensionIcon
|
|||||||
import eu.kanade.presentation.components.WarningBanner
|
import eu.kanade.presentation.components.WarningBanner
|
||||||
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
|
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
|
||||||
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||||
|
import eu.kanade.presentation.util.animateItemFastScroll
|
||||||
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||||
@ -90,7 +91,7 @@ fun ExtensionScreen(
|
|||||||
PullRefresh(
|
PullRefresh(
|
||||||
refreshing = state.isRefreshing,
|
refreshing = state.isRefreshing,
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
enabled = { !state.isLoading },
|
enabled = !state.isLoading,
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
|
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
|
||||||
@ -187,14 +188,14 @@ private fun ExtensionContent(
|
|||||||
}
|
}
|
||||||
ExtensionHeader(
|
ExtensionHeader(
|
||||||
textRes = header.textRes,
|
textRes = header.textRes,
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
action = action,
|
action = action,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is ExtensionUiModel.Header.Text -> {
|
is ExtensionUiModel.Header.Text -> {
|
||||||
ExtensionHeader(
|
ExtensionHeader(
|
||||||
text = header.text,
|
text = header.text,
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,16 +204,24 @@ private fun ExtensionContent(
|
|||||||
items(
|
items(
|
||||||
items = items,
|
items = items,
|
||||||
contentType = { "item" },
|
contentType = { "item" },
|
||||||
key = { "extension-${it.hashCode()}" },
|
key = { item ->
|
||||||
|
when (item.extension) {
|
||||||
|
is Extension.Untrusted -> "extension-untrusted-${item.hashCode()}"
|
||||||
|
is Extension.Installed -> "extension-installed-${item.hashCode()}"
|
||||||
|
is Extension.Available -> "extension-available-${item.hashCode()}"
|
||||||
|
}
|
||||||
|
},
|
||||||
) { item ->
|
) { item ->
|
||||||
ExtensionItem(
|
ExtensionItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
item = item,
|
item = item,
|
||||||
onClickItem = {
|
onClickItem = {
|
||||||
when (it) {
|
when (it) {
|
||||||
is Extension.Available -> onInstallExtension(it)
|
is Extension.Available -> onInstallExtension(it)
|
||||||
is Extension.Installed -> onOpenExtension(it)
|
is Extension.Installed -> onOpenExtension(it)
|
||||||
is Extension.Untrusted -> { trustState = it }
|
is Extension.Untrusted -> {
|
||||||
|
trustState = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongClickItem = onLongClickItem,
|
onLongClickItem = onLongClickItem,
|
||||||
@ -234,7 +243,9 @@ private fun ExtensionContent(
|
|||||||
onOpenExtension(it)
|
onOpenExtension(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Extension.Untrusted -> { trustState = it }
|
is Extension.Untrusted -> {
|
||||||
|
trustState = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
|
||||||
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
|
||||||
@ -79,6 +80,7 @@ internal fun GlobalSearchContent(
|
|||||||
} ?: source.name,
|
} ?: source.name,
|
||||||
subtitle = LocaleHelper.getLocalizedDisplayName(source.lang),
|
subtitle = LocaleHelper.getLocalizedDisplayName(source.lang),
|
||||||
onClick = { onClickSource(source) },
|
onClick = { onClickSource(source) },
|
||||||
|
modifier = Modifier.animateItem(),
|
||||||
) {
|
) {
|
||||||
when (result) {
|
when (result) {
|
||||||
SearchItemResult.Loading -> {
|
SearchItemResult.Loading -> {
|
||||||
|
@ -133,7 +133,7 @@ private fun MigrateSourceList(
|
|||||||
key = { (source, _) -> "migrate-${source.id}" },
|
key = { (source, _) -> "migrate-${source.id}" },
|
||||||
) { (source, count) ->
|
) { (source, count) ->
|
||||||
MigrateSourceItem(
|
MigrateSourceItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
source = source,
|
source = source,
|
||||||
count = count,
|
count = count,
|
||||||
onClickItem = { onClickItem(source) },
|
onClickItem = { onClickItem(source) },
|
||||||
|
@ -10,6 +10,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import eu.kanade.presentation.browse.components.BaseSourceItem
|
import eu.kanade.presentation.browse.components.BaseSourceItem
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
|
import eu.kanade.presentation.util.animateItemFastScroll
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
|
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
@ -68,7 +69,7 @@ private fun SourcesFilterContent(
|
|||||||
contentType = "source-filter-header",
|
contentType = "source-filter-header",
|
||||||
) {
|
) {
|
||||||
SourcesFilterHeader(
|
SourcesFilterHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
language = language,
|
language = language,
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onClickItem = onClickLanguage,
|
onClickItem = onClickLanguage,
|
||||||
@ -81,7 +82,7 @@ private fun SourcesFilterContent(
|
|||||||
contentType = { "source-filter-item" },
|
contentType = { "source-filter-item" },
|
||||||
) { source ->
|
) { source ->
|
||||||
SourcesFilterItem(
|
SourcesFilterItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
source = source,
|
source = source,
|
||||||
enabled = "${source.id}" !in state.disabledSources,
|
enabled = "${source.id}" !in state.disabledSources,
|
||||||
onClickItem = onClickSource,
|
onClickItem = onClickSource,
|
||||||
|
@ -28,7 +28,7 @@ import tachiyomi.domain.source.model.Pin
|
|||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@ -74,12 +74,12 @@ fun SourcesScreen(
|
|||||||
when (model) {
|
when (model) {
|
||||||
is SourceUiModel.Header -> {
|
is SourceUiModel.Header -> {
|
||||||
SourceHeader(
|
SourceHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
language = model.language,
|
language = model.language,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is SourceUiModel.Item -> SourceItem(
|
is SourceUiModel.Item -> SourceItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
source = model.source,
|
source = model.source,
|
||||||
onClickItem = onClickItem,
|
onClickItem = onClickItem,
|
||||||
onLongClickItem = onLongClickItem,
|
onLongClickItem = onLongClickItem,
|
||||||
@ -148,7 +148,7 @@ private fun SourcePinButton(
|
|||||||
MaterialTheme.colorScheme.primary
|
MaterialTheme.colorScheme.primary
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.onBackground.copy(
|
MaterialTheme.colorScheme.onBackground.copy(
|
||||||
alpha = SecondaryItemAlpha,
|
alpha = SECONDARY_ALPHA,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin
|
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin
|
||||||
|
@ -25,13 +25,13 @@ import androidx.compose.ui.res.imageResource
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import eu.kanade.domain.source.model.icon
|
import eu.kanade.domain.source.model.icon
|
||||||
import eu.kanade.presentation.util.rememberResourceBitmapPainter
|
import eu.kanade.presentation.util.rememberResourceBitmapPainter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): St
|
|||||||
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
|
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
|
||||||
withIOContext {
|
withIOContext {
|
||||||
value = try {
|
value = try {
|
||||||
val appInfo = ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
|
val appInfo = ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo!!
|
||||||
val appResources = context.packageManager.getResourcesForApplication(appInfo)
|
val appResources = context.packageManager.getResourcesForApplication(appInfo)
|
||||||
Result.Success(
|
Result.Success(
|
||||||
appResources.getDrawableForDensity(appInfo.icon, density, null)!!
|
appResources.getDrawableForDensity(appInfo.icon, density, null)!!
|
||||||
|
@ -32,9 +32,10 @@ fun GlobalSearchResultItem(
|
|||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column {
|
Column(modifier = modifier) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(
|
.padding(
|
||||||
|
@ -2,7 +2,7 @@ package eu.kanade.presentation.category
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -107,7 +107,7 @@ private fun CategoryContent(
|
|||||||
key = { _, category -> "category-${category.id}" },
|
key = { _, category -> "category-${category.id}" },
|
||||||
) { index, category ->
|
) { index, category ->
|
||||||
CategoryListItem(
|
CategoryListItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItem(),
|
||||||
category = category,
|
category = category,
|
||||||
canMoveUp = index != 0,
|
canMoveUp = index != 0,
|
||||||
canMoveDown = index != categories.lastIndex,
|
canMoveDown = index != categories.lastIndex,
|
||||||
|
@ -30,7 +30,7 @@ import eu.kanade.presentation.category.visualName
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import tachiyomi.core.preference.CheckboxState
|
import tachiyomi.core.common.preference.CheckboxState
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
@ -10,8 +10,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
import tachiyomi.presentation.core.util.shouldExpandFAB
|
||||||
import tachiyomi.presentation.core.util.isScrollingUp
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryFloatingActionButton(
|
fun CategoryFloatingActionButton(
|
||||||
@ -23,7 +22,7 @@ fun CategoryFloatingActionButton(
|
|||||||
text = { Text(text = stringResource(MR.strings.action_add)) },
|
text = { Text(text = stringResource(MR.strings.action_add)) },
|
||||||
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
|
||||||
onClick = onCreate,
|
onClick = onCreate,
|
||||||
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
|
expanded = lazyListState.shouldExpandFAB(),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
||||||
@ -23,7 +21,6 @@ import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
|
|||||||
@Composable
|
@Composable
|
||||||
fun NavigatorAdaptiveSheet(
|
fun NavigatorAdaptiveSheet(
|
||||||
screen: Screen,
|
screen: Screen,
|
||||||
tonalElevation: Dp = 1.dp,
|
|
||||||
enableSwipeDismiss: (Navigator) -> Boolean = { true },
|
enableSwipeDismiss: (Navigator) -> Boolean = { true },
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@ -31,7 +28,6 @@ fun NavigatorAdaptiveSheet(
|
|||||||
screen = screen,
|
screen = screen,
|
||||||
content = { sheetNavigator ->
|
content = { sheetNavigator ->
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
) {
|
) {
|
||||||
@ -73,7 +69,6 @@ fun NavigatorAdaptiveSheet(
|
|||||||
fun AdaptiveSheet(
|
fun AdaptiveSheet(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
tonalElevation: Dp = 1.dp,
|
|
||||||
enableSwipeDismiss: Boolean = true,
|
enableSwipeDismiss: Boolean = true,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
@ -86,7 +81,6 @@ fun AdaptiveSheet(
|
|||||||
AdaptiveSheetImpl(
|
AdaptiveSheetImpl(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
isTabletUi = isTabletUi,
|
isTabletUi = isTabletUi,
|
||||||
tonalElevation = tonalElevation,
|
|
||||||
enableSwipeDismiss = enableSwipeDismiss,
|
enableSwipeDismiss = enableSwipeDismiss,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
) {
|
) {
|
||||||
@ -97,5 +91,5 @@ fun AdaptiveSheet(
|
|||||||
|
|
||||||
private val dialogProperties = DialogProperties(
|
private val dialogProperties = DialogProperties(
|
||||||
usePlatformDefaultWidth = false,
|
usePlatformDefaultWidth = false,
|
||||||
decorFitsSystemWindows = false,
|
decorFitsSystemWindows = true,
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.TextFieldDefaults
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Close
|
import androidx.compose.material.icons.outlined.Close
|
||||||
@ -21,6 +20,7 @@ import androidx.compose.material3.LocalContentColor
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PlainTooltip
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.TooltipBox
|
import androidx.compose.material3.TooltipBox
|
||||||
import androidx.compose.material3.TooltipDefaults
|
import androidx.compose.material3.TooltipDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@ -179,7 +179,7 @@ fun AppBarTitle(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.basicMarquee(
|
modifier = Modifier.basicMarquee(
|
||||||
delayMillis = 2_000,
|
repeatDelayMillis = 2_000,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -312,7 +312,7 @@ fun SearchToolbar(
|
|||||||
visualTransformation = visualTransformation,
|
visualTransformation = visualTransformation,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
TextFieldDefaults.TextFieldDecorationBox(
|
TextFieldDefaults.DecorationBox(
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
innerTextField = innerTextField,
|
innerTextField = innerTextField,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
@ -331,6 +331,7 @@ fun SearchToolbar(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
container = {},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -9,20 +9,26 @@ import tachiyomi.i18n.MR
|
|||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Date
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.ZoneId
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun relativeDateText(
|
fun relativeDateText(
|
||||||
dateEpochMillis: Long,
|
dateEpochMillis: Long,
|
||||||
): String {
|
): String {
|
||||||
return relativeDateText(
|
return relativeDateText(
|
||||||
date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L },
|
localDate = LocalDate.ofInstant(
|
||||||
|
Instant.ofEpochMilli(dateEpochMillis),
|
||||||
|
ZoneId.systemDefault(),
|
||||||
|
)
|
||||||
|
.takeIf { dateEpochMillis > 0L },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun relativeDateText(
|
fun relativeDateText(
|
||||||
date: Date?,
|
localDate: LocalDate?,
|
||||||
): String {
|
): String {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@ -30,11 +36,10 @@ fun relativeDateText(
|
|||||||
val relativeTime = remember { preferences.relativeTime().get() }
|
val relativeTime = remember { preferences.relativeTime().get() }
|
||||||
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
||||||
|
|
||||||
return date
|
return localDate?.toRelativeString(
|
||||||
?.toRelativeString(
|
context = context,
|
||||||
context = context,
|
relative = relativeTime,
|
||||||
relative = relativeTime,
|
dateFormat = dateFormat,
|
||||||
dateFormat = dateFormat,
|
)
|
||||||
)
|
|
||||||
?: stringResource(MR.strings.not_applicable)
|
?: stringResource(MR.strings.not_applicable)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.wrapContentSize
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.PagerState
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@ -29,7 +30,6 @@ import androidx.compose.ui.util.fastForEachIndexed
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.HorizontalPager
|
|
||||||
import tachiyomi.presentation.core.components.material.TabText
|
import tachiyomi.presentation.core.components.material.TabText
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ fun TabbedDialog(
|
|||||||
PrimaryTabRow(
|
PrimaryTabRow(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
selectedTabIndex = pagerState.currentPage,
|
selectedTabIndex = pagerState.currentPage,
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
divider = {},
|
divider = {},
|
||||||
) {
|
) {
|
||||||
tabTitles.fastForEachIndexed { index, tab ->
|
tabTitles.fastForEachIndexed { index, tab ->
|
||||||
@ -78,9 +79,8 @@ fun TabbedDialog(
|
|||||||
modifier = Modifier.animateContentSize(),
|
modifier = Modifier.animateContentSize(),
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
) { page ->
|
pageContent = { page -> content(page) },
|
||||||
content(page)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.calculateEndPadding
|
|||||||
import androidx.compose.foundation.layout.calculateStartPadding
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.PrimaryTabRow
|
import androidx.compose.material3.PrimaryTabRow
|
||||||
@ -24,7 +25,6 @@ import dev.icerock.moko.resources.StringResource
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.presentation.core.components.HorizontalPager
|
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.components.material.TabText
|
import tachiyomi.presentation.core.components.material.TabText
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -37,7 +37,7 @@ fun CrashScreen(
|
|||||||
acceptText = stringResource(MR.strings.pref_dump_crash_logs),
|
acceptText = stringResource(MR.strings.pref_dump_crash_logs),
|
||||||
onAcceptClick = {
|
onAcceptClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
CrashLogUtil(context).dumpLogs()
|
CrashLogUtil(context).dumpLogs(exception)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rejectText = stringResource(MR.strings.crash_screen_restart_application),
|
rejectText = stringResource(MR.strings.crash_screen_restart_application),
|
||||||
|
@ -18,6 +18,7 @@ import eu.kanade.presentation.components.SearchToolbar
|
|||||||
import eu.kanade.presentation.components.relativeDateText
|
import eu.kanade.presentation.components.relativeDateText
|
||||||
import eu.kanade.presentation.history.components.HistoryItem
|
import eu.kanade.presentation.history.components.HistoryItem
|
||||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||||
|
import eu.kanade.presentation.util.animateItemFastScroll
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
@ -28,7 +29,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
|||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
import java.util.Date
|
import java.time.LocalDate
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryScreen(
|
fun HistoryScreen(
|
||||||
@ -113,14 +114,14 @@ private fun HistoryScreenContent(
|
|||||||
when (item) {
|
when (item) {
|
||||||
is HistoryUiModel.Header -> {
|
is HistoryUiModel.Header -> {
|
||||||
ListGroupHeader(
|
ListGroupHeader(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
text = relativeDateText(item.date),
|
text = relativeDateText(item.date),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is HistoryUiModel.Item -> {
|
is HistoryUiModel.Item -> {
|
||||||
val value = item.item
|
val value = item.item
|
||||||
HistoryItem(
|
HistoryItem(
|
||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemFastScroll(),
|
||||||
history = value,
|
history = value,
|
||||||
onClickCover = { onClickCover(value) },
|
onClickCover = { onClickCover(value) },
|
||||||
onClickResume = { onClickResume(value) },
|
onClickResume = { onClickResume(value) },
|
||||||
@ -133,7 +134,7 @@ private fun HistoryScreenContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed interface HistoryUiModel {
|
sealed interface HistoryUiModel {
|
||||||
data class Header(val date: Date) : HistoryUiModel
|
data class Header(val date: LocalDate) : HistoryUiModel
|
||||||
data class Item(val item: HistoryWithRelations) : HistoryUiModel
|
data class Item(val item: HistoryWithRelations) : HistoryUiModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
|||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
@ -71,10 +72,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo
|
|||||||
private object HistoryUiModelExamples {
|
private object HistoryUiModelExamples {
|
||||||
val headerToday = header()
|
val headerToday = header()
|
||||||
val headerTomorrow =
|
val headerTomorrow =
|
||||||
HistoryUiModel.Header(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))
|
HistoryUiModel.Header(LocalDate.now().plusDays(1))
|
||||||
|
|
||||||
fun header(instantBuilder: (Instant) -> Instant = { it }) =
|
fun header(instantBuilder: (Instant) -> Instant = { it }) =
|
||||||
HistoryUiModel.Header(Date.from(instantBuilder(Instant.now())))
|
HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now())))
|
||||||
|
|
||||||
fun items() = sequence {
|
fun items() = sequence {
|
||||||
var count = 1
|
var count = 1
|
||||||
|
@ -10,7 +10,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import tachiyomi.core.preference.CheckboxState
|
import tachiyomi.core.common.preference.CheckboxState
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -6,9 +6,12 @@ import androidx.compose.foundation.layout.ColumnScope
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@ -19,13 +22,14 @@ import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel
|
|||||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.core.preference.TriState
|
import tachiyomi.core.common.preference.TriState
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.library.model.LibrarySort
|
import tachiyomi.domain.library.model.LibrarySort
|
||||||
import tachiyomi.domain.library.model.sort
|
import tachiyomi.domain.library.model.sort
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.BaseSortItem
|
||||||
import tachiyomi.presentation.core.components.CheckboxItem
|
import tachiyomi.presentation.core.components.CheckboxItem
|
||||||
import tachiyomi.presentation.core.components.HeadingItem
|
import tachiyomi.presentation.core.components.HeadingItem
|
||||||
import tachiyomi.presentation.core.components.SettingsChipRow
|
import tachiyomi.presentation.core.components.SettingsChipRow
|
||||||
@ -125,7 +129,7 @@ private fun ColumnScope.FilterPage(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val trackers = remember { screenModel.trackers }
|
val trackers by screenModel.trackersFlow.collectAsState()
|
||||||
when (trackers.size) {
|
when (trackers.size) {
|
||||||
0 -> {
|
0 -> {
|
||||||
// No trackers
|
// No trackers
|
||||||
@ -158,26 +162,42 @@ private fun ColumnScope.SortPage(
|
|||||||
category: Category?,
|
category: Category?,
|
||||||
screenModel: LibrarySettingsScreenModel,
|
screenModel: LibrarySettingsScreenModel,
|
||||||
) {
|
) {
|
||||||
|
val trackers by screenModel.trackersFlow.collectAsState()
|
||||||
val sortingMode = category.sort.type
|
val sortingMode = category.sort.type
|
||||||
val sortDescending = !category.sort.isAscending
|
val sortDescending = !category.sort.isAscending
|
||||||
|
|
||||||
val trackerSortOption =
|
val options = remember(trackers.isEmpty()) {
|
||||||
if (screenModel.trackers.isEmpty()) {
|
val trackerMeanPair = if (trackers.isNotEmpty()) {
|
||||||
emptyList()
|
MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean
|
||||||
} else {
|
} else {
|
||||||
listOf(MR.strings.action_sort_tracker_score to LibrarySort.Type.TrackerMean)
|
null
|
||||||
}
|
}
|
||||||
|
listOfNotNull(
|
||||||
|
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
|
||||||
|
MR.strings.action_sort_total to LibrarySort.Type.TotalChapters,
|
||||||
|
MR.strings.action_sort_last_read to LibrarySort.Type.LastRead,
|
||||||
|
MR.strings.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
|
||||||
|
MR.strings.action_sort_unread_count to LibrarySort.Type.UnreadCount,
|
||||||
|
MR.strings.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
|
||||||
|
MR.strings.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
|
||||||
|
MR.strings.action_sort_date_added to LibrarySort.Type.DateAdded,
|
||||||
|
trackerMeanPair,
|
||||||
|
MR.strings.action_sort_random to LibrarySort.Type.Random,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
listOf(
|
options.map { (titleRes, mode) ->
|
||||||
MR.strings.action_sort_alpha to LibrarySort.Type.Alphabetical,
|
if (mode == LibrarySort.Type.Random) {
|
||||||
MR.strings.action_sort_total to LibrarySort.Type.TotalChapters,
|
BaseSortItem(
|
||||||
MR.strings.action_sort_last_read to LibrarySort.Type.LastRead,
|
label = stringResource(titleRes),
|
||||||
MR.strings.action_sort_last_manga_update to LibrarySort.Type.LastUpdate,
|
icon = Icons.Default.Refresh
|
||||||
MR.strings.action_sort_unread_count to LibrarySort.Type.UnreadCount,
|
.takeIf { sortingMode == LibrarySort.Type.Random },
|
||||||
MR.strings.action_sort_latest_chapter to LibrarySort.Type.LatestChapter,
|
onClick = {
|
||||||
MR.strings.action_sort_chapter_fetch_date to LibrarySort.Type.ChapterFetchDate,
|
screenModel.setSort(category, mode, LibrarySort.Direction.Ascending)
|
||||||
MR.strings.action_sort_date_added to LibrarySort.Type.DateAdded,
|
},
|
||||||
).plus(trackerSortOption).map { (titleRes, mode) ->
|
)
|
||||||
|
return@map
|
||||||
|
}
|
||||||
SortItem(
|
SortItem(
|
||||||
label = stringResource(titleRes),
|
label = stringResource(titleRes),
|
||||||
sortDescending = sortDescending.takeIf { sortingMode == mode },
|
sortDescending = sortDescending.takeIf { sortingMode == mode },
|
||||||
|
@ -35,6 +35,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.Shadow
|
import androidx.compose.ui.graphics.Shadow
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.kanade.presentation.manga.components.MangaCover
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
@ -42,19 +43,26 @@ import tachiyomi.i18n.MR
|
|||||||
import tachiyomi.presentation.core.components.BadgeGroup
|
import tachiyomi.presentation.core.components.BadgeGroup
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.selectedBackground
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
|
import tachiyomi.domain.manga.model.MangaCover as MangaCoverModel
|
||||||
|
|
||||||
object CommonMangaItemDefaults {
|
object CommonMangaItemDefaults {
|
||||||
val GridHorizontalSpacer = 4.dp
|
val GridHorizontalSpacer = 4.dp
|
||||||
val GridVerticalSpacer = 4.dp
|
val GridVerticalSpacer = 4.dp
|
||||||
|
|
||||||
|
@Suppress("ConstPropertyName")
|
||||||
const val BrowseFavoriteCoverAlpha = 0.34f
|
const val BrowseFavoriteCoverAlpha = 0.34f
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ContinueReadingButtonSize = 28.dp
|
private val ContinueReadingButtonSizeSmall = 28.dp
|
||||||
|
private val ContinueReadingButtonSizeLarge = 32.dp
|
||||||
|
|
||||||
|
private val ContinueReadingButtonIconSizeSmall = 16.dp
|
||||||
|
private val ContinueReadingButtonIconSizeLarge = 20.dp
|
||||||
|
|
||||||
private val ContinueReadingButtonGridPadding = 6.dp
|
private val ContinueReadingButtonGridPadding = 6.dp
|
||||||
private val ContinueReadingButtonListSpacing = 8.dp
|
private val ContinueReadingButtonListSpacing = 8.dp
|
||||||
|
|
||||||
private const val GridSelectedCoverAlpha = 0.76f
|
private const val GRID_SELECTED_COVER_ALPHA = 0.76f
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout of grid list item with title overlaying the cover.
|
* Layout of grid list item with title overlaying the cover.
|
||||||
@ -62,7 +70,7 @@ private const val GridSelectedCoverAlpha = 0.76f
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaCompactGridItem(
|
fun MangaCompactGridItem(
|
||||||
coverData: tachiyomi.domain.manga.model.MangaCover,
|
coverData: MangaCoverModel,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
isSelected: Boolean = false,
|
isSelected: Boolean = false,
|
||||||
@ -82,7 +90,7 @@ fun MangaCompactGridItem(
|
|||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
|
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
|
||||||
data = coverData,
|
data = coverData,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -96,10 +104,12 @@ fun MangaCompactGridItem(
|
|||||||
)
|
)
|
||||||
} else if (onClickContinueReading != null) {
|
} else if (onClickContinueReading != null) {
|
||||||
ContinueReadingButton(
|
ContinueReadingButton(
|
||||||
|
size = ContinueReadingButtonSizeLarge,
|
||||||
|
iconSize = ContinueReadingButtonIconSizeLarge,
|
||||||
|
onClick = onClickContinueReading,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(ContinueReadingButtonGridPadding)
|
.padding(ContinueReadingButtonGridPadding)
|
||||||
.align(Alignment.BottomEnd),
|
.align(Alignment.BottomEnd),
|
||||||
onClickContinueReading = onClickContinueReading,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -148,11 +158,13 @@ private fun BoxScope.CoverTextOverlay(
|
|||||||
)
|
)
|
||||||
if (onClickContinueReading != null) {
|
if (onClickContinueReading != null) {
|
||||||
ContinueReadingButton(
|
ContinueReadingButton(
|
||||||
|
size = ContinueReadingButtonSizeSmall,
|
||||||
|
iconSize = ContinueReadingButtonIconSizeSmall,
|
||||||
|
onClick = onClickContinueReading,
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
end = ContinueReadingButtonGridPadding,
|
end = ContinueReadingButtonGridPadding,
|
||||||
bottom = ContinueReadingButtonGridPadding,
|
bottom = ContinueReadingButtonGridPadding,
|
||||||
),
|
),
|
||||||
onClickContinueReading = onClickContinueReading,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,7 +175,7 @@ private fun BoxScope.CoverTextOverlay(
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaComfortableGridItem(
|
fun MangaComfortableGridItem(
|
||||||
coverData: tachiyomi.domain.manga.model.MangaCover,
|
coverData: MangaCoverModel,
|
||||||
title: String,
|
title: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
@ -185,7 +197,7 @@ fun MangaComfortableGridItem(
|
|||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
|
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
|
||||||
data = coverData,
|
data = coverData,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -194,10 +206,12 @@ fun MangaComfortableGridItem(
|
|||||||
content = {
|
content = {
|
||||||
if (onClickContinueReading != null) {
|
if (onClickContinueReading != null) {
|
||||||
ContinueReadingButton(
|
ContinueReadingButton(
|
||||||
|
size = ContinueReadingButtonSizeLarge,
|
||||||
|
iconSize = ContinueReadingButtonIconSizeLarge,
|
||||||
|
onClick = onClickContinueReading,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(ContinueReadingButtonGridPadding)
|
.padding(ContinueReadingButtonGridPadding)
|
||||||
.align(Alignment.BottomEnd),
|
.align(Alignment.BottomEnd),
|
||||||
onClickContinueReading = onClickContinueReading,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -309,14 +323,14 @@ private fun GridItemSelectable(
|
|||||||
private fun Modifier.selectedOutline(
|
private fun Modifier.selectedOutline(
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
color: Color,
|
color: Color,
|
||||||
) = this then drawBehind { if (isSelected) drawRect(color = color) }
|
) = drawBehind { if (isSelected) drawRect(color = color) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Layout of list item.
|
* Layout of list item.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaListItem(
|
fun MangaListItem(
|
||||||
coverData: tachiyomi.domain.manga.model.MangaCover,
|
coverData: MangaCoverModel,
|
||||||
title: String,
|
title: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
@ -354,8 +368,10 @@ fun MangaListItem(
|
|||||||
BadgeGroup(content = badge)
|
BadgeGroup(content = badge)
|
||||||
if (onClickContinueReading != null) {
|
if (onClickContinueReading != null) {
|
||||||
ContinueReadingButton(
|
ContinueReadingButton(
|
||||||
|
size = ContinueReadingButtonSizeSmall,
|
||||||
|
iconSize = ContinueReadingButtonIconSizeSmall,
|
||||||
|
onClick = onClickContinueReading,
|
||||||
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing),
|
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing),
|
||||||
onClickContinueReading = onClickContinueReading,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,23 +379,25 @@ fun MangaListItem(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContinueReadingButton(
|
private fun ContinueReadingButton(
|
||||||
|
size: Dp,
|
||||||
|
iconSize: Dp,
|
||||||
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClickContinueReading: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
FilledIconButton(
|
FilledIconButton(
|
||||||
onClick = onClickContinueReading,
|
onClick = onClick,
|
||||||
modifier = Modifier.size(ContinueReadingButtonSize),
|
|
||||||
shape = MaterialTheme.shapes.small,
|
shape = MaterialTheme.shapes.small,
|
||||||
colors = IconButtonDefaults.filledIconButtonColors(
|
colors = IconButtonDefaults.filledIconButtonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
|
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
|
||||||
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
|
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
|
||||||
),
|
),
|
||||||
|
modifier = Modifier.size(size),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.PlayArrow,
|
imageVector = Icons.Filled.PlayArrow,
|
||||||
contentDescription = stringResource(MR.strings.action_resume),
|
contentDescription = stringResource(MR.strings.action_resume),
|
||||||
modifier = Modifier.size(16.dp),
|
modifier = Modifier.size(iconSize),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ fun LibraryContent(
|
|||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = { notSelectionMode },
|
enabled = notSelectionMode,
|
||||||
) {
|
) {
|
||||||
LibraryPager(
|
LibraryPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.PagerState
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
@ -22,7 +23,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryItem
|
|||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.HorizontalPager
|
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.util.plus
|
import tachiyomi.presentation.core.util.plus
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ import eu.kanade.domain.manga.model.forceDownloaded
|
|||||||
import eu.kanade.presentation.components.TabbedDialog
|
import eu.kanade.presentation.components.TabbedDialog
|
||||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.core.preference.TriState
|
import tachiyomi.core.common.preference.TriState
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
|
@ -1,16 +1,33 @@
|
|||||||
package eu.kanade.presentation.manga
|
package eu.kanade.presentation.manga
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Add
|
||||||
|
import androidx.compose.material.icons.outlined.Book
|
||||||
|
import androidx.compose.material.icons.outlined.SwapVert
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import eu.kanade.presentation.components.AdaptiveSheet
|
||||||
|
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||||
|
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
|
||||||
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -18,42 +35,92 @@ fun DuplicateMangaDialog(
|
|||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onConfirm: () -> Unit,
|
onConfirm: () -> Unit,
|
||||||
onOpenManga: () -> Unit,
|
onOpenManga: () -> Unit,
|
||||||
|
onMigrate: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
val minHeight = LocalPreferenceMinHeight.current
|
||||||
|
|
||||||
|
AdaptiveSheet(
|
||||||
|
modifier = modifier,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
title = {
|
) {
|
||||||
Text(text = stringResource(MR.strings.are_you_sure))
|
Column(
|
||||||
},
|
modifier = Modifier
|
||||||
text = {
|
.padding(
|
||||||
Text(text = stringResource(MR.strings.confirm_add_duplicate_manga))
|
vertical = TabbedDialogPaddings.Vertical,
|
||||||
},
|
horizontal = TabbedDialogPaddings.Horizontal,
|
||||||
confirmButton = {
|
)
|
||||||
FlowRow(
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(TitlePadding),
|
||||||
|
text = stringResource(MR.strings.are_you_sure),
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.confirm_add_duplicate_manga),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(PaddingSize))
|
||||||
|
|
||||||
|
TextPreferenceWidget(
|
||||||
|
title = stringResource(MR.strings.action_show_manga),
|
||||||
|
icon = Icons.Outlined.Book,
|
||||||
|
onPreferenceClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
onOpenManga()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
TextPreferenceWidget(
|
||||||
|
title = stringResource(MR.strings.action_migrate_duplicate),
|
||||||
|
icon = Icons.Outlined.SwapVert,
|
||||||
|
onPreferenceClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
onMigrate()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
TextPreferenceWidget(
|
||||||
|
title = stringResource(MR.strings.action_add_anyway),
|
||||||
|
icon = Icons.Outlined.Add,
|
||||||
|
onPreferenceClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
onConfirm()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.sizeIn(minHeight = minHeight)
|
||||||
|
.clickable { onDismissRequest.invoke() }
|
||||||
|
.padding(ButtonPadding)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
TextButton(
|
OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) {
|
||||||
onClick = {
|
Text(
|
||||||
onDismissRequest()
|
modifier = Modifier
|
||||||
onOpenManga()
|
.padding(vertical = 8.dp),
|
||||||
},
|
text = stringResource(MR.strings.action_cancel),
|
||||||
) {
|
color = MaterialTheme.colorScheme.primary,
|
||||||
Text(text = stringResource(MR.strings.action_show_manga))
|
style = MaterialTheme.typography.titleLarge,
|
||||||
}
|
fontSize = 16.sp,
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(text = stringResource(MR.strings.action_cancel))
|
|
||||||
}
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
onDismissRequest()
|
|
||||||
onConfirm()
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(MR.strings.action_add))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val PaddingSize = 16.dp
|
||||||
|
|
||||||
|
private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp)
|
||||||
|
private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp)
|
||||||
|
@ -75,8 +75,7 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut
|
|||||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
import tachiyomi.presentation.core.util.shouldExpandFAB
|
||||||
import tachiyomi.presentation.core.util.isScrollingUp
|
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@ -346,7 +345,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
},
|
},
|
||||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||||
onClick = onContinueReading,
|
onClick = onContinueReading,
|
||||||
expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
|
expanded = chapterListState.shouldExpandFAB(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -356,7 +355,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
PullRefresh(
|
PullRefresh(
|
||||||
refreshing = state.isRefreshingData,
|
refreshing = state.isRefreshingData,
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
enabled = { !isAnySelected },
|
enabled = !isAnySelected,
|
||||||
indicatorPadding = PaddingValues(top = topPadding),
|
indicatorPadding = PaddingValues(top = topPadding),
|
||||||
) {
|
) {
|
||||||
val layoutDirection = LocalLayoutDirection.current
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
@ -381,13 +380,9 @@ private fun MangaScreenSmallImpl(
|
|||||||
MangaInfoBox(
|
MangaInfoBox(
|
||||||
isTabletUi = false,
|
isTabletUi = false,
|
||||||
appBarPadding = topPadding,
|
appBarPadding = topPadding,
|
||||||
title = state.manga.title,
|
manga = state.manga,
|
||||||
author = state.manga.author,
|
|
||||||
artist = state.manga.artist,
|
|
||||||
sourceName = remember { state.source.getNameForMangaInfo() },
|
sourceName = remember { state.source.getNameForMangaInfo() },
|
||||||
isStubSource = remember { state.source is StubSource },
|
isStubSource = remember { state.source is StubSource },
|
||||||
coverDataProvider = { state.manga },
|
|
||||||
status = state.manga.status,
|
|
||||||
onCoverClick = onCoverClicked,
|
onCoverClick = onCoverClicked,
|
||||||
doSearch = onSearch,
|
doSearch = onSearch,
|
||||||
)
|
)
|
||||||
@ -594,7 +589,7 @@ fun MangaScreenLargeImpl(
|
|||||||
},
|
},
|
||||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||||
onClick = onContinueReading,
|
onClick = onContinueReading,
|
||||||
expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(),
|
expanded = chapterListState.shouldExpandFAB(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -602,7 +597,7 @@ fun MangaScreenLargeImpl(
|
|||||||
PullRefresh(
|
PullRefresh(
|
||||||
refreshing = state.isRefreshingData,
|
refreshing = state.isRefreshingData,
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
enabled = { !isAnySelected },
|
enabled = !isAnySelected,
|
||||||
indicatorPadding = PaddingValues(
|
indicatorPadding = PaddingValues(
|
||||||
start = insetPadding.calculateStartPadding(layoutDirection),
|
start = insetPadding.calculateStartPadding(layoutDirection),
|
||||||
top = with(density) { topBarHeight.toDp() },
|
top = with(density) { topBarHeight.toDp() },
|
||||||
@ -623,13 +618,9 @@ fun MangaScreenLargeImpl(
|
|||||||
MangaInfoBox(
|
MangaInfoBox(
|
||||||
isTabletUi = true,
|
isTabletUi = true,
|
||||||
appBarPadding = contentPadding.calculateTopPadding(),
|
appBarPadding = contentPadding.calculateTopPadding(),
|
||||||
title = state.manga.title,
|
manga = state.manga,
|
||||||
author = state.manga.author,
|
|
||||||
artist = state.manga.artist,
|
|
||||||
sourceName = remember { state.source.getNameForMangaInfo() },
|
sourceName = remember { state.source.getNameForMangaInfo() },
|
||||||
isStubSource = remember { state.source is StubSource },
|
isStubSource = remember { state.source is StubSource },
|
||||||
coverDataProvider = { state.manga },
|
|
||||||
status = state.manga.status,
|
|
||||||
onCoverClick = onCoverClicked,
|
onCoverClick = onCoverClicked,
|
||||||
doSearch = onSearch,
|
doSearch = onSearch,
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,6 @@ package eu.kanade.presentation.manga.components
|
|||||||
|
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@ -10,13 +9,13 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material.icons.outlined.ArrowDownward
|
import androidx.compose.material.icons.outlined.ArrowDownward
|
||||||
import androidx.compose.material.icons.outlined.ErrorOutline
|
import androidx.compose.material.icons.outlined.ErrorOutline
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@ -24,8 +23,9 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.composed
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedback
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
@ -91,6 +91,7 @@ private fun NotDownloadedIndicator(
|
|||||||
.size(IconButtonTokens.StateLayerSize)
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
.commonClickable(
|
.commonClickable(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
|
hapticFeedback = LocalHapticFeedback.current,
|
||||||
onLongClick = { onClick(ChapterDownloadAction.START_NOW) },
|
onLongClick = { onClick(ChapterDownloadAction.START_NOW) },
|
||||||
onClick = { onClick(ChapterDownloadAction.START) },
|
onClick = { onClick(ChapterDownloadAction.START) },
|
||||||
)
|
)
|
||||||
@ -120,6 +121,7 @@ private fun DownloadingIndicator(
|
|||||||
.size(IconButtonTokens.StateLayerSize)
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
.commonClickable(
|
.commonClickable(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
|
hapticFeedback = LocalHapticFeedback.current,
|
||||||
onLongClick = { onClick(ChapterDownloadAction.CANCEL) },
|
onLongClick = { onClick(ChapterDownloadAction.CANCEL) },
|
||||||
onClick = { isMenuExpanded = true },
|
onClick = { isMenuExpanded = true },
|
||||||
),
|
),
|
||||||
@ -136,6 +138,8 @@ private fun DownloadingIndicator(
|
|||||||
modifier = IndicatorModifier,
|
modifier = IndicatorModifier,
|
||||||
color = strokeColor,
|
color = strokeColor,
|
||||||
strokeWidth = IndicatorStrokeWidth,
|
strokeWidth = IndicatorStrokeWidth,
|
||||||
|
trackColor = Color.Transparent,
|
||||||
|
strokeCap = StrokeCap.Butt,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val animatedProgress by animateFloatAsState(
|
val animatedProgress by animateFloatAsState(
|
||||||
@ -153,6 +157,9 @@ private fun DownloadingIndicator(
|
|||||||
modifier = IndicatorModifier,
|
modifier = IndicatorModifier,
|
||||||
color = strokeColor,
|
color = strokeColor,
|
||||||
strokeWidth = IndicatorSize / 2,
|
strokeWidth = IndicatorSize / 2,
|
||||||
|
trackColor = Color.Transparent,
|
||||||
|
strokeCap = StrokeCap.Butt,
|
||||||
|
gapSize = 0.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
|
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
|
||||||
@ -192,6 +199,7 @@ private fun DownloadedIndicator(
|
|||||||
.size(IconButtonTokens.StateLayerSize)
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
.commonClickable(
|
.commonClickable(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
|
hapticFeedback = LocalHapticFeedback.current,
|
||||||
onLongClick = { isMenuExpanded = true },
|
onLongClick = { isMenuExpanded = true },
|
||||||
onClick = { isMenuExpanded = true },
|
onClick = { isMenuExpanded = true },
|
||||||
),
|
),
|
||||||
@ -226,6 +234,7 @@ private fun ErrorIndicator(
|
|||||||
.size(IconButtonTokens.StateLayerSize)
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
.commonClickable(
|
.commonClickable(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
|
hapticFeedback = LocalHapticFeedback.current,
|
||||||
onLongClick = { onClick(ChapterDownloadAction.START) },
|
onLongClick = { onClick(ChapterDownloadAction.START) },
|
||||||
onClick = { onClick(ChapterDownloadAction.START) },
|
onClick = { onClick(ChapterDownloadAction.START) },
|
||||||
),
|
),
|
||||||
@ -242,26 +251,23 @@ private fun ErrorIndicator(
|
|||||||
|
|
||||||
private fun Modifier.commonClickable(
|
private fun Modifier.commonClickable(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
|
hapticFeedback: HapticFeedback,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) = composed {
|
) = this.combinedClickable(
|
||||||
val haptic = LocalHapticFeedback.current
|
enabled = enabled,
|
||||||
|
onLongClick = {
|
||||||
Modifier.combinedClickable(
|
onLongClick()
|
||||||
enabled = enabled,
|
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
onLongClick = {
|
},
|
||||||
onLongClick()
|
onClick = onClick,
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
role = Role.Button,
|
||||||
},
|
interactionSource = null,
|
||||||
onClick = onClick,
|
indication = ripple(
|
||||||
role = Role.Button,
|
bounded = false,
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
radius = IconButtonTokens.StateLayerSize / 2,
|
||||||
indication = rememberRipple(
|
),
|
||||||
bounded = false,
|
)
|
||||||
radius = IconButtonTokens.StateLayerSize / 2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val IndicatorSize = 26.dp
|
private val IndicatorSize = 26.dp
|
||||||
private val IndicatorPadding = 2.dp
|
private val IndicatorPadding = 2.dp
|
||||||
|
@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@ -60,6 +60,6 @@ private fun MissingChaptersWarning(count: Int) {
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.error.copy(alpha = SecondaryItemAlpha),
|
color = MaterialTheme.colorScheme.error.copy(alpha = SECONDARY_ALPHA),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@ -30,11 +29,11 @@ import androidx.compose.material.icons.outlined.Delete
|
|||||||
import androidx.compose.material.icons.outlined.DoneAll
|
import androidx.compose.material.icons.outlined.DoneAll
|
||||||
import androidx.compose.material.icons.outlined.Download
|
import androidx.compose.material.icons.outlined.Download
|
||||||
import androidx.compose.material.icons.outlined.RemoveDone
|
import androidx.compose.material.icons.outlined.RemoveDone
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
@ -82,7 +81,7 @@ fun MangaBottomActionMenu(
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
||||||
tonalElevation = 3.dp,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
|
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
|
||||||
@ -191,8 +190,8 @@ private fun RowScope.Button(
|
|||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.weight(animatedWeight)
|
.weight(animatedWeight)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = null,
|
||||||
indication = rememberRipple(bounded = false),
|
indication = ripple(bounded = false),
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
),
|
),
|
||||||
@ -238,7 +237,7 @@ fun LibraryBottomActionMenu(
|
|||||||
Surface(
|
Surface(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
||||||
tonalElevation = 3.dp,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm = remember { mutableStateListOf(false, false, false, false, false) }
|
val confirm = remember { mutableStateListOf(false, false, false, false, false) }
|
||||||
|
@ -24,36 +24,26 @@ import androidx.compose.material3.ProvideTextStyle
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.contentColorFor
|
import androidx.compose.material3.contentColorFor
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
|
||||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
|
||||||
import androidx.compose.ui.platform.ViewConfiguration
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import me.saket.swipe.SwipeableActionsBox
|
import me.saket.swipe.SwipeableActionsBox
|
||||||
import me.saket.swipe.rememberSwipeableActionsState
|
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.selectedBackground
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaChapterListItem(
|
fun MangaChapterListItem(
|
||||||
@ -75,142 +65,119 @@ fun MangaChapterListItem(
|
|||||||
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
|
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val start = getSwipeAction(
|
||||||
val density = LocalDensity.current
|
action = chapterSwipeStartAction,
|
||||||
|
read = read,
|
||||||
|
bookmark = bookmark,
|
||||||
|
downloadState = downloadStateProvider(),
|
||||||
|
background = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
onSwipe = { onChapterSwipe(chapterSwipeStartAction) },
|
||||||
|
)
|
||||||
|
val end = getSwipeAction(
|
||||||
|
action = chapterSwipeEndAction,
|
||||||
|
read = read,
|
||||||
|
bookmark = bookmark,
|
||||||
|
downloadState = downloadStateProvider(),
|
||||||
|
background = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
onSwipe = { onChapterSwipe(chapterSwipeEndAction) },
|
||||||
|
)
|
||||||
|
|
||||||
val textAlpha = if (read) ReadItemAlpha else 1f
|
SwipeableActionsBox(
|
||||||
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
|
modifier = Modifier.clipToBounds(),
|
||||||
|
startActions = listOfNotNull(start),
|
||||||
// Increase touch slop of swipe action to reduce accidental trigger
|
endActions = listOfNotNull(end),
|
||||||
val configuration = LocalViewConfiguration.current
|
swipeThreshold = swipeActionThreshold,
|
||||||
CompositionLocalProvider(
|
backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest,
|
||||||
LocalViewConfiguration provides object : ViewConfiguration by configuration {
|
|
||||||
override val touchSlop: Float = configuration.touchSlop * 3f
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
val start = getSwipeAction(
|
Row(
|
||||||
action = chapterSwipeStartAction,
|
modifier = modifier
|
||||||
read = read,
|
.selectedBackground(selected)
|
||||||
bookmark = bookmark,
|
.combinedClickable(
|
||||||
downloadState = downloadStateProvider(),
|
onClick = onClick,
|
||||||
background = MaterialTheme.colorScheme.primaryContainer,
|
onLongClick = onLongClick,
|
||||||
onSwipe = { onChapterSwipe(chapterSwipeStartAction) },
|
)
|
||||||
)
|
.padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
|
||||||
val end = getSwipeAction(
|
|
||||||
action = chapterSwipeEndAction,
|
|
||||||
read = read,
|
|
||||||
bookmark = bookmark,
|
|
||||||
downloadState = downloadStateProvider(),
|
|
||||||
background = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
onSwipe = { onChapterSwipe(chapterSwipeEndAction) },
|
|
||||||
)
|
|
||||||
|
|
||||||
val swipeableActionsState = rememberSwipeableActionsState()
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
// Haptic effect when swipe over threshold
|
|
||||||
val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() }
|
|
||||||
snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx }
|
|
||||||
.collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) }
|
|
||||||
}
|
|
||||||
|
|
||||||
SwipeableActionsBox(
|
|
||||||
modifier = Modifier.clipToBounds(),
|
|
||||||
state = swipeableActionsState,
|
|
||||||
startActions = listOfNotNull(start),
|
|
||||||
endActions = listOfNotNull(end),
|
|
||||||
swipeThreshold = swipeActionThreshold,
|
|
||||||
backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest,
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Column(
|
||||||
modifier = modifier
|
modifier = Modifier.weight(1f),
|
||||||
.selectedBackground(selected)
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
.combinedClickable(
|
|
||||||
onClick = onClick,
|
|
||||||
onLongClick = onLongClick,
|
|
||||||
)
|
|
||||||
.padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.weight(1f),
|
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Row(
|
var textHeight by remember { mutableIntStateOf(0) }
|
||||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
if (!read) {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Icon(
|
||||||
) {
|
imageVector = Icons.Filled.Circle,
|
||||||
var textHeight by remember { mutableIntStateOf(0) }
|
contentDescription = stringResource(MR.strings.unread),
|
||||||
if (!read) {
|
modifier = Modifier
|
||||||
Icon(
|
.height(8.dp)
|
||||||
imageVector = Icons.Filled.Circle,
|
.padding(end = 4.dp),
|
||||||
contentDescription = stringResource(MR.strings.unread),
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
modifier = Modifier
|
|
||||||
.height(8.dp)
|
|
||||||
.padding(end = 4.dp),
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (bookmark) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.Bookmark,
|
|
||||||
contentDescription = stringResource(MR.strings.action_filter_bookmarked),
|
|
||||||
modifier = Modifier
|
|
||||||
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = LocalContentColor.current.copy(alpha = textAlpha),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
onTextLayout = { textHeight = it.size.height },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (bookmark) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Bookmark,
|
||||||
|
contentDescription = stringResource(MR.strings.action_filter_bookmarked),
|
||||||
|
modifier = Modifier
|
||||||
|
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
onTextLayout = { textHeight = it.size.height },
|
||||||
|
color = LocalContentColor.current.copy(alpha = if (read) DISABLED_ALPHA else 1f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
ProvideTextStyle(
|
val subtitleStyle = MaterialTheme.typography.bodySmall
|
||||||
value = MaterialTheme.typography.bodyMedium.copy(
|
.merge(
|
||||||
fontSize = 12.sp,
|
color = LocalContentColor.current
|
||||||
color = LocalContentColor.current.copy(alpha = textSubtitleAlpha),
|
.copy(alpha = if (read) DISABLED_ALPHA else SECONDARY_ALPHA),
|
||||||
),
|
)
|
||||||
) {
|
ProvideTextStyle(value = subtitleStyle) {
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
Text(
|
Text(
|
||||||
text = date,
|
text = date,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
if (readProgress != null || scanlator != null) DotSeparatorText()
|
if (readProgress != null || scanlator != null) DotSeparatorText()
|
||||||
}
|
}
|
||||||
if (readProgress != null) {
|
if (readProgress != null) {
|
||||||
Text(
|
Text(
|
||||||
text = readProgress,
|
text = readProgress,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
|
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
|
||||||
)
|
)
|
||||||
if (scanlator != null) DotSeparatorText()
|
if (scanlator != null) DotSeparatorText()
|
||||||
}
|
}
|
||||||
if (scanlator != null) {
|
if (scanlator != null) {
|
||||||
Text(
|
Text(
|
||||||
text = scanlator,
|
text = scanlator,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ChapterDownloadIndicator(
|
|
||||||
enabled = downloadIndicatorEnabled,
|
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
|
||||||
downloadStateProvider = downloadStateProvider,
|
|
||||||
downloadProgressProvider = downloadProgressProvider,
|
|
||||||
onClick = { onDownloadClick?.invoke(it) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChapterDownloadIndicator(
|
||||||
|
enabled = downloadIndicatorEnabled,
|
||||||
|
modifier = Modifier.padding(start = 4.dp),
|
||||||
|
downloadStateProvider = downloadStateProvider,
|
||||||
|
downloadProgressProvider = downloadProgressProvider,
|
||||||
|
onClick = { onDownloadClick?.invoke(it) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Shape
|
|||||||
import androidx.compose.ui.graphics.painter.ColorPainter
|
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import eu.kanade.presentation.util.rememberResourceBitmapPainter
|
import eu.kanade.presentation.util.rememberResourceBitmapPainter
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
@ -37,10 +37,11 @@ import androidx.compose.ui.viewinterop.AndroidView
|
|||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import coil.imageLoader
|
import coil3.asDrawable
|
||||||
import coil.request.CachePolicy
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.CachePolicy
|
||||||
import coil.size.Size
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.size.Size
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
import eu.kanade.presentation.components.DropdownMenu
|
||||||
@ -55,7 +56,7 @@ import tachiyomi.presentation.core.util.clickableNoIndication
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaCoverDialog(
|
fun MangaCoverDialog(
|
||||||
coverDataProvider: () -> Manga,
|
manga: Manga,
|
||||||
isCustomCover: Boolean,
|
isCustomCover: Boolean,
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
onShareClick: () -> Unit,
|
onShareClick: () -> Unit,
|
||||||
@ -165,10 +166,12 @@ fun MangaCoverDialog(
|
|||||||
},
|
},
|
||||||
update = { view ->
|
update = { view ->
|
||||||
val request = ImageRequest.Builder(view.context)
|
val request = ImageRequest.Builder(view.context)
|
||||||
.data(coverDataProvider())
|
.data(manga)
|
||||||
.size(Size.ORIGINAL)
|
.size(Size.ORIGINAL)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.target { drawable ->
|
.target { image ->
|
||||||
|
val drawable = image.asDrawable(view.context.resources)
|
||||||
|
|
||||||
// Copy bitmap in case it came from memory cache
|
// Copy bitmap in case it came from memory cache
|
||||||
// Because SSIV needs to thoroughly read the image
|
// Because SSIV needs to thoroughly read the image
|
||||||
val copy = (drawable as? BitmapDrawable)?.let {
|
val copy = (drawable as? BitmapDrawable)?.let {
|
||||||
|
@ -102,9 +102,12 @@ fun SetIntervalDialog(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
Spacer(Modifier.height(MaterialTheme.padding.small))
|
Text(
|
||||||
|
stringResource(MR.strings.manga_interval_expected_update_null),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Spacer(Modifier.height(MaterialTheme.padding.small))
|
||||||
|
|
||||||
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
|
if (onValueChanged != null && (isDevFlavor || isPreviewBuildType)) {
|
||||||
Text(stringResource(MR.strings.manga_interval_custom_amount))
|
Text(stringResource(MR.strings.manga_interval_custom_amount))
|
||||||
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.manga.components
|
|||||||
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
||||||
@ -42,7 +43,7 @@ import androidx.compose.material.icons.outlined.Sync
|
|||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProvideTextStyle
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
@ -73,13 +74,16 @@ import androidx.compose.ui.unit.Constraints
|
|||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
import eu.kanade.presentation.components.DropdownMenu
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.TextButton
|
import tachiyomi.presentation.core.components.material.TextButton
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
@ -96,13 +100,9 @@ private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTIL
|
|||||||
fun MangaInfoBox(
|
fun MangaInfoBox(
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
appBarPadding: Dp,
|
appBarPadding: Dp,
|
||||||
title: String,
|
manga: Manga,
|
||||||
author: String?,
|
|
||||||
artist: String?,
|
|
||||||
sourceName: String,
|
sourceName: String,
|
||||||
isStubSource: Boolean,
|
isStubSource: Boolean,
|
||||||
coverDataProvider: () -> Manga,
|
|
||||||
status: Long,
|
|
||||||
onCoverClick: () -> Unit,
|
onCoverClick: () -> Unit,
|
||||||
doSearch: (query: String, global: Boolean) -> Unit,
|
doSearch: (query: String, global: Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@ -114,7 +114,10 @@ fun MangaInfoBox(
|
|||||||
MaterialTheme.colorScheme.background,
|
MaterialTheme.colorScheme.background,
|
||||||
)
|
)
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = coverDataProvider(),
|
model = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(manga)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -134,28 +137,20 @@ fun MangaInfoBox(
|
|||||||
if (!isTabletUi) {
|
if (!isTabletUi) {
|
||||||
MangaAndSourceTitlesSmall(
|
MangaAndSourceTitlesSmall(
|
||||||
appBarPadding = appBarPadding,
|
appBarPadding = appBarPadding,
|
||||||
coverDataProvider = coverDataProvider,
|
manga = manga,
|
||||||
onCoverClick = onCoverClick,
|
|
||||||
title = title,
|
|
||||||
doSearch = doSearch,
|
|
||||||
author = author,
|
|
||||||
artist = artist,
|
|
||||||
status = status,
|
|
||||||
sourceName = sourceName,
|
sourceName = sourceName,
|
||||||
isStubSource = isStubSource,
|
isStubSource = isStubSource,
|
||||||
|
onCoverClick = onCoverClick,
|
||||||
|
doSearch = doSearch,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
MangaAndSourceTitlesLarge(
|
MangaAndSourceTitlesLarge(
|
||||||
appBarPadding = appBarPadding,
|
appBarPadding = appBarPadding,
|
||||||
coverDataProvider = coverDataProvider,
|
manga = manga,
|
||||||
onCoverClick = onCoverClick,
|
|
||||||
title = title,
|
|
||||||
doSearch = doSearch,
|
|
||||||
author = author,
|
|
||||||
artist = artist,
|
|
||||||
status = status,
|
|
||||||
sourceName = sourceName,
|
sourceName = sourceName,
|
||||||
isStubSource = isStubSource,
|
isStubSource = isStubSource,
|
||||||
|
onCoverClick = onCoverClick,
|
||||||
|
doSearch = doSearch,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +171,7 @@ fun MangaActionRow(
|
|||||||
onEditCategory: (() -> Unit)?,
|
onEditCategory: (() -> Unit)?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
|
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = DISABLED_ALPHA)
|
||||||
|
|
||||||
// TODO: show something better when using custom interval
|
// TODO: show something better when using custom interval
|
||||||
val nextUpdateDays = remember(nextUpdate) {
|
val nextUpdateDays = remember(nextUpdate) {
|
||||||
@ -271,7 +266,8 @@ fun ExpandableMangaDescription(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 8.dp)
|
.padding(top = 8.dp)
|
||||||
.padding(vertical = 12.dp)
|
.padding(vertical = 12.dp)
|
||||||
.animateContentSize(),
|
.animateContentSize(animationSpec = spring())
|
||||||
|
.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
var tagSelected by remember { mutableStateOf("") }
|
var tagSelected by remember { mutableStateOf("") }
|
||||||
@ -335,15 +331,11 @@ fun ExpandableMangaDescription(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MangaAndSourceTitlesLarge(
|
private fun MangaAndSourceTitlesLarge(
|
||||||
appBarPadding: Dp,
|
appBarPadding: Dp,
|
||||||
coverDataProvider: () -> Manga,
|
manga: Manga,
|
||||||
onCoverClick: () -> Unit,
|
|
||||||
title: String,
|
|
||||||
doSearch: (query: String, global: Boolean) -> Unit,
|
|
||||||
author: String?,
|
|
||||||
artist: String?,
|
|
||||||
status: Long,
|
|
||||||
sourceName: String,
|
sourceName: String,
|
||||||
isStubSource: Boolean,
|
isStubSource: Boolean,
|
||||||
|
onCoverClick: () -> Unit,
|
||||||
|
doSearch: (query: String, global: Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -353,19 +345,22 @@ private fun MangaAndSourceTitlesLarge(
|
|||||||
) {
|
) {
|
||||||
MangaCover.Book(
|
MangaCover.Book(
|
||||||
modifier = Modifier.fillMaxWidth(0.65f),
|
modifier = Modifier.fillMaxWidth(0.65f),
|
||||||
data = coverDataProvider(),
|
data = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(manga)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
contentDescription = stringResource(MR.strings.manga_cover),
|
contentDescription = stringResource(MR.strings.manga_cover),
|
||||||
onClick = onCoverClick,
|
onClick = onCoverClick,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
MangaContentInfo(
|
MangaContentInfo(
|
||||||
title = title,
|
title = manga.title,
|
||||||
doSearch = doSearch,
|
author = manga.author,
|
||||||
author = author,
|
artist = manga.artist,
|
||||||
artist = artist,
|
status = manga.status,
|
||||||
status = status,
|
|
||||||
sourceName = sourceName,
|
sourceName = sourceName,
|
||||||
isStubSource = isStubSource,
|
isStubSource = isStubSource,
|
||||||
|
doSearch = doSearch,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -374,15 +369,11 @@ private fun MangaAndSourceTitlesLarge(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MangaAndSourceTitlesSmall(
|
private fun MangaAndSourceTitlesSmall(
|
||||||
appBarPadding: Dp,
|
appBarPadding: Dp,
|
||||||
coverDataProvider: () -> Manga,
|
manga: Manga,
|
||||||
onCoverClick: () -> Unit,
|
|
||||||
title: String,
|
|
||||||
doSearch: (query: String, global: Boolean) -> Unit,
|
|
||||||
author: String?,
|
|
||||||
artist: String?,
|
|
||||||
status: Long,
|
|
||||||
sourceName: String,
|
sourceName: String,
|
||||||
isStubSource: Boolean,
|
isStubSource: Boolean,
|
||||||
|
onCoverClick: () -> Unit,
|
||||||
|
doSearch: (query: String, global: Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -395,7 +386,10 @@ private fun MangaAndSourceTitlesSmall(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sizeIn(maxWidth = 100.dp)
|
.sizeIn(maxWidth = 100.dp)
|
||||||
.align(Alignment.Top),
|
.align(Alignment.Top),
|
||||||
data = coverDataProvider(),
|
data = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(manga)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
contentDescription = stringResource(MR.strings.manga_cover),
|
contentDescription = stringResource(MR.strings.manga_cover),
|
||||||
onClick = onCoverClick,
|
onClick = onCoverClick,
|
||||||
)
|
)
|
||||||
@ -403,13 +397,13 @@ private fun MangaAndSourceTitlesSmall(
|
|||||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
) {
|
) {
|
||||||
MangaContentInfo(
|
MangaContentInfo(
|
||||||
title = title,
|
title = manga.title,
|
||||||
doSearch = doSearch,
|
author = manga.author,
|
||||||
author = author,
|
artist = manga.artist,
|
||||||
artist = artist,
|
status = manga.status,
|
||||||
status = status,
|
|
||||||
sourceName = sourceName,
|
sourceName = sourceName,
|
||||||
isStubSource = isStubSource,
|
isStubSource = isStubSource,
|
||||||
|
doSearch = doSearch,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,12 +412,12 @@ private fun MangaAndSourceTitlesSmall(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun ColumnScope.MangaContentInfo(
|
private fun ColumnScope.MangaContentInfo(
|
||||||
title: String,
|
title: String,
|
||||||
doSearch: (query: String, global: Boolean) -> Unit,
|
|
||||||
author: String?,
|
author: String?,
|
||||||
artist: String?,
|
artist: String?,
|
||||||
status: Long,
|
status: Long,
|
||||||
sourceName: String,
|
sourceName: String,
|
||||||
isStubSource: Boolean,
|
isStubSource: Boolean,
|
||||||
|
doSearch: (query: String, global: Boolean) -> Unit,
|
||||||
textAlign: TextAlign? = LocalTextStyle.current.textAlign,
|
textAlign: TextAlign? = LocalTextStyle.current.textAlign,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@ -649,7 +643,7 @@ private fun TagsChip(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
|
CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
|
||||||
SuggestionChip(
|
SuggestionChip(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
@ -31,8 +31,6 @@ import tachiyomi.i18n.MR
|
|||||||
import tachiyomi.presentation.core.components.material.TextButton
|
import tachiyomi.presentation.core.components.material.TextButton
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
|
||||||
import tachiyomi.presentation.core.util.isScrolledToStart
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ScanlatorFilterDialog(
|
fun ScanlatorFilterDialog(
|
||||||
@ -96,8 +94,8 @@ fun ScanlatorFilterDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
||||||
if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
properties = DialogProperties(
|
properties = DialogProperties(
|
||||||
@ -110,8 +108,14 @@ fun ScanlatorFilterDialog(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FlowRow {
|
FlowRow {
|
||||||
TextButton(onClick = mutableExcludedScanlators::clear) {
|
if (mutableExcludedScanlators.isEmpty()) {
|
||||||
Text(text = stringResource(MR.strings.action_reset))
|
TextButton(onClick = { mutableExcludedScanlators.addAll(availableScanlators) }) {
|
||||||
|
Text(text = stringResource(MR.strings.action_select_all))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TextButton(onClick = mutableExcludedScanlators::clear) {
|
||||||
|
Text(text = stringResource(MR.strings.action_reset))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
TextButton(onClick = onDismissRequest) {
|
TextButton(onClick = onDismissRequest) {
|
||||||
|
@ -26,7 +26,7 @@ import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
|||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
import eu.kanade.tachiyomi.ui.more.DownloadQueueState
|
||||||
import tachiyomi.core.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
@ -75,7 +75,7 @@ private fun NewUpdateScreenPreview() {
|
|||||||
changelogInfo = """
|
changelogInfo = """
|
||||||
## Yay
|
## Yay
|
||||||
Foobar
|
Foobar
|
||||||
|
|
||||||
### More info
|
### More info
|
||||||
- Hello
|
- Hello
|
||||||
- World
|
- World
|
||||||
|
@ -14,11 +14,13 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.ListItemDefaults
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
@ -28,19 +30,24 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState
|
||||||
|
import eu.kanade.tachiyomi.core.security.PrivacyPreferences
|
||||||
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
|
import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
internal class PermissionStep : OnboardingStep {
|
internal class PermissionStep : OnboardingStep {
|
||||||
|
|
||||||
|
private val privacyPreferences: PrivacyPreferences by injectLazy()
|
||||||
|
|
||||||
private var notificationGranted by mutableStateOf(false)
|
private var notificationGranted by mutableStateOf(false)
|
||||||
private var batteryGranted by mutableStateOf(false)
|
private var batteryGranted by mutableStateOf(false)
|
||||||
|
|
||||||
@ -73,7 +80,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_install_apps),
|
title = stringResource(MR.strings.onboarding_permission_install_apps),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_install_apps_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_install_apps_description),
|
||||||
granted = installGranted,
|
granted = installGranted,
|
||||||
@ -89,7 +96,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
// no-op. resulting checks is being done on resume
|
// no-op. resulting checks is being done on resume
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_notifications),
|
title = stringResource(MR.strings.onboarding_permission_notifications),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_notifications_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_notifications_description),
|
||||||
granted = notificationGranted,
|
granted = notificationGranted,
|
||||||
@ -97,7 +104,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionItem(
|
PermissionCheckbox(
|
||||||
title = stringResource(MR.strings.onboarding_permission_ignore_battery_opts),
|
title = stringResource(MR.strings.onboarding_permission_ignore_battery_opts),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_ignore_battery_opts_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_ignore_battery_opts_description),
|
||||||
granted = batteryGranted,
|
granted = batteryGranted,
|
||||||
@ -109,6 +116,29 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||||
|
)
|
||||||
|
|
||||||
|
val crashlyticsPref = privacyPreferences.crashlytics()
|
||||||
|
val crashlytics by crashlyticsPref.collectAsState()
|
||||||
|
PermissionSwitch(
|
||||||
|
title = stringResource(MR.strings.onboarding_permission_crashlytics),
|
||||||
|
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
|
||||||
|
granted = crashlytics,
|
||||||
|
onToggleChange = crashlyticsPref::set,
|
||||||
|
)
|
||||||
|
|
||||||
|
val analyticsPref = privacyPreferences.analytics()
|
||||||
|
val analytics by analyticsPref.collectAsState()
|
||||||
|
PermissionSwitch(
|
||||||
|
title = stringResource(MR.strings.onboarding_permission_analytics),
|
||||||
|
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
|
||||||
|
granted = analytics,
|
||||||
|
onToggleChange = analyticsPref::set,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +157,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PermissionItem(
|
private fun PermissionCheckbox(
|
||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
granted: Boolean,
|
granted: Boolean,
|
||||||
@ -157,4 +187,26 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun PermissionSwitch(
|
||||||
|
title: String,
|
||||||
|
subtitle: String,
|
||||||
|
granted: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onToggleChange: (Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
ListItem(
|
||||||
|
modifier = modifier,
|
||||||
|
headlineContent = { Text(text = title) },
|
||||||
|
supportingContent = { Text(text = subtitle) },
|
||||||
|
trailingContent = {
|
||||||
|
Switch(
|
||||||
|
checked = granted,
|
||||||
|
onCheckedChange = onToggleChange,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user