Compare commits
1625 Commits
Author | SHA1 | Date | |
---|---|---|---|
6294521460 | |||
1d6dc1e8b0 | |||
da4584e193 | |||
935f1fcf3f | |||
093e8e5c5a | |||
b372657238 | |||
dae7d17966 | |||
4c6bc8e830 | |||
8a3b610775 | |||
ac432e2e94 | |||
28093935d8 | |||
7028b8673a | |||
2dc8cf000b | |||
f76a3ad15a | |||
f33aa1ac92 | |||
05012de569 | |||
0d19308054 | |||
aa6a4953c5 | |||
046f09c4bd | |||
eddf07f9ac | |||
22b5fb58ff | |||
4f06c1cc09 | |||
7913679f9d | |||
0893609ad2 | |||
85d168ed5e | |||
d3691cc256 | |||
7f9406aec9 | |||
563bc02113 | |||
b702603965 | |||
2e2f1ed82d | |||
b2765a00d2 | |||
0e6d6c087e | |||
4f7122d6f0 | |||
b763d3e2c2 | |||
9957fff2fb | |||
debca74e0d | |||
1313ff7a16 | |||
793d7fbe40 | |||
b12ee027ea | |||
d7a1ae2734 | |||
7566918ee7 | |||
7b70b40d30 | |||
cd0481592c | |||
b93746b01e | |||
2b0c28938b | |||
4db3817782 | |||
ec07843f0c | |||
b08b22fdcc | |||
919607cd06 | |||
d91c7b6093 | |||
fab8b17d99 | |||
8b28a9bcee | |||
79e25451bd | |||
0dda64b9d8 | |||
181dbbb638 | |||
3ce013fa19 | |||
fe22f5aa37 | |||
1dd81ef1e1 | |||
2d0be5b0c9 | |||
4d7350e318 | |||
49b2b346b6 | |||
badc229a23 | |||
277d8bad8e | |||
8b48d1016b | |||
a96fbba3dc | |||
d8a530266f | |||
e1724d1aa0 | |||
1a5b4c2804 | |||
2cd52d5a1f | |||
ebfbbf0741 | |||
eeb683069a | |||
7e71a34256 | |||
29ee53f461 | |||
6a223f34a0 | |||
c335ea9103 | |||
c97fe71e29 | |||
8e81a5e68b | |||
b08270d523 | |||
34d1e6fa27 | |||
a80965f7f1 | |||
59ee61039b | |||
b7a96e6946 | |||
31a3f9e051 | |||
42e45e6020 | |||
e8c9cb2c2e | |||
d592ab2e87 | |||
9d6ed93daa | |||
34efa8d901 | |||
bfc8320aa4 | |||
29ec7c125a | |||
dce6aacf02 | |||
503d0be667 | |||
82fd89cee6 | |||
643f95f046 | |||
9c81f2486c | |||
e59d2d381d | |||
da90064c94 | |||
d53a3828b1 | |||
c283abefb0 | |||
4bc593861c | |||
5a9367603b | |||
c01e9f3e92 | |||
ae9753a1ea | |||
1fe4d6cbd4 | |||
2c5f28f277 | |||
3a3abc6854 | |||
d9a550b935 | |||
1617f8eb49 | |||
77faab14b1 | |||
d60802721b | |||
19af85ab61 | |||
4a7fe44e0e | |||
c5655e8803 | |||
d3973f4ad8 | |||
bb230fd6a7 | |||
e526fd44c6 | |||
f61f039a45 | |||
79eb02d8f0 | |||
814584d35b | |||
8751307301 | |||
bcff2262b3 | |||
04454ecdbe | |||
69320e4d09 | |||
e86aeee9c4 | |||
be37f214d8 | |||
1a833e88b1 | |||
4c84878adc | |||
054198e78f | |||
d522d81164 | |||
40fe5f8437 | |||
3a100c7816 | |||
ead9c0cd47 | |||
b4ad9ae063 | |||
7f2cfb5eb2 | |||
cc96f859bc | |||
386a714ffe | |||
a807722838 | |||
3bd8d3ecb7 | |||
8ea95cb27f | |||
e280fd63b6 | |||
addb4ae9ad | |||
d6dfd24548 | |||
88aff2c77f | |||
81effea01c | |||
9aef08c333 | |||
dcddac5daa | |||
e6d96bd348 | |||
1909126921 | |||
36d5ee0763 | |||
5a91d5c611 | |||
e332590b1b | |||
0106750503 | |||
39982c4063 | |||
d1a970e3f3 | |||
9df21583dc | |||
57e6e198b8 | |||
3a648e4fa5 | |||
6159bc3636 | |||
3cfc2be104 | |||
9580a00aa6 | |||
cb2b0464d0 | |||
ef7992f912 | |||
261bbef997 | |||
a5349a881b | |||
2ca2cec02b | |||
2f4bb7cadb | |||
bb4d9fc81a | |||
ee134fce58 | |||
79e711efc2 | |||
a1c6089791 | |||
06efc3b25c | |||
f508d10ad1 | |||
22d8aad598 | |||
76dcf90340 | |||
f33a6d2520 | |||
41ae8505fe | |||
2914d166fe | |||
328ec8c752 | |||
78f9a84b14 | |||
eedece5adf | |||
9d6ddb5d91 | |||
b8b053b1d7 | |||
ed9e13a365 | |||
371c1432e2 | |||
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 | |||
a605a4ec75 | |||
c83037eeab | |||
25c76f5612 | |||
0d449a9b1d | |||
62cb12a3f1 | |||
9ec4dc5758 | |||
f594f1994b | |||
96b85962e3 | |||
f77e0e2d00 | |||
ce60ac150b | |||
19afd8c9ca | |||
5067160132 | |||
c9906491fb | |||
e51013d2a4 | |||
1aa75f22d0 | |||
8c910f2a2c | |||
dfb3091e38 | |||
98bdef230a | |||
4b594fc11f | |||
71931cf697 | |||
87e3525f88 | |||
a9c7cbf2c4 | |||
e63a52b8e3 | |||
49991d38d9 | |||
33c62ab711 | |||
899bd26956 | |||
a37f3eb709 | |||
9ae71dfe93 | |||
c65a9aecf5 | |||
02e50411de | |||
6e822dfd5b | |||
7292dadd5f | |||
b1067b942e | |||
d6c4af89c4 | |||
cf6f7c521c | |||
c6601c1f94 | |||
68899aea61 | |||
c3edf9b5d0 | |||
97e04392d3 | |||
3d178737b1 | |||
bf737cf95c | |||
c91ec9a33b | |||
a8040cb21a | |||
f60782f11f | |||
7d6e1bdafc | |||
5854ad97e0 | |||
4b8fa059d5 | |||
3dc2f9a711 | |||
8033a94ee2 | |||
028da099dd | |||
e6c6c32d81 | |||
bce6af62fc | |||
6510a9617a | |||
14510f1d26 | |||
f115edf2ea | |||
8a8362203f | |||
f3336fc5c3 | |||
727289c8eb | |||
9c91ddd4e3 | |||
3ea026e311 | |||
36f307e3bb | |||
89678ebb17 | |||
80b7d14af1 | |||
bbd8098a61 | |||
f8ef0f143b | |||
a3ef3604ee | |||
c4ceda59df | |||
7e053b5862 | |||
ac8ed3c028 | |||
8321ff6000 | |||
9c899e97a9 | |||
556f5a42a7 | |||
850813820c | |||
dba5e6fbfd | |||
c17ada2c98 | |||
32bed9b041 | |||
e0a0942015 | |||
8409ebe4eb | |||
493da5c3f4 | |||
4e221397ce | |||
8a7d6a328a | |||
22589a9c30 | |||
ec478cbb1b | |||
b5e3f429fc | |||
83130f9bf9 | |||
6f34c5e894 | |||
74931fad86 | |||
6ab8e1e73d | |||
1cdaa761b7 | |||
901b77f55c | |||
54f4711f7b | |||
3d0d5c0472 | |||
f0a0ecfd4a | |||
f3b7eaf4a3 | |||
5bba7af24a | |||
32c3269291 | |||
a1e84911be | |||
6bb77bcf1a | |||
ccec5c3efe | |||
8735836498 | |||
8b65fd5751 | |||
f0710df356 | |||
3afcee81f4 | |||
9c120e6231 | |||
4b208fc7ce | |||
a9b0ac43c4 | |||
fca4f25122 | |||
bfb0d31ff6 | |||
8939274b5c | |||
087da2b2f3 | |||
4571dc6b56 | |||
f31bc47757 | |||
950b4a6c90 | |||
2d7650537d | |||
80d6d412f3 | |||
446b146f95 | |||
6887d98f15 | |||
6d74a86711 | |||
5908bd1930 | |||
1a559124eb | |||
54ba1d719e | |||
93cbeca5c0 | |||
19f0175a56 | |||
bf3899d04a | |||
dcf0379496 | |||
9f90ee358b | |||
565317d99c | |||
83a67feb48 | |||
a51108cbe8 | |||
b9fd416fc6 | |||
c10cd6c808 | |||
c62cd6e997 | |||
7ae17e6aac | |||
f20980b4c9 | |||
02cd2d2ca3 | |||
3847d4f4cf | |||
f9b57800b1 | |||
387159b5af | |||
09531e7f5a | |||
c6356fe4b2 | |||
ff3bc66055 | |||
13b3bec8ad | |||
8aaf8df708 | |||
c00f05a1c1 | |||
db3ddf07ee | |||
cd16522805 | |||
5fec881387 | |||
3ac68e810d | |||
e6fe5c827c | |||
65e1e2cf4f | |||
e36a2c68f1 | |||
add9357257 | |||
ad3d915fc5 | |||
36f400d542 | |||
dd1a19745a | |||
58daedc89e | |||
d20a8fcf13 | |||
e56bf82c31 | |||
0f9895eec8 | |||
f776c36e70 | |||
1ef01b53f2 | |||
720169dce3 | |||
0d09039e5f | |||
cc56fde9fe | |||
3a0b3de175 | |||
47e544b710 | |||
44d6c4fe44 | |||
e5693ed668 | |||
8c21aa86e9 | |||
f7c5b42435 | |||
e3404cd3d3 | |||
8b57169e92 | |||
ab9a26f6bd | |||
8779b263ab | |||
3135db4bb2 | |||
734cb0be6e | |||
1f259f9298 | |||
427fbfdf5e | |||
0c860c0fe9 | |||
5b2a099203 | |||
ccadfc8fe5 | |||
3aead3a2a9 | |||
6a48fed170 | |||
ea1684133b | |||
e5263d0345 | |||
24e1b4034e | |||
dfa5c229b3 | |||
87be54aa4a | |||
82d9ae31bd | |||
e5518b7615 | |||
e5a22eafe7 | |||
7a52afd223 | |||
296201d6b7 | |||
162b639705 | |||
5dda32bb81 | |||
8ce8b60092 | |||
8ff2c01bf2 | |||
e22eebfd02 | |||
4fcdde4913 | |||
e41668862f | |||
a74a689c90 | |||
d85a76484c | |||
82bdf63419 | |||
9ce0bc6b5f | |||
bf524595e2 | |||
27c4db752c | |||
ca54984344 | |||
46aeab9a7a | |||
f365b53a0f | |||
d4dfa9a2c2 | |||
cf9e60fd92 | |||
21ae04d25d | |||
f1778ac5b4 | |||
ba10093ddc | |||
a5c9469698 | |||
75314c78e0 | |||
53edae1b6b | |||
356fc5b524 | |||
60150423d7 | |||
bcc42dd259 | |||
d59cb9c1e3 | |||
3006604922 | |||
1fbf8ca079 | |||
695813ef7d | |||
e3b70ca08d | |||
8857b7e0c1 | |||
4a7c20f5a0 | |||
29368fc953 | |||
0696e4bce0 | |||
255ed50685 | |||
00afee83b8 | |||
0d1bced122 | |||
46e734fc8e | |||
c39ae21f4a | |||
69aa13bc56 | |||
2c032ff70d | |||
0af4703b78 | |||
ea15bc782a | |||
9ec0f73e87 | |||
f9fb034330 | |||
6eb5a25ea1 | |||
45d8411f98 | |||
d9e2317e62 | |||
336221a972 | |||
dd998be1e7 | |||
3c3b09209c | |||
4a6571d310 | |||
cb67f1de52 | |||
402e2c47fb | |||
58b2895ec9 | |||
00b2853d3d | |||
634ceeec50 | |||
d7442d771b | |||
8f22480ec9 | |||
9d974273af | |||
3a8aa3e8cd | |||
9e67abcc8a | |||
d0bcd30909 | |||
b97aa23548 | |||
e6ca54fd04 | |||
4502902fb0 | |||
5f34539525 | |||
953f5fb025 | |||
4f3a0b3523 | |||
1d144e6767 | |||
056dbaefda | |||
3a15c6b843 | |||
db20d04c4b | |||
c5e8c9f01f | |||
64c50c1283 | |||
4146c4c31d | |||
69223df27c | |||
4b225a4ff1 | |||
4a2ee0b596 | |||
8644d90bd4 | |||
f30ab56fd0 | |||
5d91b77c93 | |||
aca36f9625 | |||
d5e8c38075 | |||
6d538db5f2 | |||
b3d7c92475 | |||
d862d83511 | |||
8a1625ec79 | |||
2ee895ee3c | |||
7cf2ce2994 | |||
cb8ea5eab0 | |||
ce7bf396eb | |||
1aa5222c99 | |||
298c49f3ab | |||
ce5e10be95 | |||
64ad25d1b5 | |||
4868dd2d03 | |||
443d56f69b | |||
7457a18aee | |||
d80ba2e807 | |||
118d3b7fcc | |||
eed57b80be | |||
c46c39d4ae | |||
d7d7a6d2fc | |||
17b90d2491 | |||
9ecec5d468 | |||
0bdd3f79d4 | |||
7dccde0930 | |||
c8d68590db | |||
94448faf97 | |||
28028c789c | |||
f8834ee764 | |||
7c703b17d3 | |||
f77ade7dda | |||
91712daee8 | |||
8057f067b9 | |||
548f7f415a | |||
d9c0b1ce7d | |||
0a0b686119 | |||
092d930175 | |||
3b7ed9bc6d | |||
012854dd1e | |||
6d1e520c6c | |||
dcc3141080 | |||
7326598475 | |||
fcba2306e9 | |||
f84868a264 | |||
15423bfc84 | |||
8e4cedf173 | |||
19965e0bdb | |||
3a35c13575 | |||
489d22720a | |||
e1b3345b94 | |||
c53172265b | |||
8626a55fe4 | |||
1302461518 | |||
8f3681d79f | |||
c4ce3dd46f | |||
22df12a680 | |||
e572abb041 | |||
ea99d77fda | |||
2bf77f1d81 | |||
f79f0a7e97 | |||
82a9d36df7 | |||
447bcb28ef | |||
0be7ac5871 | |||
d18022c259 | |||
5619a4c0d9 | |||
8a7bbfddda | |||
0026f96fad | |||
c492efcb31 | |||
c386d375de | |||
540fb1bb7c | |||
81448f5d01 | |||
7c01201055 | |||
90d3dd2242 | |||
97b4d1f13d | |||
79b37df647 | |||
b7d282235d | |||
77ebc362f6 | |||
8568d5d6c3 | |||
7ed99fbbd6 | |||
94cba9324c | |||
6dab94a937 | |||
0f42b9f154 | |||
730f3a6e52 | |||
72024aa44a | |||
9c688b08c0 | |||
c66a4fa7a7 | |||
e47f4cc177 | |||
6462472d16 | |||
78aa50bb35 | |||
7f0f67d752 | |||
df332860b8 | |||
8a8afa46e9 | |||
509bee0563 | |||
afb1ee2200 | |||
66a938779d | |||
ed506f8495 | |||
c8e226acb2 | |||
86edce0d87 | |||
4e69bf993a | |||
56d2464870 | |||
5de72b7d32 | |||
de92b1351f | |||
77a8a4229c | |||
d4290f6f59 | |||
b08d604d2a | |||
9e04f14a7b | |||
6663abebaf | |||
e5f83d0c6e | |||
3ad7add3b5 | |||
66aacade9a | |||
fe3a710ed0 | |||
f9754f4f58 | |||
8824c7dbe3 | |||
ccc9a5a052 | |||
f5e0cee36c | |||
36f1e0e476 | |||
2dd2db7225 | |||
3d0e750519 | |||
26c5d761da | |||
1668be8587 | |||
86a3fc77c6 | |||
cc018cee18 | |||
d9d143e6be | |||
3f0db60a99 | |||
87f3d4bd05 | |||
5c3d655d9e | |||
66b175a3c8 | |||
3cd3f45c8a | |||
816d7815e9 | |||
dbc7fe4d54 | |||
d29b7c4e57 | |||
772db51593 | |||
87530f506e | |||
98d6ce2eaf | |||
7644d7c31e | |||
dde2f42138 | |||
6922792ad1 | |||
f32243899d | |||
13dc54df70 | |||
6d9a8a30e9 | |||
2bf263e301 | |||
c06beac660 | |||
74f74eef56 | |||
3aafec482c | |||
ed80ac3154 | |||
eeeaae4570 | |||
d1c956401c | |||
1be7949275 | |||
5572b28d01 | |||
4e68b62881 | |||
4e31e6a2fa | |||
bc692ebfc6 | |||
96f6a5abc2 | |||
3411ac40c0 | |||
8a6a104987 | |||
efa7a3a167 | |||
67bc81ebde | |||
0a3ce8ebe4 | |||
3ebf39bd55 | |||
8f395d98e7 | |||
26b3eb696c | |||
627f07408e | |||
7146913c71 | |||
39c6bcccd8 | |||
6259bbaa5e | |||
cb4b8ac0dc | |||
4b7acdb022 | |||
af0fdfa3b7 | |||
d874f20362 | |||
8680accd8e | |||
7308090288 | |||
400ca48456 | |||
9b6567f5e4 | |||
7798186c32 | |||
9dc66c7c8d | |||
10b0ef9b6d | |||
81cd765543 | |||
c9a1bd86b5 | |||
dfbbbadfac | |||
0f21d16263 | |||
d65f9c2916 | |||
5718983f41 | |||
f7b335e4fb | |||
aa6937baf2 | |||
59f7d2273f | |||
cd91ea9b77 | |||
6a558ad119 | |||
f5936e9456 | |||
9df351da0a | |||
90325d48aa | |||
e2abf283fe | |||
db788d519d | |||
f3e9d5f346 | |||
fd30c0adcd | |||
3ad4f1114a | |||
fe90546821 | |||
3892c4caac | |||
cb639f4e90 | |||
6d69caf59e | |||
cdc1c5efa3 | |||
77bfd0c099 | |||
8ff0c9d61a | |||
b6620434b3 | |||
abae9bf37d | |||
2556e9f08c | |||
7aa172c512 | |||
81cf232bcb | |||
ee26d6dffd | |||
7b2764e8f7 | |||
46e3b9e40d | |||
8d00ff1b40 | |||
cf14831fbe | |||
7a4680603d | |||
99f12b1fbf | |||
5c73045aa4 | |||
ac306547a0 | |||
3f868c0435 | |||
262ce3473f | |||
43b9b104f5 | |||
c7f0a54a37 | |||
ca789dca0e | |||
ef7b285151 | |||
dd3ca0c131 | |||
8b46e8edad | |||
30f845139d | |||
818471b7e1 | |||
a3a3f44056 | |||
22c6dbda3f | |||
34f7caa0fc | |||
01553b1ed8 | |||
a24afa9a76 | |||
ec08ba05fc | |||
30bea8b753 | |||
09e4b5a9cd | |||
5467104b95 | |||
e0733c1a4c | |||
1cf7f9be54 | |||
fb99577836 | |||
28131ac135 | |||
e40b8d537c | |||
12e7ee9d0c | |||
54733e6ceb | |||
22e8050fff | |||
a629db2884 | |||
cbcec8c4d9 | |||
2f05f7b91f | |||
a3a9699e8a | |||
f01a312c23 | |||
0fffde50ff | |||
8775596a82 | |||
2f0133986a | |||
3bd2cad45f | |||
48f7a2de41 | |||
3aa6e7ae0e | |||
813d7e49cd | |||
710ebfb7a5 | |||
87bdee5990 | |||
efabe801be | |||
9a817e49be | |||
a577f5534f | |||
d0f52ea93d | |||
6063efd101 | |||
0759936226 | |||
1e3d9a00f2 | |||
7c62453280 | |||
226272f686 | |||
16cbcecd99 | |||
b008223661 | |||
f8cf3db4a4 | |||
a585d46e7a | |||
8cc42bce5a | |||
67c6dbea0d | |||
db33437577 | |||
8287c9d193 | |||
d32409bd6e | |||
cf3f2d0380 | |||
53c6230afe | |||
4882896f4d | |||
4d67066de3 | |||
6fe5e6e21b | |||
8c5496b53f | |||
235a587e42 | |||
3125d78706 | |||
bb8f3c63f1 | |||
20faaaa908 | |||
44cc6f11c7 | |||
bae391c2c1 | |||
0ac5f3b93c | |||
b79ef5dc79 | |||
7d26ca046f | |||
d99f4697e8 | |||
bb3fdef40b | |||
2a7cca6ea4 | |||
6ed2748846 | |||
7c90fe0f7d | |||
8a5e443ca5 | |||
a07e0df815 | |||
88e9fefa59 | |||
c0fd47b066 | |||
ee684cbef5 | |||
1f618d6634 | |||
7d4af1f8cc | |||
fe82cdb9c8 | |||
b354e37cc3 | |||
f344831d58 | |||
2eca8511cb | |||
f2b0d74b4c | |||
42bc2b07ce | |||
e2d6269a38 | |||
fcfa62f220 | |||
25b0458930 | |||
6808fbbb21 | |||
b36b3bfcab | |||
7f0ed58b54 | |||
b4393ff741 | |||
b8af1621b5 | |||
4a75f82a6f | |||
740e370465 | |||
245985bf42 | |||
344f5afd50 | |||
1c6e5605f9 | |||
0871208023 | |||
ee95c1439f | |||
e323f3c25a | |||
9766399539 | |||
fc4fd487f9 | |||
dddba7bb6f | |||
9ec8d770ea | |||
cf777d9893 | |||
0d9f8e8743 | |||
841f80f935 | |||
39a7356ed1 | |||
438054a0ec | |||
34b9c82cd0 | |||
405a75438a | |||
39e4568460 | |||
0d96791a84 | |||
531e1c62bb | |||
1a1f16f44a | |||
431f8772f8 | |||
8a5382042c | |||
8f4bc71cf7 | |||
0ac38297f4 | |||
4c65c2311e | |||
f48f212001 | |||
c90f344910 | |||
a458bd9fdb | |||
ed5a56be60 | |||
899fe57f15 | |||
bac42edabb | |||
8735f3566f | |||
46efd4c134 | |||
dfd38db7e3 | |||
0189fc1f66 | |||
929a881943 | |||
152fdec855 | |||
9c07451d95 | |||
e3b2720924 | |||
3ae1e37c40 | |||
d8998aacb4 | |||
efdff9a21a | |||
1824adb2ed | |||
38445673f3 | |||
5a9889b562 | |||
5ca7c39751 | |||
44609c494c | |||
0810d3db69 | |||
d4fb9995ef | |||
22a4372583 | |||
a4d86a2e1e | |||
b8716ff6fe | |||
73118d4af7 | |||
c27bf4e866 | |||
f50f5c4b54 | |||
fb38d30775 | |||
a3a9c8ac8e | |||
b4bb855675 | |||
6263a52777 | |||
96defd6b05 | |||
8df9bce1b4 | |||
bcd90be525 | |||
22afae4449 | |||
8fae92034e | |||
ce81b76150 | |||
f70d5ea976 | |||
dbbf6c5de0 | |||
2379df7e60 | |||
e3ce3ff418 | |||
4395202703 | |||
84acae27b7 | |||
71f6e07e71 | |||
6f59c6c6bb | |||
d36cf5ce15 | |||
332d9ff61b | |||
7bb1ccf6f7 | |||
05a7d5174a | |||
b051e37ab7 | |||
44383ff950 | |||
1b25290d39 | |||
2f5eb73d29 | |||
f0dd33ee4c | |||
e15b945e16 | |||
5c7d88c2ed | |||
bbe0ab1dd0 | |||
cb2d43c0d1 | |||
fce9cb820c | |||
08e4863d94 | |||
9a10656bf0 | |||
3c79777e66 | |||
f5ad95d78a | |||
14c465d36f | |||
921a988c4a | |||
99378ddf20 | |||
c623258e8c | |||
6ce42dc167 | |||
f63573f25f | |||
b328f0e344 | |||
02864ebd60 | |||
eed91f6360 | |||
f317193bec | |||
f459515dd7 | |||
9339ea4196 | |||
6bdc1b676e | |||
7451c13edd | |||
ccd4143d9d | |||
c590f55030 | |||
c21813a8b5 | |||
2cb08e6bb1 | |||
058ee4c86b | |||
ea6e5eebac | |||
9cc25ff345 | |||
c9805b8612 | |||
41c89eb61d | |||
392c3492b3 | |||
f7cd3929a3 | |||
20bec66a9d | |||
3ce9a9ff97 | |||
a8f17a3fab | |||
ef3d2c14b4 | |||
44619febd3 | |||
c48accb357 | |||
418e6a8b3a | |||
67b4e53a58 | |||
d62d94f587 | |||
265934d77a | |||
2a218cca90 | |||
e23cc8f83a | |||
0b125b7106 | |||
320587e36e | |||
26f3995595 | |||
94c94b2d88 | |||
41cc1fe723 | |||
03a344e9c1 | |||
add228407f | |||
2c6e025063 | |||
ba30dfe7e2 | |||
97e6f1ea9a | |||
5c1a81d8ca | |||
7e56cba060 | |||
dc569fb20a | |||
c6ac992798 | |||
8a18e10cc2 | |||
d6b9711e45 | |||
8ab7e63293 | |||
4bcd623829 | |||
18acf66cb8 | |||
4816b4b53a | |||
60d8650860 | |||
bfb7b5afd5 | |||
a2627d70af | |||
6662a97b2f | |||
564a0980b9 | |||
e3fbd26880 | |||
c1e23ec18e | |||
b7cd7b8b4e | |||
776d36caf1 | |||
182e642cfc | |||
88bf1a706b | |||
d25ba23079 | |||
75460e01c8 | |||
c9bd3a5314 | |||
7c6a5dc43b | |||
274218cf22 | |||
c7d6509565 | |||
bc0b9e536a | |||
7a1b599462 | |||
1dd62af188 | |||
6f1099b710 | |||
be8e2f119f | |||
18f9e5ba6b | |||
d1bf857079 | |||
1814b3b22c | |||
be54b8862e | |||
1a61130f0b | |||
1de4bc9586 | |||
1986042277 | |||
e932983494 | |||
35d381144d | |||
0bcc22822d | |||
1ff78173f7 | |||
ee45f46193 | |||
290efb0283 | |||
8d7a7919a9 | |||
953720472f | |||
f94d902bb6 | |||
da25322572 | |||
cb4699a5bb | |||
2e5efadf42 | |||
e5e18c2030 | |||
ac0596a53d | |||
7ec5a51eb8 | |||
3cca460282 | |||
d703fb7946 | |||
859601a46e | |||
cdc160afc2 | |||
14d1bcacc9 | |||
abd23b6826 | |||
7d8a865cac | |||
dfdb688b43 | |||
c955ac6a66 | |||
f3ca4e76a8 | |||
2769525b2c | |||
843e748de3 | |||
d160cfaa0e | |||
81af97df77 | |||
18e55aa25f | |||
4d3e13b0d1 | |||
a335b4ee9e | |||
47a2d06682 | |||
ce66ed0389 | |||
c0f94ae8af | |||
ed32a511e7 | |||
17ed4873e8 | |||
09acc53483 | |||
bebd4be43d | |||
9b77759f24 | |||
e458de5e9c | |||
737a303df7 | |||
477dd37981 | |||
e917349bb7 | |||
ad4912803b | |||
f96f0c5889 | |||
2b9acadc5b | |||
9caa0d147b | |||
c6e5f8abd9 | |||
1abf01c4a0 | |||
b41565f879 | |||
f03a834136 | |||
f7f2072621 | |||
5b2e937d5f | |||
f27dc19b37 | |||
2368c50ebb | |||
0505906e7a | |||
4efca04765 | |||
b12c7cf963 | |||
487622c592 | |||
26d422b0ae | |||
79a7b68837 | |||
63048d2f0b | |||
79662a5866 | |||
ed6809fa28 | |||
86b9262a7e | |||
7ec87e76db | |||
a0e76d2fd9 | |||
ec3ce74af8 | |||
83a4e34095 | |||
84a0044d51 | |||
92132c59f5 | |||
36ae388332 | |||
bd47eafeec | |||
9432d2d06a | |||
fa61c8fe6f | |||
92bd98e45f | |||
fd7c993b0b | |||
779df32e98 | |||
f4e843f114 | |||
c0e2eb211d | |||
0bd56ab77c | |||
6b03dca5f4 | |||
bd7b21337c | |||
60a3ba5a5c | |||
7c2eb0b881 | |||
93523ef50b | |||
10d7349506 | |||
3d7c136320 | |||
a6d6a5ed87 | |||
ec49411bee | |||
3f7911235c | |||
727399611d | |||
94232a4937 | |||
07fdb74fbc | |||
d400ac2a49 | |||
dd71c76a8f | |||
58a0add4f6 | |||
bfe143015a | |||
e3cf863230 | |||
ee818bc7c5 | |||
f816196df2 | |||
753bf7de5d | |||
3634b52e3a | |||
ef863335e6 | |||
ceaf579cb0 | |||
b49280e347 | |||
d3dadf71e8 | |||
ffa8c8fd07 | |||
0ef7650c1a | |||
4635e58405 | |||
dc2eaf0788 | |||
d02b0ca2db | |||
4d607c4aed | |||
be4072c86b | |||
2970eca9e4 | |||
42954609b9 | |||
6348cbaeb7 | |||
a7cb33d8c9 | |||
ec46b2281b | |||
3a2dc46ff0 | |||
e052bdef96 | |||
d522d6d545 | |||
7b118eba22 | |||
f6e6a7ddf1 | |||
5ce64ac7ff | |||
1671a56f42 | |||
ab6dfe9e25 | |||
bff98ca768 | |||
32b9b261f0 | |||
23432e4405 | |||
34a586ce48 | |||
ad762f8303 | |||
389b039679 | |||
ef9dacde79 | |||
13bb45b4be | |||
bd2cb97179 | |||
0d8f1c8560 |
@ -1,7 +1,28 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.xml]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# noinspection EditorConfigKeyCorrectness
|
||||||
[*.{kt,kts}]
|
[*.{kt,kts}]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
insert_final_newline=true
|
max_line_length = 120
|
||||||
|
|
||||||
ij_kotlin_allow_trailing_comma = true
|
ij_kotlin_allow_trailing_comma = true
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_function_naming_ignore_when_annotated_with = Composable
|
||||||
|
ktlint_standard_class-signature = disabled
|
||||||
|
ktlint_standard_discouraged-comment-location = disabled
|
||||||
|
ktlint_standard_function-expression-body = disabled
|
||||||
|
ktlint_standard_function-signature = disabled
|
||||||
|
1
.github/FUNDING.yml
vendored
@ -1 +0,0 @@
|
|||||||
ko_fi: inorichi
|
|
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.14.6)
|
|
||||||
- All extensions
|
|
||||||
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
|
|
||||||
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-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.
|
|
13
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: ⚠️ Extension/source issue
|
- name: ❌ Help with Extensions
|
||||||
url: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose
|
url: https://mihon.app/docs/faq/browse/extensions
|
||||||
about: Issues and requests for extensions and sources should be opened in the tachiyomi-extensions repository instead
|
about: For extension-related questions/issues
|
||||||
- name: 📦 Tachiyomi extensions
|
- name: 🖥️ Mihon website
|
||||||
url: https://tachiyomi.org/extensions
|
url: https://mihon.app/
|
||||||
about: List of all available extensions with download links
|
|
||||||
- name: 🖥️ Tachiyomi website
|
|
||||||
url: https://tachiyomi.org/help/
|
|
||||||
about: Guides, troubleshooting, and answers to common questions
|
about: Guides, troubleshooting, and answers to common questions
|
||||||
|
24
.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:
|
||||||
|
|
||||||
@ -43,17 +43,17 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Crash logs
|
label: Crash logs
|
||||||
description: |
|
description: |
|
||||||
If you're experiencing crashes, share the crash logs from **More → Settings → Advanced** then press **Dump crash logs**.
|
If you're experiencing crashes, if possible, go to the app's **More → Settings → Advanced** page, press **Dump crash logs** and share the crash logs here.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
You can paste the crash logs in plain text or upload it as an attachment.
|
You can upload the crash log file as an attachment, or paste the crash logs in plain text if needed.
|
||||||
|
|
||||||
- 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.14.6"
|
Example: "0.18.0"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -94,13 +94,11 @@ 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 extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
- 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 tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
|
- label: I have updated the app to version **[0.18.0](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[0.14.6](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
- label: I have filled out all of the requested information in this form, including specific version numbers.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated all installed extensions.
|
- label: I understand that **Mihon does not have or fix any extensions**, and I **will not receive help** for any issues related to sources or extensions.
|
||||||
required: true
|
|
||||||
- label: I will fill out all of the requested information in this form.
|
|
||||||
required: true
|
required: true
|
||||||
|
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 extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
- label: I have updated the app to version **[0.18.0](https://github.com/mihonapp/mihon/releases/latest)**.
|
||||||
required: true
|
|
||||||
- label: I have updated the app to version **[0.14.6](https://github.com/tachiyomiorg/tachiyomi/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
After Width: | Height: | Size: 7.5 KiB |
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
Before Width: | Height: | Size: 1.1 KiB |
12
.github/renovate.json
vendored
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"config:base"
|
|
||||||
],
|
|
||||||
"schedule": ["every sunday"],
|
|
||||||
"ignoreDeps": [
|
|
||||||
"androidx.core:core-splashscreen",
|
|
||||||
"com.android.tools:r8",
|
|
||||||
"com.google.guava:guava",
|
|
||||||
"com.github.commandiron:WheelPickerCompose"
|
|
||||||
]
|
|
||||||
}
|
|
13
.github/renovate.json5
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": ["config:recommended"],
|
||||||
|
"labels": ["Dependencies"],
|
||||||
|
"semanticCommits": "disabled",
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"groupName": "GitHub Actions",
|
||||||
|
"matchManagers": ["github-actions"],
|
||||||
|
"pinDigests": true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
52
.github/workflows/build_pull_request.yml
vendored
@ -1,9 +1,13 @@
|
|||||||
name: PR build check
|
name: PR build check
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths:
|
||||||
- '**.md'
|
- '**'
|
||||||
- 'i18n/src/main/res/**/strings.xml'
|
- '!**.md'
|
||||||
|
- '!i18n/src/commonMain/moko-resources/**/strings.xml'
|
||||||
|
- '!i18n/src/commonMain/moko-resources/**/plurals.xml'
|
||||||
|
- 'i18n/src/commonMain/moko-resources/base/strings.xml'
|
||||||
|
- 'i18n/src/commonMain/moko-resources/base/plurals.xml'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||||
@ -15,25 +19,41 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build app
|
name: Build app
|
||||||
runs-on: ubuntu-latest
|
runs-on: 'ubuntu-24.04'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
|
||||||
uses: gradle/wrapper-validation-action@v1
|
|
||||||
|
|
||||||
- name: Dependency Review
|
- name: Dependency Review
|
||||||
uses: actions/dependency-review-action@v3
|
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||||
|
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: temurin
|
||||||
|
|
||||||
- name: Build app and run unit tests
|
- name: Set up gradle
|
||||||
uses: gradle/gradle-command-action@v2
|
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||||
|
|
||||||
|
- name: Check code format
|
||||||
|
run: ./gradlew spotlessCheck
|
||||||
|
|
||||||
|
- name: Build app
|
||||||
|
run: ./gradlew assembleRelease
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: ./gradlew testReleaseUnitTest
|
||||||
|
|
||||||
|
- name: Upload APK
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest
|
name: arm64-v8a-${{ github.sha }}
|
||||||
|
path: app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
|
- name: Upload mapping
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
with:
|
||||||
|
name: mapping-${{ github.sha }}
|
||||||
|
path: app/build/outputs/mapping/release
|
||||||
|
95
.github/workflows/build_push.yml
vendored
@ -2,7 +2,7 @@ name: CI
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
tags:
|
tags:
|
||||||
- v*
|
- v*
|
||||||
|
|
||||||
@ -13,75 +13,93 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build app
|
name: Build app
|
||||||
runs-on: ubuntu-latest
|
runs-on: 'ubuntu-24.04'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Set up JDK
|
||||||
uses: gradle/wrapper-validation-action@v1
|
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4.7.0
|
||||||
|
|
||||||
- name: Set up JDK 11
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: temurin
|
||||||
|
|
||||||
- name: Build app and run unit tests
|
- name: Set up gradle
|
||||||
uses: gradle/gradle-command-action@v2
|
uses: gradle/actions/setup-gradle@94baf225fe0a508e581a564467443d0e2379123b # v4.3.0
|
||||||
|
|
||||||
|
- name: Check code format
|
||||||
|
run: ./gradlew spotlessCheck
|
||||||
|
|
||||||
|
- name: Build app
|
||||||
|
run: ./gradlew assembleRelease -Pinclude-telemetry -Penable-updater
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: ./gradlew testReleaseUnitTest
|
||||||
|
|
||||||
|
- name: Upload APK
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest
|
name: arm64-v8a-${{ github.sha }}
|
||||||
|
path: app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
|
||||||
|
|
||||||
|
- name: Upload mapping
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
with:
|
||||||
|
name: mapping-${{ github.sha }}
|
||||||
|
path: app/build/outputs/mapping/release
|
||||||
|
|
||||||
# Sign APK and create release for tags
|
# Sign APK and create release for tags
|
||||||
|
|
||||||
- name: Get tag name
|
- name: Get tag name
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'tachiyomiorg/tachiyomi'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
echo "VERSION_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
echo "VERSION_TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Sign APK
|
- name: Sign APK
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'tachiyomiorg/tachiyomi'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
uses: r0adkll/sign-android-release@v1
|
uses: r0adkll/sign-android-release@f30bdd30588842ac76044ecdbd4b6d0e3e813478
|
||||||
with:
|
with:
|
||||||
releaseDirectory: app/build/outputs/apk/standard/release
|
releaseDirectory: app/build/outputs/apk/release
|
||||||
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
||||||
alias: ${{ secrets.ALIAS }}
|
alias: ${{ secrets.ALIAS }}
|
||||||
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
|
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
|
||||||
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
env:
|
||||||
|
BUILD_TOOLS_VERSION: '35.0.1'
|
||||||
|
|
||||||
- name: Clean up build artifacts
|
- name: Clean up build artifacts
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'tachiyomiorg/tachiyomi'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk tachiyomi-${{ env.VERSION_TAG }}.apk
|
mv app/build/outputs/apk/release/app-universal-release-unsigned-signed.apk mihon-${{ env.VERSION_TAG }}.apk
|
||||||
sha=`sha256sum tachiyomi-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
sha=`sha256sum mihon-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
||||||
echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_ENV
|
echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_ENV
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk tachiyomi-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
mv app/build/outputs/apk/release/app-arm64-v8a-release-unsigned-signed.apk mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
||||||
sha=`sha256sum tachiyomi-arm64-v8a-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
sha=`sha256sum mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
||||||
echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_ENV
|
echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_ENV
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk tachiyomi-armeabi-v7a-${{ env.VERSION_TAG }}.apk
|
mv app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned-signed.apk mihon-armeabi-v7a-${{ env.VERSION_TAG }}.apk
|
||||||
sha=`sha256sum tachiyomi-armeabi-v7a-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
sha=`sha256sum mihon-armeabi-v7a-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
||||||
echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_ENV
|
echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_ENV
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk tachiyomi-x86-${{ env.VERSION_TAG }}.apk
|
mv app/build/outputs/apk/release/app-x86-release-unsigned-signed.apk mihon-x86-${{ env.VERSION_TAG }}.apk
|
||||||
sha=`sha256sum tachiyomi-x86-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
sha=`sha256sum mihon-x86-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
||||||
echo "APK_X86_SHA=$sha" >> $GITHUB_ENV
|
echo "APK_X86_SHA=$sha" >> $GITHUB_ENV
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk tachiyomi-x86_64-${{ env.VERSION_TAG }}.apk
|
mv app/build/outputs/apk/release/app-x86_64-release-unsigned-signed.apk mihon-x86_64-${{ env.VERSION_TAG }}.apk
|
||||||
sha=`sha256sum tachiyomi-x86_64-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
sha=`sha256sum mihon-x86_64-${{ env.VERSION_TAG }}.apk | awk '{ print $1 }'`
|
||||||
echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV
|
echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'tachiyomiorg/tachiyomi'
|
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ env.VERSION_TAG }}
|
tag_name: ${{ env.VERSION_TAG }}
|
||||||
name: Tachiyomi ${{ env.VERSION_TAG }}
|
name: Mihon ${{ env.VERSION_TAG }}
|
||||||
body: |
|
body: |
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -94,13 +112,14 @@ jobs:
|
|||||||
| armeabi-v7a | ${{ env.APK_ARMEABI_V7A_SHA }}
|
| armeabi-v7a | ${{ env.APK_ARMEABI_V7A_SHA }}
|
||||||
| 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
|
||||||
files: |
|
files: |
|
||||||
tachiyomi-${{ env.VERSION_TAG }}.apk
|
mihon-${{ env.VERSION_TAG }}.apk
|
||||||
tachiyomi-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk
|
||||||
tachiyomi-armeabi-v7a-${{ env.VERSION_TAG }}.apk
|
mihon-armeabi-v7a-${{ env.VERSION_TAG }}.apk
|
||||||
tachiyomi-x86-${{ env.VERSION_TAG }}.apk
|
mihon-x86-${{ env.VERSION_TAG }}.apk
|
||||||
tachiyomi-x86_64-${{ env.VERSION_TAG }}.apk
|
mihon-x86_64-${{ env.VERSION_TAG }}.apk
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
env:
|
token: ${{ secrets.MIHON_BOT_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
35
.github/workflows/issue_moderator.yml
vendored
@ -1,35 +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@v1
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
]
|
|
19
.github/workflows/lock.yml
vendored
@ -1,19 +0,0 @@
|
|||||||
name: Lock threads
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Daily
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
# Manual trigger
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lock:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: dessant/lock-threads@v4
|
|
||||||
with:
|
|
||||||
github-token: ${{ github.token }}
|
|
||||||
issue-inactive-days: '2'
|
|
||||||
pr-inactive-days: '2'
|
|
23
.github/workflows/update_website.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: Update website
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
- deleted
|
||||||
|
- edited
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_website:
|
||||||
|
runs-on: 'ubuntu-24.04'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Update website
|
||||||
|
run: |
|
||||||
|
curl --fail-with-body -L \
|
||||||
|
-X POST \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.MIHON_BOT_TOKEN }}" \
|
||||||
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||||
|
https://api.github.com/repos/mihonapp/website/dispatches \
|
||||||
|
-d '{"event_type":"app_release"}'
|
24
.gitignore
vendored
@ -1,16 +1,16 @@
|
|||||||
|
# Build files
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
.kotlin
|
||||||
/.idea/workspace.xml
|
build
|
||||||
.DS_Store
|
|
||||||
.idea/
|
# IDE files
|
||||||
*iml
|
|
||||||
*.iml
|
*.iml
|
||||||
|
.idea/*
|
||||||
|
!.idea/icon.svg
|
||||||
|
/captures
|
||||||
|
|
||||||
# Built files
|
# Configuration files
|
||||||
*/build
|
local.properties
|
||||||
/build
|
|
||||||
*.apk
|
|
||||||
app/**/output.json
|
|
||||||
|
|
||||||
# Unnecessary file
|
# macOS specific files
|
||||||
*.swp
|
.DS_Store
|
||||||
|
6
.idea/icon.svg
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" fill="none" viewBox="0 0 432 432">
|
||||||
|
<circle cx="216" cy="216" r="216" fill="#2e3943"/>
|
||||||
|
<path fill="#f2faff" d="M398.073 216c0 97.433-81.517 176.419-182.073 176.419-100.556 0-182.073-78.986-182.073-176.419S115.444 39.581 216 39.581c100.556 0 182.073 78.986 182.073 176.419Z"/>
|
||||||
|
<path fill="#7ebbed" fill-rule="evenodd" d="M216 359.34c81.702 0 147.934-64.175 147.934-143.34S297.702 72.66 216 72.66 68.065 136.835 68.065 216 134.298 359.34 216 359.34zm0 33.079c100.556 0 182.073-78.986 182.073-176.419S316.556 39.581 216 39.581C115.444 39.581 33.927 118.567 33.927 216S115.444 392.419 216 392.419z" clip-rule="evenodd"/>
|
||||||
|
<path fill="#031019" d="m155.273 168.033-1.227-28.215c3.68.7 8.063.875 18.052.875 12.092 0 28.041-.7 36.279-1.752 3.504-.35 4.907-.876 7.185-2.103l18.928 16.124c-1.753 2.453-2.279 3.505-4.207 8.412-1.576 3.856-8.762 26.113-11.567 35.577 12.97 2.63 20.155 4.557 29.97 8.588 1.226-8.588 1.401-13.144 1.401-28.742 0-4.03-.175-6.31-.7-9.99l30.495 1.051c-.877 4.207-1.052 5.959-1.227 12.794-.701 16.475-1.403 24.361-3.154 36.279 12.092 6.134 12.092 6.134 18.226 9.464 3.154 1.752 3.855 2.102 5.959 2.804l-10.165 32.773c-4.908-4.381-11.743-9.113-21.732-14.721-8.763 20.855-23.31 36.103-45.392 48.195-7.36-9.814-12.97-15.772-21.907-22.783 12.969-6.134 18.928-9.99 25.763-16.475 6.66-6.484 11.04-12.793 15.247-22.258-11.217-5.082-18.403-7.36-30.846-9.989-7.185 21.382-12.969 35.052-18.051 43.29-6.835 11.04-16.124 16.824-26.815 16.824-8.237 0-16.65-3.68-22.784-9.99-7.01-7.185-10.69-17.175-10.69-28.742 0-17.176 8.238-32.072 22.609-41.361 9.288-5.959 19.103-8.588 34.7-9.465 3.155-10.34 5.785-19.278 8.238-29.267-7.712.701-17.35 1.227-29.093 1.752-6.309.175-8.412.35-13.495 1.051zm26.64 53.279c-8.238 1.402-13.145 4.031-17.527 9.64-3.33 3.855-4.907 8.412-4.907 13.32 0 5.432 2.63 9.464 5.959 9.464 4.03 0 8.588-9.114 16.475-32.424z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
335
CHANGELOG.md
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# 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.18.0] - 2025-03-20
|
||||||
|
### Added
|
||||||
|
- Add option to always decode long strip images with SSIV ([@AntsyLich](https://github.com/AntsyLich)) ([`c5655e8`](https://github.com/mihonapp/mihon/commit/c5655e8803bc32d0931657f0b7bc6afeab70feaf))
|
||||||
|
- Change option label ([@AntsyLich](https://github.com/AntsyLich)) ([#1835](https://github.com/mihonapp/mihon/pull/1835))
|
||||||
|
- Added option to enable incognito per extension ([@sdaqo](https://github.com/sdaqo), [@AntsyLich](https://github.com/AntsyLich)) ([#157](https://github.com/mihonapp/mihon/pull/157))
|
||||||
|
- Add button to favorite manga from history screen ([@Animeboynz](https://github.com/Animeboynz)) ([#1733](https://github.com/mihonapp/mihon/pull/1733))
|
||||||
|
- Add Monochrome theme (made with e-ink displays in mind) ([@MajorTanya](https://github.com/MajorTanya)) ([#1752](https://github.com/mihonapp/mihon/pull/1752))
|
||||||
|
- Support for private tracking with AniList and Bangumi ([@NarwhalHorns](https://github.com/NarwhalHorns)) ([#1736](https://github.com/mihonapp/mihon/pull/1736))
|
||||||
|
- Add private tracking support for Kitsu ([@MajorTanya](https://github.com/MajorTanya)) ([#1774](https://github.com/mihonapp/mihon/pull/1774))
|
||||||
|
- Add option to export minimal library information to a CSV file ([@Animeboynz](https://github.com/Animeboynz), [@AntsyLich](https://github.com/AntsyLich)) ([#1161](https://github.com/mihonapp/mihon/pull/1161))
|
||||||
|
- Add back support for drag-and-drop category reordering ([@cuong-tran](https://github.com/cuong-tran)) ([#1427](https://github.com/mihonapp/mihon/pull/1427))
|
||||||
|
- Add option to mark duplicate read chapters as read after library update or while reading ([@AntsyLich](https://github.com/AntsyLich)) ([#1785](https://github.com/mihonapp/mihon/pull/1785), [#1791](https://github.com/mihonapp/mihon/pull/1791), [#1870](https://github.com/mihonapp/mihon/pull/1870))
|
||||||
|
- Display staff information on Anilist tracker search results ([@NarwhalHorns](https://github.com/NarwhalHorns)) ([#1810](https://github.com/mihonapp/mihon/pull/1810))
|
||||||
|
- Add `id:` prefix search to library to search by internal DB ID ([@MajorTanya](https://github.com/MajorTanya)) ([#1856](https://github.com/mihonapp/mihon/pull/1856))
|
||||||
|
- Add back option to disable unread chapter badge in library ([@AntsyLich](https://github.com/AntsyLich)) ([#1871](https://github.com/mihonapp/mihon/pull/1871))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Sliders UI ([@AntsyLich](https://github.com/AntsyLich)) ([#1840](https://github.com/mihonapp/mihon/pull/1840))
|
||||||
|
- Apply "Downloaded only" filter to all entries regardless of favourite status ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#1603](https://github.com/mihonapp/mihon/pull/1603))
|
||||||
|
- Ignore hidden files/folders for Local Source chapter list ([@BrutuZ](https://github.com/BrutuZ)) ([#1763](https://github.com/mihonapp/mihon/pull/1763))
|
||||||
|
- Migrate to newer Bangumi API ([@MajorTanya](https://github.com/MajorTanya)) ([#1748](https://github.com/mihonapp/mihon/pull/1748))
|
||||||
|
- Now showing manga starting dates in search
|
||||||
|
- Reduced request load by 2-4x in certain situations
|
||||||
|
- Bump default user agent ([@AntsyLich](https://github.com/AntsyLich)) ([#1833](https://github.com/mihonapp/mihon/pull/1833))
|
||||||
|
- Changed the label of chapter swipe settings and renamed the group to "Behavior" ([@AntsyLich](https://github.com/AntsyLich)) ([#1870](https://github.com/mihonapp/mihon/pull/1870))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix MAL `main_picture` nullability breaking search if a result doesn't have a cover set ([@MajorTanya](https://github.com/MajorTanya)) ([#1618](https://github.com/mihonapp/mihon/pull/1618))
|
||||||
|
- Fix Bangumi and MAL tracking 401 errors due to Mihon sending expired credentials ([@MajorTanya](https://github.com/MajorTanya)) ([#1681](https://github.com/mihonapp/mihon/pull/1681), [#1682](https://github.com/mihonapp/mihon/pull/1682))
|
||||||
|
- Fix certain Infinix, Xiaomi devices being unable to use any "Open link in browser" actions, including tracker setup ([@MajorTanya](https://github.com/MajorTanya)) ([#1684](https://github.com/mihonapp/mihon/pull/1684)) ([#1776](https://github.com/mihonapp/mihon/pull/1776))
|
||||||
|
- Fix App's preferences referencing deleted categories ([@cuong-tran](https://github.com/cuong-tran)) ([#1734](https://github.com/mihonapp/mihon/pull/1734))
|
||||||
|
- Fix backup/restore of category related preferences ([@cuong-tran](https://github.com/cuong-tran)) ([#1726](https://github.com/mihonapp/mihon/pull/1726))
|
||||||
|
- Fix WebView sending app's package name in `X-Requested-With` header, which led to sources blocking access ([@AwkwardPeak7](https://github.com/AwkwardPeak7)) ([#1812](https://github.com/mihonapp/mihon/pull/1812))
|
||||||
|
- Fix an issue where tracker reading progress is changed to a lower value ([@Animeboynz](https://github.com/Animeboynz)) ([#1795](https://github.com/mihonapp/mihon/pull/1795))
|
||||||
|
- Attempt to fix crash when migrating or removing entries from library ([@FlaminSarge](https://github.com/FlaminSarge)) ([#1828](https://github.com/mihonapp/mihon/pull/1828))
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Remove alphabetical category sort option ([@AntsyLich](https://github.com/AntsyLich)) ([#1781](https://github.com/mihonapp/mihon/pull/1781))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- Add zoned "Current time" to debug info and include year & timezone in logcat output ([@MajorTanya](https://github.com/MajorTanya)) ([#1672](https://github.com/mihonapp/mihon/pull/1672))
|
||||||
|
- Add application package ID to debug info ([@MajorTanya](https://github.com/MajorTanya)) ([#1847](https://github.com/mihonapp/mihon/pull/1847))
|
||||||
|
|
||||||
|
## [v0.17.1] - 2024-12-06
|
||||||
|
### Changed
|
||||||
|
- Bump default user agent ([@AntsyLich](https://github.com/AntsyLich)) ([`76dcf90`](https://github.com/mihonapp/mihon/commit/76dcf903403d565056f44c66d965c1ea8affffc3))
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- Bangumi search now shows the score and summary of a search result ([@MajorTanya](https://github.com/MajorTanya)) ([#1396](https://github.com/mihonapp/mihon/pull/1396))
|
||||||
|
- Extension repo URLs are now auto-formatted ([@AntsyLich](https://github.com/AntsyLich), [@MajorTanya](https://github.com/MajorTanya)) ([`22d8aad`](https://github.com/mihonapp/mihon/commit/22d8aad598bea8f00f2831779e45a6645392ca0f))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fix "currentTab was used multiple times" ([@AntsyLich](https://github.com/AntsyLich)) ([`371c143`](https://github.com/mihonapp/mihon/commit/371c1432e218f6dcf129f05405dceb2cd351c647))
|
||||||
|
- Fix a rare crash when invoking "Mark previous as read" action ([@AntsyLich](https://github.com/AntsyLich)) ([`f508d10`](https://github.com/mihonapp/mihon/commit/f508d10ad13560d7316df8642bc93fe66c05b9a8))
|
||||||
|
- Fix long strip images not loading in some old devices ([@AntsyLich](https://github.com/AntsyLich)) ([`06efc3b`](https://github.com/mihonapp/mihon/commit/06efc3b25c5af51f42448af27a269ee459d9093d))
|
||||||
|
- Switch to hardware bitmap in reader only if device can handle it ([@AntsyLich](https://github.com/AntsyLich)) ([`e6d96bd`](https://github.com/mihonapp/mihon/commit/e6d96bd348ea5d18a005d6465222ad5f5123103e))
|
||||||
|
- Add option to lower the threshold for hardware bitmaps ([@AntsyLich](https://github.com/AntsyLich)) ([`dcddac5`](https://github.com/mihonapp/mihon/commit/dcddac5daaff3ec89c8507c35dc13d345ffdb6d7))
|
||||||
|
- Improve hardware bitmap threshold option ([@AntsyLich](https://github.com/AntsyLich)) ([`d6dfd24`](https://github.com/mihonapp/mihon/commit/d6dfd24548eaa05a8c3e478068fe2e08f2ee4473))
|
||||||
|
- Always use software bitmap on certain devices ([@MajorTanya](https://github.com/MajorTanya)) ([#1543](https://github.com/mihonapp/mihon/pull/1543))
|
||||||
|
- Fix crash after removing last category while it's active in library ([@cuong-tran](https://github.com/cuong-tran)) ([#1450](https://github.com/mihonapp/mihon/pull/1450))
|
||||||
|
- Fix reader transition color scheme in auto background mode ([@cuong-tran](https://github.com/cuong-tran)) ([#1487](https://github.com/mihonapp/mihon/pull/1487))
|
||||||
|
- Fix app update error notification disappearing ([@cuong-tran](https://github.com/cuong-tran)) ([#1476](https://github.com/mihonapp/mihon/pull/1476))
|
||||||
|
- Fix browser not opening in some cases in Honor devices ([@AntsyLich](https://github.com/AntsyLich), [@MajorTanya](https://github.com/MajorTanya)) ([#1520](https://github.com/mihonapp/mihon/pull/1520))
|
||||||
|
|
||||||
|
## [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) ([@AntsyLich](https://github.com/AntsyLich)) ([`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.18.0...main
|
||||||
|
[v0.18.0]: https://github.com/mihonapp/mihon/compare/v0.17.1...v0.18.0
|
||||||
|
[v0.17.1]: https://github.com/mihonapp/mihon/compare/v0.17.0...v0.17.1
|
||||||
|
[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
|
@ -60,7 +60,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community moderators responsible for enforcement at
|
reported to the community moderators responsible for enforcement at
|
||||||
the [Tachiyomi Discord server](https://discord.gg/tachiyomi).
|
the [Mihon Discord server](https://discord.gg/mihon).
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
All community moderators are obligated to respect the privacy and security of the
|
All community moderators are obligated to respect the privacy and security of the
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
Looking to report an issue/bug or make a feature request? Please refer to the [README file](https://github.com/tachiyomiorg/tachiyomi#issues-feature-requests-and-contributing).
|
Looking to report an issue/bug or make a feature request? Please refer to the [README file](https://github.com/mihonapp/mihon#issues-feature-requests-and-contributing).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Thanks for your interest in contributing to Tachiyomi!
|
Thanks for your interest in contributing to Mihon!
|
||||||
|
|
||||||
|
|
||||||
# Code contributions
|
# Code contributions
|
||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
If you're interested in taking on [an open issue](https://github.com/tachiyomiorg/tachiyomi/issues), please comment on it so others are aware.
|
If you're interested in taking on [an open issue](https://github.com/mihonapp/mihon/issues), please comment on it so others are aware.
|
||||||
You do not need to ask for permission nor an assignment.
|
You do not need to ask for permission nor an assignment.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
@ -26,25 +26,24 @@ Before you start, please note that the ability to use following technologies is
|
|||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
|
|
||||||
- Join [the Discord server](https://discord.gg/tachiyomi) 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.
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
|
|
||||||
Translations are done externally via Weblate. See [our website](https://tachiyomi.org/help/contribution/#translation) for more details.
|
Translations are done externally via Weblate. See [our website](https://mihon.app/docs/contribute#translation) for more details.
|
||||||
|
|
||||||
|
|
||||||
# Forks
|
# Forks
|
||||||
|
|
||||||
Forks are allowed so long as they abide by [the project's LICENSE](https://github.com/tachiyomiorg/tachiyomi/blob/master/LICENSE).
|
Forks are allowed so long as they abide by [the project's LICENSE](https://github.com/mihonapp/mihon/blob/main/LICENSE).
|
||||||
|
|
||||||
When creating a fork, remember to:
|
When creating a fork, remember to:
|
||||||
|
|
||||||
- To avoid confusion with the main app:
|
- To avoid confusion with the main app:
|
||||||
- Change the app name
|
- Change the app name
|
||||||
- Change the app icon
|
- Change the app icon
|
||||||
- Change or disable the [app update checker](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt)
|
- Change or disable the [app update checker](https://github.com/mihonapp/mihon/blob/main/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt)
|
||||||
- To avoid installation conflicts:
|
- To avoid installation conflicts:
|
||||||
- Change the `applicationId` in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts)
|
- Change the `applicationId` in [`build.gradle.kts`](https://github.com/mihonapp/mihon/blob/main/app/build.gradle.kts)
|
||||||
- To avoid having your data polluting the main app's analytics and crash report services:
|
- To avoid having your data polluting the main app's analytics and crash report services:
|
||||||
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/standard/google-services.json) with your own
|
- If you want to use Firebase analytics, replace [`google-services.json`](https://github.com/mihonapp/mihon/blob/main/app/src/standard/google-services.json) with your own
|
||||||
- If you want to use ACRA crash reporting, replace the `ACRA_URI` endpoint in [`build.gradle.kts`](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/build.gradle.kts) with your own
|
|
||||||
|
112
README.md
@ -1,81 +1,74 @@
|
|||||||
| Build | Stable | Weekly Preview | Contribute | Support Server |
|
<div align="center">
|
||||||
|-------|----------|---------|------------|---------|
|
|
||||||
|  | [](https://github.com/tachiyomiorg/tachiyomi/releases) | [](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [](https://discord.gg/tachiyomi) |
|
|
||||||
|
|
||||||
|
<a href="https://mihon.app">
|
||||||
|
<img src="./.github/assets/logo.png" alt="Mihon logo" title="Mihon logo" width="80"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
# Tachiyomi
|
# Mihon [App](#)
|
||||||
Tachiyomi is a free and open source manga reader for Android 6.0 and above.
|
|
||||||
|
### 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/tachiyomiorg/tachiyomi/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/tachiyomiorg/tachiyomi-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://tachiyomi.org/help/faq/), the [changelog](https://github.com/tachiyomiorg/tachiyomi/releases) and the already opened [issues](https://github.com/tachiyomiorg/tachiyomi/issues).**
|
|
||||||
2. If you are unsure, ask here: [](https://discord.gg/tachiyomi)
|
|
||||||
|
|
||||||
</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 in 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
|
|
||||||
|
|
||||||
DO: https://github.com/tachiyomiorg/tachiyomi/issues/24 https://github.com/tachiyomiorg/tachiyomi/issues/71
|
Thank you to all the people who have contributed!
|
||||||
|
|
||||||
DON'T: https://github.com/tachiyomiorg/tachiyomi/issues/75
|
<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>
|
||||||
|
|
||||||
</details>
|
### Disclaimer
|
||||||
|
|
||||||
<details><summary>Feature Requests</summary>
|
The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content.
|
||||||
|
|
||||||
* Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does"
|
### License
|
||||||
* Include screenshot (if needed)
|
|
||||||
|
|
||||||
Source requests should be created at https://github.com/tachiyomiorg/tachiyomi-extensions, they do not belong in this repository.
|
<pre>
|
||||||
</details>
|
Copyright © 2015 Javier Tomás
|
||||||
|
Copyright © 2024 Mihon Open Source Project
|
||||||
<details><summary>Contributing</summary>
|
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>Code of Conduct</summary>
|
|
||||||
|
|
||||||
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md).
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
[See our website.](https://tachiyomi.org/)
|
|
||||||
You can also reach out to us on [Discord](https://discord.gg/tachiyomi).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright 2015 Javier Tomás
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -88,7 +81,6 @@ You can also reach out to us on [Discord](https://discord.gg/tachiyomi).
|
|||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
</pre>
|
||||||
|
|
||||||
## Disclaimer
|
</div>
|
||||||
|
|
||||||
The developer of this application does not have any affiliation with the content providers available.
|
|
||||||
|
4
app/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
/build
|
|
||||||
*iml
|
|
||||||
*.iml
|
|
||||||
custom.gradle
|
|
@ -1,84 +1,90 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import mihon.buildlogic.Config
|
||||||
import org.jmailen.gradle.kotlinter.tasks.LintTask
|
import mihon.buildlogic.getBuildTime
|
||||||
|
import mihon.buildlogic.getCommitCount
|
||||||
|
import mihon.buildlogic.getGitSha
|
||||||
|
|
||||||
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 (Config.includeTelemetry) {
|
||||||
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")
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "eu.kanade.tachiyomi"
|
namespace = "eu.kanade.tachiyomi"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi"
|
applicationId = "app.mihon"
|
||||||
versionCode = 101
|
|
||||||
versionName = "0.14.6"
|
versionCode = 11
|
||||||
|
versionName = "0.18.0"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime()}\"")
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
|
||||||
buildConfigField("boolean", "INCLUDE_UPDATER", "false")
|
buildConfigField("boolean", "TELEMETRY_INCLUDED", "${Config.includeTelemetry}")
|
||||||
buildConfigField("boolean", "PREVIEW", "false")
|
buildConfigField("boolean", "UPDATER_ENABLED", "${Config.enableUpdater}")
|
||||||
|
|
||||||
// Please disable ACRA or use your own instance in forked versions of the project
|
|
||||||
buildConfigField("String", "ACRA_URI", "\"https://tachiyomi.kanade.eu/crash_report\"")
|
|
||||||
|
|
||||||
ndk {
|
|
||||||
abiFilters += SUPPORTED_ABIS
|
|
||||||
}
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
splits {
|
|
||||||
abi {
|
|
||||||
isEnable = true
|
|
||||||
reset()
|
|
||||||
include(*SUPPORTED_ABIS.toTypedArray())
|
|
||||||
isUniversalApk = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
named("debug") {
|
val debug by getting {
|
||||||
|
applicationIdSuffix = ".dev"
|
||||||
versionNameSuffix = "-${getCommitCount()}"
|
versionNameSuffix = "-${getCommitCount()}"
|
||||||
applicationIdSuffix = ".debug"
|
|
||||||
isPseudoLocalesEnabled = true
|
isPseudoLocalesEnabled = true
|
||||||
}
|
}
|
||||||
named("release") {
|
val release by getting {
|
||||||
isShrinkResources = true
|
isMinifyEnabled = Config.enableCodeShrink
|
||||||
isMinifyEnabled = true
|
isShrinkResources = Config.enableCodeShrink
|
||||||
|
|
||||||
proguardFiles("proguard-android-optimize.txt", "proguard-rules.pro")
|
proguardFiles("proguard-android-optimize.txt", "proguard-rules.pro")
|
||||||
|
|
||||||
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = true)}\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
val commonMatchingFallbacks = listOf(release.name)
|
||||||
|
|
||||||
|
create("foss") {
|
||||||
|
initWith(release)
|
||||||
|
|
||||||
|
applicationIdSuffix = ".foss"
|
||||||
|
|
||||||
|
matchingFallbacks.addAll(commonMatchingFallbacks)
|
||||||
}
|
}
|
||||||
create("preview") {
|
create("preview") {
|
||||||
initWith(getByName("release"))
|
initWith(release)
|
||||||
buildConfigField("boolean", "PREVIEW", "true")
|
|
||||||
|
|
||||||
val debugType = getByName("debug")
|
applicationIdSuffix = ".debug"
|
||||||
signingConfig = debugType.signingConfig
|
|
||||||
versionNameSuffix = debugType.versionNameSuffix
|
versionNameSuffix = debug.versionNameSuffix
|
||||||
applicationIdSuffix = debugType.applicationIdSuffix
|
signingConfig = debug.signingConfig
|
||||||
matchingFallbacks.add("release")
|
|
||||||
|
matchingFallbacks.addAll(commonMatchingFallbacks)
|
||||||
|
|
||||||
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
|
||||||
}
|
}
|
||||||
create("benchmark") {
|
create("benchmark") {
|
||||||
initWith(getByName("release"))
|
initWith(release)
|
||||||
|
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
|
||||||
matchingFallbacks.add("release")
|
|
||||||
isDebuggable = false
|
isDebuggable = false
|
||||||
|
isProfileable = true
|
||||||
versionNameSuffix = "-benchmark"
|
versionNameSuffix = "-benchmark"
|
||||||
applicationIdSuffix = ".benchmark"
|
applicationIdSuffix = ".benchmark"
|
||||||
|
|
||||||
|
signingConfig = debug.signingConfig
|
||||||
|
|
||||||
|
matchingFallbacks.addAll(commonMatchingFallbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,39 +93,51 @@ android {
|
|||||||
getByName("benchmark").res.srcDirs("src/debug/res")
|
getByName("benchmark").res.srcDirs("src/debug/res")
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions.add("default")
|
splits {
|
||||||
|
abi {
|
||||||
productFlavors {
|
isEnable = true
|
||||||
create("standard") {
|
isUniversalApk = true
|
||||||
buildConfigField("boolean", "INCLUDE_UPDATER", "true")
|
reset()
|
||||||
dimension = "default"
|
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||||
}
|
|
||||||
create("dev") {
|
|
||||||
// Include pseudolocales: https://developer.android.com/guide/topics/resources/pseudolocales
|
|
||||||
resourceConfigurations.addAll(listOf("en", "en_XA", "ar_XB", "xxhdpi"))
|
|
||||||
dimension = "default"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packaging {
|
||||||
resources.excludes.addAll(listOf(
|
jniLibs {
|
||||||
"META-INF/DEPENDENCIES",
|
keepDebugSymbols += listOf(
|
||||||
|
"libandroidx.graphics.path",
|
||||||
|
"libarchive-jni",
|
||||||
|
"libconscrypt_jni",
|
||||||
|
"libimagedecoder",
|
||||||
|
"libquickjs",
|
||||||
|
"libsqlite3x",
|
||||||
|
)
|
||||||
|
.map { "**/$it.so" }
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
excludes += setOf(
|
||||||
|
"kotlin-tooling-metadata.json",
|
||||||
"LICENSE.txt",
|
"LICENSE.txt",
|
||||||
|
"META-INF/**/*.properties",
|
||||||
|
"META-INF/**/LICENSE.txt",
|
||||||
|
"META-INF/*.properties",
|
||||||
|
"META-INF/*.version",
|
||||||
|
"META-INF/DEPENDENCIES",
|
||||||
"META-INF/LICENSE",
|
"META-INF/LICENSE",
|
||||||
"META-INF/LICENSE.txt",
|
|
||||||
"META-INF/README.md",
|
|
||||||
"META-INF/NOTICE",
|
"META-INF/NOTICE",
|
||||||
"META-INF/*.kotlin_module",
|
"META-INF/README.md",
|
||||||
))
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependenciesInfo {
|
dependenciesInfo {
|
||||||
includeInApk = false
|
includeInApk = Config.includeDependencyInfo
|
||||||
|
includeInBundle = Config.includeDependencyInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
compose = true
|
buildConfig = true
|
||||||
|
|
||||||
// Disable some unused things
|
// Disable some unused things
|
||||||
aidl = false
|
aidl = false
|
||||||
@ -131,37 +149,51 @@ android {
|
|||||||
abortOnError = false
|
abortOnError = false
|
||||||
checkReleaseBuilds = false
|
checkReleaseBuilds = false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
composeOptions {
|
kotlin {
|
||||||
kotlinCompilerExtensionVersion = compose.versions.compiler.get()
|
compilerOptions {
|
||||||
|
freeCompilerArgs.addAll(
|
||||||
|
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||||
|
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
||||||
|
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||||
|
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||||
|
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
||||||
|
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
|
||||||
|
"-opt-in=coil3.annotation.ExperimentalCoilApi",
|
||||||
|
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||||
|
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||||
|
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||||
|
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":i18n"))
|
implementation(projects.i18n)
|
||||||
implementation(project(":core"))
|
implementation(projects.core.archive)
|
||||||
implementation(project(":source-api"))
|
implementation(projects.core.common)
|
||||||
implementation(project(":data"))
|
implementation(projects.coreMetadata)
|
||||||
implementation(project(":domain"))
|
implementation(projects.sourceApi)
|
||||||
implementation(project(":presentation-core"))
|
implementation(projects.sourceLocal)
|
||||||
implementation(project(":presentation-widget"))
|
implementation(projects.data)
|
||||||
|
implementation(projects.domain)
|
||||||
|
implementation(projects.presentationCore)
|
||||||
|
implementation(projects.presentationWidget)
|
||||||
|
implementation(projects.telemetry)
|
||||||
|
|
||||||
// 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)
|
||||||
implementation(compose.ui.tooling)
|
debugImplementation(compose.ui.tooling)
|
||||||
|
implementation(compose.ui.tooling.preview)
|
||||||
implementation(compose.ui.util)
|
implementation(compose.ui.util)
|
||||||
implementation(compose.accompanist.webview)
|
|
||||||
implementation(compose.accompanist.flowlayout)
|
implementation(androidx.interpolator)
|
||||||
implementation(compose.accompanist.permissions)
|
|
||||||
implementation(compose.accompanist.themeadapter)
|
|
||||||
implementation(compose.accompanist.systemuicontroller)
|
|
||||||
|
|
||||||
implementation(androidx.paging.runtime)
|
implementation(androidx.paging.runtime)
|
||||||
implementation(androidx.paging.compose)
|
implementation(androidx.paging.compose)
|
||||||
@ -169,6 +201,7 @@ dependencies {
|
|||||||
implementation(libs.bundles.sqlite)
|
implementation(libs.bundles.sqlite)
|
||||||
|
|
||||||
implementation(kotlinx.reflect)
|
implementation(kotlinx.reflect)
|
||||||
|
implementation(kotlinx.immutables)
|
||||||
|
|
||||||
implementation(platform(kotlinx.coroutines.bom))
|
implementation(platform(kotlinx.coroutines.bom))
|
||||||
implementation(kotlinx.bundles.coroutines)
|
implementation(kotlinx.bundles.coroutines)
|
||||||
@ -178,7 +211,6 @@ dependencies {
|
|||||||
implementation(androidx.appcompat)
|
implementation(androidx.appcompat)
|
||||||
implementation(androidx.biometricktx)
|
implementation(androidx.biometricktx)
|
||||||
implementation(androidx.constraintlayout)
|
implementation(androidx.constraintlayout)
|
||||||
implementation(androidx.coordinatorlayout)
|
|
||||||
implementation(androidx.corektx)
|
implementation(androidx.corektx)
|
||||||
implementation(androidx.splashscreen)
|
implementation(androidx.splashscreen)
|
||||||
implementation(androidx.recyclerview)
|
implementation(androidx.recyclerview)
|
||||||
@ -188,20 +220,17 @@ dependencies {
|
|||||||
implementation(androidx.bundles.lifecycle)
|
implementation(androidx.bundles.lifecycle)
|
||||||
|
|
||||||
// Job scheduling
|
// Job scheduling
|
||||||
implementation(androidx.bundles.workmanager)
|
implementation(androidx.workmanager)
|
||||||
|
|
||||||
// RX
|
// RxJava
|
||||||
implementation(libs.bundles.reactivex)
|
implementation(libs.rxjava)
|
||||||
implementation(libs.flowreactivenetwork)
|
|
||||||
|
|
||||||
// Network client
|
// Networking
|
||||||
implementation(libs.bundles.okhttp)
|
implementation(libs.bundles.okhttp)
|
||||||
implementation(libs.okio)
|
implementation(libs.okio)
|
||||||
|
implementation(libs.conscrypt.android) // TLS 1.3 support for Android < 10
|
||||||
|
|
||||||
// TLS 1.3 support for Android < 10
|
// Data serialization (JSON, protobuf, xml)
|
||||||
implementation(libs.conscrypt.android)
|
|
||||||
|
|
||||||
// Data serialization (JSON, protobuf)
|
|
||||||
implementation(kotlinx.bundles.serialization)
|
implementation(kotlinx.bundles.serialization)
|
||||||
|
|
||||||
// HTML parser
|
// HTML parser
|
||||||
@ -210,28 +239,24 @@ 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(libs.bundles.coil)
|
implementation(libs.bundles.coil)
|
||||||
implementation(libs.subsamplingscaleimageview) {
|
implementation(libs.subsamplingscaleimageview) {
|
||||||
exclude(module = "image-decoder")
|
exclude(module = "image-decoder")
|
||||||
}
|
}
|
||||||
implementation(libs.image.decoder)
|
implementation(libs.image.decoder)
|
||||||
|
|
||||||
// Sort
|
|
||||||
implementation(libs.natural.comparator)
|
|
||||||
|
|
||||||
// UI libraries
|
// UI libraries
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
implementation(libs.flexible.adapter.core)
|
implementation(libs.flexible.adapter.core)
|
||||||
implementation(libs.flexible.adapter.ui)
|
|
||||||
implementation(libs.photoview)
|
implementation(libs.photoview)
|
||||||
implementation(libs.directionalviewpager) {
|
implementation(libs.directionalviewpager) {
|
||||||
exclude(group = "androidx.viewpager", module = "viewpager")
|
exclude(group = "androidx.viewpager", module = "viewpager")
|
||||||
@ -239,34 +264,36 @@ dependencies {
|
|||||||
implementation(libs.insetter)
|
implementation(libs.insetter)
|
||||||
implementation(libs.bundles.richtext)
|
implementation(libs.bundles.richtext)
|
||||||
implementation(libs.aboutLibraries.compose)
|
implementation(libs.aboutLibraries.compose)
|
||||||
implementation(libs.cascade)
|
|
||||||
implementation(libs.bundles.voyager)
|
implementation(libs.bundles.voyager)
|
||||||
implementation(libs.wheelpicker)
|
implementation(libs.compose.materialmotion)
|
||||||
implementation(libs.materialmotion.core)
|
implementation(libs.swipe)
|
||||||
|
implementation(libs.compose.webview)
|
||||||
|
implementation(libs.compose.grid)
|
||||||
|
implementation(libs.reorderable)
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
|
|
||||||
// Crash reports/analytics
|
|
||||||
implementation(libs.acra.http)
|
|
||||||
"standardImplementation"(libs.firebase.analytics)
|
|
||||||
|
|
||||||
// Shizuku
|
// Shizuku
|
||||||
implementation(libs.bundles.shizuku)
|
implementation(libs.bundles.shizuku)
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.bundles.test)
|
||||||
|
|
||||||
// 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 {
|
||||||
beforeVariants { variantBuilder ->
|
beforeVariants { variantBuilder ->
|
||||||
// Disables standardBenchmark
|
// Disables standardBenchmark
|
||||||
if (variantBuilder.buildType == "benchmark") {
|
if (variantBuilder.buildType == "benchmark") {
|
||||||
variantBuilder.enable = variantBuilder.productFlavors.containsAll(listOf("default" to "dev"))
|
variantBuilder.enable = variantBuilder.productFlavors.containsAll(
|
||||||
|
listOf("default" to "dev"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onVariants(selector().withFlavor("default" to "standard")) {
|
onVariants(selector().withFlavor("default" to "standard")) {
|
||||||
@ -276,46 +303,6 @@ androidComponents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
|
||||||
|
|
||||||
withType<LintTask>().configureEach {
|
|
||||||
exclude { it.file.path.contains("generated[\\\\/]".toRegex()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
|
||||||
withType<KotlinCompile> {
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
|
||||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
|
||||||
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
|
|
||||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
|
||||||
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
|
|
||||||
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
|
||||||
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
|
|
||||||
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
|
|
||||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
|
||||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
|
||||||
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
|
|
||||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
|
||||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
|
||||||
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
|
||||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
|
||||||
)
|
|
||||||
|
|
||||||
if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") {
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
|
||||||
"-P",
|
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
|
||||||
project.buildDir.absolutePath + "/compose_metrics"
|
|
||||||
)
|
|
||||||
kotlinOptions.freeCompilerArgs += listOf(
|
|
||||||
"-P",
|
|
||||||
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
|
||||||
project.buildDir.absolutePath + "/compose_metrics"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath(kotlinx.gradle)
|
classpath(kotlinx.gradle)
|
||||||
|
48
app/google-services.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "82031285239",
|
||||||
|
"project_id": "mihonapp",
|
||||||
|
"storage_bucket": "mihonapp.appspot.com"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:82031285239:android:336ed6dceef55c357594f2",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "app.mihon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyDTvOxBQnuXADx5isKxoynPG0nlAO8bQbk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:82031285239:android:b7440cbdd0d33c9d7594f2",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "app.mihon.debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyDTvOxBQnuXADx5isKxoynPG0nlAO8bQbk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
-dontusemixedcaseclassnames
|
-dontusemixedcaseclassnames
|
||||||
|
-ignorewarnings
|
||||||
-verbose
|
-verbose
|
||||||
|
|
||||||
-keepattributes *Annotation*
|
-keepattributes *Annotation*
|
||||||
|
17
app/proguard-rules.pro
vendored
@ -1,14 +1,19 @@
|
|||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
|
||||||
|
-keep,allowoptimization class eu.kanade.**
|
||||||
|
-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 *; }
|
||||||
-keep,allowoptimization class kotlin.** { public protected *; }
|
-keep,allowoptimization class kotlin.** { public protected *; }
|
||||||
-keep,allowoptimization class kotlinx.coroutines.** { public protected *; }
|
-keep,allowoptimization class kotlinx.coroutines.** { public protected *; }
|
||||||
-keep,allowoptimization class kotlinx.serialization.** { public protected *; }
|
-keep,allowoptimization class kotlinx.serialization.** { public protected *; }
|
||||||
|
-keep,allowoptimization class kotlin.time.** { public protected *; }
|
||||||
-keep,allowoptimization class okhttp3.** { public protected *; }
|
-keep,allowoptimization class okhttp3.** { public protected *; }
|
||||||
-keep,allowoptimization class okio.** { public protected *; }
|
-keep,allowoptimization class okio.** { public protected *; }
|
||||||
-keep,allowoptimization class rx.** { public protected *; }
|
|
||||||
-keep,allowoptimization class org.jsoup.** { public protected *; }
|
-keep,allowoptimization class org.jsoup.** { public protected *; }
|
||||||
|
-keep,allowoptimization class rx.** { public protected *; }
|
||||||
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
|
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
|
||||||
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
|
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
|
||||||
|
|
||||||
@ -39,9 +44,13 @@
|
|||||||
-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.AnnotationsKt # core serialization annotations
|
-dontnote kotlinx.serialization.** # core serialization annotations
|
||||||
|
|
||||||
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
||||||
-keepclassmembers class kotlinx.serialization.json.** {
|
-keepclassmembers class kotlinx.serialization.json.** {
|
||||||
@ -67,3 +76,7 @@
|
|||||||
|
|
||||||
# XmlUtil
|
# XmlUtil
|
||||||
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
|
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
|
||||||
|
|
||||||
|
# Firebase
|
||||||
|
-keep class com.google.firebase.installations.** { *; }
|
||||||
|
-keep interface com.google.firebase.installations.** { *; }
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<shortcut
|
<shortcut
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:icon="@drawable/sc_collections_bookmark_48dp"
|
android:icon="@drawable/sc_collections_bookmark_48dp"
|
||||||
|
23
app/src/debug/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="432"
|
||||||
|
android:viewportHeight="432">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h432v432h-432z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0h432v432h-432z"
|
||||||
|
android:fillColor="#FAFAFA"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M0,0h432v432h-432z"
|
||||||
|
android:fillColor="#2E3943"/>
|
||||||
|
<path
|
||||||
|
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"/>
|
||||||
|
<path
|
||||||
|
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:fillType="evenOdd"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
@ -1,27 +1,9 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="108dp"
|
android:width="108dp"
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
android:viewportWidth="108.0"
|
android:viewportWidth="432"
|
||||||
android:viewportHeight="108.0">
|
android:viewportHeight="432">
|
||||||
<path
|
<path
|
||||||
android:pathData="M14.5,7L86.5,7A7,7 0,0 1,93.5 14L93.5,95A7,7 0,0 1,86.5 102L14.5,102A7,7 0,0 1,7.5 95L7.5,14A7,7 0,0 1,14.5 7z"
|
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:fillColor="#000"/>
|
android:fillColor="#031019"/>
|
||||||
<path
|
|
||||||
android:pathData="M14.5,7L86.5,7A7,7 0,0 1,93.5 14L93.5,95A7,7 0,0 1,86.5 102L14.5,102A7,7 0,0 1,7.5 95L7.5,14A7,7 0,0 1,14.5 7z"
|
|
||||||
android:fillColor="#455A64"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M7.5,12.01C7.5,9.24 9.74,7 12.5,7L17.5,7L17.5,102L12.5,102C9.74,102 7.5,99.77 7.5,96.99L7.5,12.01Z"
|
|
||||||
android:fillColor="#607D8B"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M54,54.5m-25.5,0a25.5,25.5 0,1 1,51 0a25.5,25.5 0,1 1,-51 0"
|
|
||||||
android:fillColor="#000"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M54,54.5m-25.5,0a25.5,25.5 0,1 1,51 0a25.5,25.5 0,1 1,-51 0"
|
|
||||||
android:fillColor="#CE2828"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M54,54.5m-19.94,0a19.94,19.94 0,1 1,39.87 0a19.94,19.94 0,1 1,-39.87 0"
|
|
||||||
android:fillColor="#FFF"/>
|
|
||||||
<path
|
|
||||||
android:pathData="M52.04,46.3L47.42,46.3C46.14,46.3 44.93,46.23 44.2,46.14L44.2,49.76C45,49.65 46.16,49.6 47.42,49.6L60.58,49.6C61.86,49.6 63.02,49.65 63.82,49.76L63.82,46.14C63.09,46.23 61.86,46.3 60.58,46.3L55.69,46.3L55.69,45.07C55.69,44.43 55.73,43.95 55.82,43.45L51.9,43.45C51.99,44 52.04,44.43 52.04,45.07L52.04,46.3ZM46.78,60.68C45.46,60.68 44.29,60.63 43.45,60.52L43.45,64.14C44.34,64.03 45.46,63.98 46.78,63.98L61.29,63.98C62.57,63.98 63.71,64.03 64.57,64.14L64.57,60.52C63.73,60.63 62.57,60.68 61.29,60.68L58.24,60.68C59.33,58.06 59.99,56.23 60.7,53.91C61.34,51.81 61.34,51.81 61.56,51.13L57.58,50.06C57.51,50.93 57.37,51.52 56.89,53.41C56.19,56.14 55.32,58.74 54.5,60.68L46.78,60.68ZM46.48,51.36C47.55,54.02 48.28,56.53 49.03,60.15L52.66,58.9C51.65,54.98 50.92,52.66 49.94,50.11L46.48,51.36Z"
|
|
||||||
android:fillColor="#000"/>
|
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@android:color/transparent"/>
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
<monochrome android:drawable="@drawable/ic_tachi_monochrome_launcher" />
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 13 KiB |
@ -8,7 +8,9 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
|
||||||
<!-- Storage -->
|
<!-- Storage -->
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
<!-- For background jobs -->
|
<!-- For background jobs -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
@ -20,43 +22,82 @@
|
|||||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||||
<!-- To view extension packages in API 30+ -->
|
<!-- To view extension packages in API 30+ -->
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission
|
||||||
|
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_APP_SPECIFIC_LOCALES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
<!-- Remove permission from Firebase dependency -->
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
<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"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:largeHeap="true"
|
android:largeHeap="true"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:preserveLegacyExternalStorage="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:theme="@style/Theme.Tachiyomi"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config">
|
android:theme="@style/Theme.Tachiyomi">
|
||||||
|
|
||||||
<!-- enable profiling by macrobenchmark -->
|
|
||||||
<profileable
|
|
||||||
android:shell="true"
|
|
||||||
tools:targetApi="q" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.main.MainActivity"
|
android:name=".ui.main.MainActivity"
|
||||||
|
android:exported="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:theme="@style/Theme.Tachiyomi.SplashScreen"
|
android:theme="@style/Theme.Tachiyomi.SplashScreen">
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Deep link to add repos -->
|
||||||
|
<intent-filter android:label="@string/action_add_repo">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="tachiyomi" />
|
||||||
|
<data android:host="add-repo" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<!-- Open backup files -->
|
||||||
|
<intent-filter android:label="@string/pref_restore_backup">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="file" />
|
||||||
|
<data android:scheme="content" />
|
||||||
|
<data android:host="*" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
<!--
|
||||||
|
Work around Android's ugly primitive PatternMatcher
|
||||||
|
implementation that can't cope with finding a . early in
|
||||||
|
the path unless it's explicitly matched.
|
||||||
|
|
||||||
|
See https://stackoverflow.com/a/31028507
|
||||||
|
-->
|
||||||
|
<data android:pathPattern=".*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.tachibk" />
|
||||||
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.tachibk" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<!--suppress AndroidDomInspection -->
|
<!--suppress AndroidDomInspection -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.shortcuts"
|
android:name="android.app.shortcuts"
|
||||||
@ -64,16 +105,16 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:process=":error_handler"
|
|
||||||
android:name=".crash.CrashActivity"
|
android:name=".crash.CrashActivity"
|
||||||
android:exported="false" />
|
android:exported="false"
|
||||||
|
android:process=":error_handler" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.main.DeepLinkActivity"
|
android:name=".ui.deeplink.DeepLinkActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/action_search"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:theme="@android:style/Theme.NoDisplay"
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
android:label="@string/action_global_search"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
<action android:name="com.google.android.gms.actions.SEARCH_ACTION" />
|
<action android:name="com.google.android.gms.actions.SEARCH_ACTION" />
|
||||||
@ -97,20 +138,21 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.reader.ReaderActivity"
|
android:name=".ui.reader.ReaderActivity"
|
||||||
android:launchMode="singleTask"
|
android:exported="false"
|
||||||
android:exported="false">
|
android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.samsung.android.support.REMOTE_ACTION" />
|
<action android:name="com.samsung.android.support.REMOTE_ACTION" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="com.samsung.android.support.REMOTE_ACTION"
|
<meta-data
|
||||||
|
android:name="com.samsung.android.support.REMOTE_ACTION"
|
||||||
android:resource="@xml/s_pen_actions" />
|
android:resource="@xml/s_pen_actions" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.security.UnlockActivity"
|
android:name=".ui.security.UnlockActivity"
|
||||||
android:theme="@style/Theme.Tachiyomi"
|
android:exported="false"
|
||||||
android:exported="false" />
|
android:theme="@style/Theme.Tachiyomi" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.webview.WebViewActivity"
|
android:name=".ui.webview.WebViewActivity"
|
||||||
@ -119,67 +161,25 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".extension.util.ExtensionInstallActivity"
|
android:name=".extension.util.ExtensionInstallActivity"
|
||||||
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
android:exported="false"
|
||||||
android:exported="false" />
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.setting.track.AnilistLoginActivity"
|
android:name=".ui.setting.track.TrackLoginActivity"
|
||||||
android:label="Anilist"
|
android:exported="true"
|
||||||
android:exported="true">
|
android:label="@string/track_activity_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data
|
<data android:scheme="mihon" />
|
||||||
android:host="anilist-auth"
|
|
||||||
android:scheme="tachiyomi" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".ui.setting.track.MyAnimeListLoginActivity"
|
|
||||||
android:label="MyAnimeList"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<data android:host="anilist-auth" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<data android:host="bangumi-auth" />
|
||||||
|
<data android:host="myanimelist-auth" />
|
||||||
<data
|
<data android:host="shikimori-auth" />
|
||||||
android:host="myanimelist-auth"
|
|
||||||
android:scheme="tachiyomi" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".ui.setting.track.ShikimoriLoginActivity"
|
|
||||||
android:label="Shikimori"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data
|
|
||||||
android:host="shikimori-auth"
|
|
||||||
android:scheme="tachiyomi" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".ui.setting.track.BangumiLoginActivity"
|
|
||||||
android:label="Bangumi"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data
|
|
||||||
android:host="bangumi-auth"
|
|
||||||
android:scheme="tachiyomi" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
@ -187,38 +187,10 @@
|
|||||||
android:name=".data.notification.NotificationReceiver"
|
android:name=".data.notification.NotificationReceiver"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<receiver
|
<service
|
||||||
android:name="tachiyomi.presentation.widget.UpdatesGridGlanceReceiver"
|
android:name=".extension.util.ExtensionInstallService"
|
||||||
android:enabled="@bool/glance_appwidget_available"
|
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:label="@string/label_recent_updates">
|
android:foregroundServiceType="shortService" />
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.appwidget.provider"
|
|
||||||
android:resource="@xml/updates_grid_glance_widget_info" />
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".data.library.LibraryUpdateService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".data.download.DownloadService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".data.updater.AppUpdateService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".data.backup.BackupRestoreService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service android:name=".extension.util.ExtensionInstallService"
|
|
||||||
android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
@ -229,6 +201,11 @@
|
|||||||
android:value="true" />
|
android:value="true" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||||
|
android:foregroundServiceType="dataSync"
|
||||||
|
tools:node="merge" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
@ -242,9 +219,9 @@
|
|||||||
<provider
|
<provider
|
||||||
android:name="rikka.shizuku.ShizukuProvider"
|
android:name="rikka.shizuku.ShizukuProvider"
|
||||||
android:authorities="${applicationId}.shizuku"
|
android:authorities="${applicationId}.shizuku"
|
||||||
android:multiprocess="false"
|
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:multiprocess="false"
|
||||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
@ -254,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>
|
||||||
|
Before Width: | Height: | Size: 22 KiB |
10
app/src/main/java/eu/kanade/core/preference/CheckboxState.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package eu.kanade.core.preference
|
||||||
|
|
||||||
|
import androidx.compose.ui.state.ToggleableState
|
||||||
|
import tachiyomi.core.common.preference.CheckboxState
|
||||||
|
|
||||||
|
fun <T> CheckboxState.TriState<T>.asToggleableState() = when (this) {
|
||||||
|
is CheckboxState.TriState.Exclude -> ToggleableState.Indeterminate
|
||||||
|
is CheckboxState.TriState.Include -> ToggleableState.On
|
||||||
|
is CheckboxState.TriState.None -> ToggleableState.Off
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package eu.kanade.core.prefs
|
package eu.kanade.core.preference
|
||||||
|
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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>,
|
||||||
@ -31,7 +31,7 @@ class PreferenceMutableState<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun component2(): (T) -> Unit {
|
override fun component2(): (T) -> Unit {
|
||||||
return { preference.set(it) }
|
return preference::set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,32 +1,41 @@
|
|||||||
package eu.kanade.core.util
|
package eu.kanade.core.util
|
||||||
|
|
||||||
|
import androidx.compose.ui.util.fastFilter
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
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>()
|
||||||
for (i in -1..lastIndex) {
|
for (i in -1..lastIndex) {
|
||||||
val before = getOrNull(i)
|
val before = getOrNull(i)
|
||||||
before?.let { newList.add(it) }
|
before?.let(newList::add)
|
||||||
val after = getOrNull(i + 1)
|
val after = getOrNull(i + 1)
|
||||||
val separator = generator.invoke(before, after)
|
val separator = generator.invoke(before, after)
|
||||||
separator?.let { newList.add(it) }
|
separator?.let(newList::add)
|
||||||
}
|
}
|
||||||
return newList
|
return newList
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new map containing only the key entries of [transform] that are not null.
|
* Similar to [eu.kanade.core.util.insertSeparators] but iterates from last to first element
|
||||||
*/
|
*/
|
||||||
inline fun <K, V, R> Map<out K, V>.mapNotNullKeys(transform: (Map.Entry<K?, V>) -> R?): ConcurrentHashMap<R, V> {
|
fun <T : R, R : Any> List<T>.insertSeparatorsReversed(
|
||||||
val mutableMap = ConcurrentHashMap<R, V>()
|
generator: (before: T?, after: T?) -> R?,
|
||||||
forEach { element -> transform(element)?.let { mutableMap[it] = element.value } }
|
): List<R> {
|
||||||
return mutableMap
|
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) {
|
||||||
@ -37,21 +46,6 @@ fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list containing only elements matching the given [predicate].
|
|
||||||
*
|
|
||||||
* **Do not use for collections that come from public APIs**, since they may not support random
|
|
||||||
* access in an efficient way, and this method may actually be a lot slower. Only use for
|
|
||||||
* collections that are created by code we control and are known to support random access.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
inline fun <T> List<T>.fastFilter(predicate: (T) -> Boolean): List<T> {
|
|
||||||
contract { callsInPlace(predicate) }
|
|
||||||
val destination = ArrayList<T>()
|
|
||||||
fastForEach { if (predicate(it)) destination.add(it) }
|
|
||||||
return destination
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list containing all elements not matching the given [predicate].
|
* Returns a list containing all elements not matching the given [predicate].
|
||||||
*
|
*
|
||||||
@ -62,27 +56,7 @@ inline fun <T> List<T>.fastFilter(predicate: (T) -> Boolean): List<T> {
|
|||||||
@OptIn(ExperimentalContracts::class)
|
@OptIn(ExperimentalContracts::class)
|
||||||
inline fun <T> List<T>.fastFilterNot(predicate: (T) -> Boolean): List<T> {
|
inline fun <T> List<T>.fastFilterNot(predicate: (T) -> Boolean): List<T> {
|
||||||
contract { callsInPlace(predicate) }
|
contract { callsInPlace(predicate) }
|
||||||
val destination = ArrayList<T>()
|
return fastFilter { !predicate(it) }
|
||||||
fastForEach { if (!predicate(it)) destination.add(it) }
|
|
||||||
return destination
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list containing only the non-null results of applying the
|
|
||||||
* given [transform] function to each element in the original collection.
|
|
||||||
*
|
|
||||||
* **Do not use for collections that come from public APIs**, since they may not support random
|
|
||||||
* access in an efficient way, and this method may actually be a lot slower. Only use for
|
|
||||||
* collections that are created by code we control and are known to support random access.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
inline fun <T, R> List<T>.fastMapNotNull(transform: (T) -> R?): List<R> {
|
|
||||||
contract { callsInPlace(transform) }
|
|
||||||
val destination = ArrayList<R>()
|
|
||||||
fastForEach { element ->
|
|
||||||
transform(element)?.let { destination.add(it) }
|
|
||||||
}
|
|
||||||
return destination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,26 +97,3 @@ inline fun <T> List<T>.fastCountNot(predicate: (T) -> Boolean): Int {
|
|||||||
fastForEach { if (predicate(it)) --count }
|
fastForEach { if (predicate(it)) --count }
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list containing only elements from the given collection
|
|
||||||
* having distinct keys returned by the given [selector] function.
|
|
||||||
*
|
|
||||||
* Among elements of the given collection with equal keys, only the first one will be present in the resulting list.
|
|
||||||
* The elements in the resulting list are in the same order as they were in the source collection.
|
|
||||||
*
|
|
||||||
* **Do not use for collections that come from public APIs**, since they may not support random
|
|
||||||
* access in an efficient way, and this method may actually be a lot slower. Only use for
|
|
||||||
* collections that are created by code we control and are known to support random access.
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
inline fun <T, K> List<T>.fastDistinctBy(selector: (T) -> K): List<T> {
|
|
||||||
contract { callsInPlace(selector) }
|
|
||||||
val set = HashSet<K>()
|
|
||||||
val list = ArrayList<T>()
|
|
||||||
fastForEach {
|
|
||||||
val key = selector(it)
|
|
||||||
if (set.add(key)) list.add(it)
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package eu.kanade.core.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import eu.kanade.tachiyomi.R
|
|
||||||
import kotlin.time.Duration
|
|
||||||
|
|
||||||
fun Duration.toDurationString(context: Context, fallback: String): String {
|
|
||||||
return toComponents { days, hours, minutes, seconds, _ ->
|
|
||||||
buildList(4) {
|
|
||||||
if (days != 0L) add(context.getString(R.string.day_short, days))
|
|
||||||
if (hours != 0) add(context.getString(R.string.hour_short, hours))
|
|
||||||
if (minutes != 0 && (days == 0L || hours == 0)) add(context.getString(R.string.minute_short, minutes))
|
|
||||||
if (seconds != 0 && days == 0L && hours == 0) add(context.getString(R.string.seconds_short, seconds))
|
|
||||||
}.joinToString(" ").ifBlank { fallback }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package eu.kanade.core.util
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import rx.Emitter
|
|
||||||
import rx.Observable
|
|
||||||
import rx.Observer
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow {
|
|
||||||
val observer = object : Observer<T> {
|
|
||||||
override fun onNext(t: T) {
|
|
||||||
trySend(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(e: Throwable) {
|
|
||||||
close(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCompleted() {
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val subscription = subscribe(observer)
|
|
||||||
awaitClose { subscription.unsubscribe() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Any> Flow<T>.asObservable(
|
|
||||||
context: CoroutineContext = Dispatchers.Unconfined,
|
|
||||||
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE,
|
|
||||||
): Observable<T> {
|
|
||||||
return Observable.create(
|
|
||||||
{ emitter ->
|
|
||||||
/*
|
|
||||||
* ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if
|
|
||||||
* asObservable is already invoked from unconfined
|
|
||||||
*/
|
|
||||||
val job = GlobalScope.launch(context = context, start = CoroutineStart.ATOMIC) {
|
|
||||||
try {
|
|
||||||
collect { emitter.onNext(it) }
|
|
||||||
emitter.onCompleted()
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
// Ignore `CancellationException` as error, since it indicates "normal cancellation"
|
|
||||||
if (e !is CancellationException) {
|
|
||||||
emitter.onError(e)
|
|
||||||
} else {
|
|
||||||
emitter.onCompleted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
emitter.setCancellation { job.cancel() }
|
|
||||||
},
|
|
||||||
backpressureMode,
|
|
||||||
)
|
|
||||||
}
|
|
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
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package eu.kanade.data.source
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import tachiyomi.domain.source.model.Source
|
|
||||||
|
|
||||||
val sourceMapper: (eu.kanade.tachiyomi.source.Source) -> Source = { source ->
|
|
||||||
Source(
|
|
||||||
source.id,
|
|
||||||
source.lang,
|
|
||||||
source.name,
|
|
||||||
supportsLatest = false,
|
|
||||||
isStub = source is SourceManager.StubSource,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val catalogueSourceMapper: (CatalogueSource) -> Source = { source ->
|
|
||||||
sourceMapper(source).copy(supportsLatest = source.supportsLatest)
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package eu.kanade.data.source
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.model.SourcePagingSourceType
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import tachiyomi.data.DatabaseHandler
|
|
||||||
import tachiyomi.domain.source.model.Source
|
|
||||||
import tachiyomi.domain.source.model.SourceWithCount
|
|
||||||
|
|
||||||
class SourceRepositoryImpl(
|
|
||||||
private val sourceManager: SourceManager,
|
|
||||||
private val handler: DatabaseHandler,
|
|
||||||
) : SourceRepository {
|
|
||||||
|
|
||||||
override fun getSources(): Flow<List<Source>> {
|
|
||||||
return sourceManager.catalogueSources.map { sources ->
|
|
||||||
sources.map(catalogueSourceMapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getOnlineSources(): Flow<List<Source>> {
|
|
||||||
return sourceManager.onlineSources.map { sources ->
|
|
||||||
sources.map(sourceMapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>> {
|
|
||||||
val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
|
|
||||||
return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
|
|
||||||
sourceIdsWithCount
|
|
||||||
.filterNot { it.source == LocalSource.ID }
|
|
||||||
.map { (sourceId, count) ->
|
|
||||||
val source = sourceManager.getOrStub(sourceId).run {
|
|
||||||
sourceMapper(this)
|
|
||||||
}
|
|
||||||
source to count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>> {
|
|
||||||
val sourceIdWithNonLibraryManga = handler.subscribeToList { mangasQueries.getSourceIdsWithNonLibraryManga() }
|
|
||||||
return sourceIdWithNonLibraryManga.map { sourceId ->
|
|
||||||
sourceId.map { (sourceId, count) ->
|
|
||||||
val source = sourceManager.getOrStub(sourceId)
|
|
||||||
SourceWithCount(sourceMapper(source), count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun search(
|
|
||||||
sourceId: Long,
|
|
||||||
query: String,
|
|
||||||
filterList: FilterList,
|
|
||||||
): SourcePagingSourceType {
|
|
||||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
|
||||||
return SourceSearchPagingSource(source, query, filterList)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPopular(sourceId: Long): SourcePagingSourceType {
|
|
||||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
|
||||||
return SourcePopularPagingSource(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLatest(sourceId: Long): SourcePagingSourceType {
|
|
||||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
|
||||||
return SourceLatestPagingSource(source)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +1,95 @@
|
|||||||
package eu.kanade.domain
|
package eu.kanade.domain
|
||||||
|
|
||||||
import eu.kanade.data.source.SourceRepositoryImpl
|
import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
|
||||||
import eu.kanade.domain.category.interactor.CreateCategoryWithName
|
|
||||||
import eu.kanade.domain.category.interactor.DeleteCategory
|
|
||||||
import eu.kanade.domain.category.interactor.RenameCategory
|
|
||||||
import eu.kanade.domain.category.interactor.ReorderCategory
|
|
||||||
import eu.kanade.domain.category.interactor.ResetCategoryFlags
|
|
||||||
import eu.kanade.domain.category.interactor.SetDisplayModeForCategory
|
|
||||||
import eu.kanade.domain.category.interactor.SetMangaCategories
|
|
||||||
import eu.kanade.domain.category.interactor.SetSortModeForCategory
|
|
||||||
import eu.kanade.domain.category.interactor.UpdateCategory
|
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapter
|
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
|
||||||
import eu.kanade.domain.chapter.interactor.SetMangaDefaultChapterFlags
|
|
||||||
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.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
|
|
||||||
import eu.kanade.domain.chapter.interactor.UpdateChapter
|
|
||||||
import eu.kanade.domain.download.interactor.DeleteDownload
|
import eu.kanade.domain.download.interactor.DeleteDownload
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
||||||
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.history.interactor.GetNextChapters
|
import eu.kanade.domain.extension.interactor.TrustExtension
|
||||||
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
|
import eu.kanade.domain.manga.interactor.GetExcludedScanlators
|
||||||
import eu.kanade.domain.manga.interactor.GetFavorites
|
import eu.kanade.domain.manga.interactor.SetExcludedScanlators
|
||||||
import eu.kanade.domain.manga.interactor.GetLibraryManga
|
|
||||||
import eu.kanade.domain.manga.interactor.GetManga
|
|
||||||
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
|
|
||||||
import eu.kanade.domain.manga.interactor.NetworkToLocalManga
|
|
||||||
import eu.kanade.domain.manga.interactor.ResetViewerFlags
|
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
|
|
||||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
|
import eu.kanade.domain.source.interactor.GetIncognitoState
|
||||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||||
import eu.kanade.domain.source.interactor.GetRemoteManga
|
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
|
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleIncognito
|
||||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
import eu.kanade.domain.source.interactor.ToggleSource
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
import eu.kanade.domain.track.interactor.AddTracks
|
||||||
import eu.kanade.domain.track.interactor.DeleteTrack
|
import eu.kanade.domain.track.interactor.RefreshTracks
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
|
||||||
import eu.kanade.domain.track.interactor.GetTracksPerManga
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
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
|
||||||
import tachiyomi.data.manga.MangaRepositoryImpl
|
import tachiyomi.data.manga.MangaRepositoryImpl
|
||||||
import tachiyomi.data.source.SourceDataRepositoryImpl
|
import tachiyomi.data.release.ReleaseServiceImpl
|
||||||
|
import tachiyomi.data.source.SourceRepositoryImpl
|
||||||
|
import tachiyomi.data.source.StubSourceRepositoryImpl
|
||||||
import tachiyomi.data.track.TrackRepositoryImpl
|
import tachiyomi.data.track.TrackRepositoryImpl
|
||||||
import tachiyomi.data.updates.UpdatesRepositoryImpl
|
import tachiyomi.data.updates.UpdatesRepositoryImpl
|
||||||
|
import tachiyomi.domain.category.interactor.CreateCategoryWithName
|
||||||
|
import tachiyomi.domain.category.interactor.DeleteCategory
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
|
import tachiyomi.domain.category.interactor.RenameCategory
|
||||||
|
import tachiyomi.domain.category.interactor.ReorderCategory
|
||||||
|
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
||||||
|
import tachiyomi.domain.category.interactor.SetDisplayMode
|
||||||
|
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||||
|
import tachiyomi.domain.category.interactor.SetSortModeForCategory
|
||||||
|
import tachiyomi.domain.category.interactor.UpdateCategory
|
||||||
import tachiyomi.domain.category.repository.CategoryRepository
|
import tachiyomi.domain.category.repository.CategoryRepository
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChapter
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
|
import tachiyomi.domain.chapter.interactor.SetMangaDefaultChapterFlags
|
||||||
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
|
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
|
||||||
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
import tachiyomi.domain.chapter.repository.ChapterRepository
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
import tachiyomi.domain.history.interactor.GetHistory
|
import tachiyomi.domain.history.interactor.GetHistory
|
||||||
|
import tachiyomi.domain.history.interactor.GetNextChapters
|
||||||
import tachiyomi.domain.history.interactor.GetTotalReadDuration
|
import tachiyomi.domain.history.interactor.GetTotalReadDuration
|
||||||
import tachiyomi.domain.history.interactor.RemoveHistory
|
import tachiyomi.domain.history.interactor.RemoveHistory
|
||||||
import tachiyomi.domain.history.interactor.UpsertHistory
|
import tachiyomi.domain.history.interactor.UpsertHistory
|
||||||
import tachiyomi.domain.history.repository.HistoryRepository
|
import tachiyomi.domain.history.repository.HistoryRepository
|
||||||
|
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||||
|
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||||
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
|
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||||
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
|
import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId
|
||||||
|
import tachiyomi.domain.manga.interactor.GetMangaWithChapters
|
||||||
|
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||||
|
import tachiyomi.domain.manga.interactor.ResetViewerFlags
|
||||||
|
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
import tachiyomi.domain.source.repository.SourceDataRepository
|
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
||||||
|
import tachiyomi.domain.release.service.ReleaseService
|
||||||
|
import tachiyomi.domain.source.interactor.GetRemoteManga
|
||||||
|
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||||
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
|
import tachiyomi.domain.source.repository.StubSourceRepository
|
||||||
|
import tachiyomi.domain.track.interactor.DeleteTrack
|
||||||
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
|
import tachiyomi.domain.track.interactor.GetTracksPerManga
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
import tachiyomi.domain.track.repository.TrackRepository
|
import tachiyomi.domain.track.repository.TrackRepository
|
||||||
import tachiyomi.domain.updates.interactor.GetUpdates
|
import tachiyomi.domain.updates.interactor.GetUpdates
|
||||||
import tachiyomi.domain.updates.repository.UpdatesRepository
|
import tachiyomi.domain.updates.repository.UpdatesRepository
|
||||||
@ -79,43 +105,57 @@ class DomainModule : InjektModule {
|
|||||||
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
||||||
addFactory { GetCategories(get()) }
|
addFactory { GetCategories(get()) }
|
||||||
addFactory { ResetCategoryFlags(get(), get()) }
|
addFactory { ResetCategoryFlags(get(), get()) }
|
||||||
addFactory { SetDisplayModeForCategory(get(), get()) }
|
addFactory { SetDisplayMode(get()) }
|
||||||
addFactory { SetSortModeForCategory(get(), get()) }
|
addFactory { SetSortModeForCategory(get(), get()) }
|
||||||
addFactory { CreateCategoryWithName(get(), get()) }
|
addFactory { CreateCategoryWithName(get(), get()) }
|
||||||
addFactory { RenameCategory(get()) }
|
addFactory { RenameCategory(get()) }
|
||||||
addFactory { ReorderCategory(get()) }
|
addFactory { ReorderCategory(get()) }
|
||||||
addFactory { UpdateCategory(get()) }
|
addFactory { UpdateCategory(get()) }
|
||||||
addFactory { DeleteCategory(get()) }
|
addFactory { DeleteCategory(get(), get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||||
addFactory { GetDuplicateLibraryManga(get()) }
|
addFactory { GetDuplicateLibraryManga(get()) }
|
||||||
addFactory { GetFavorites(get()) }
|
addFactory { GetFavorites(get()) }
|
||||||
addFactory { GetLibraryManga(get()) }
|
addFactory { GetLibraryManga(get()) }
|
||||||
addFactory { GetMangaWithChapters(get(), get()) }
|
addFactory { GetMangaWithChapters(get(), 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 { SetMangaDefaultChapterFlags(get(), get(), get()) }
|
addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) }
|
||||||
addFactory { SetMangaViewerFlags(get()) }
|
addFactory { SetMangaViewerFlags(get()) }
|
||||||
addFactory { NetworkToLocalManga(get()) }
|
addFactory { NetworkToLocalManga(get()) }
|
||||||
addFactory { UpdateManga(get()) }
|
addFactory { UpdateManga(get(), get()) }
|
||||||
addFactory { SetMangaCategories(get()) }
|
addFactory { SetMangaCategories(get()) }
|
||||||
|
addFactory { GetExcludedScanlators(get()) }
|
||||||
|
addFactory { SetExcludedScanlators(get()) }
|
||||||
|
|
||||||
|
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
|
||||||
|
addFactory { GetApplicationRelease(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
||||||
|
addFactory { TrackChapter(get(), get(), get(), get()) }
|
||||||
|
addFactory { AddTracks(get(), get(), get(), get()) }
|
||||||
|
addFactory { RefreshTracks(get(), get(), get(), get()) }
|
||||||
addFactory { DeleteTrack(get()) }
|
addFactory { DeleteTrack(get()) }
|
||||||
addFactory { GetTracksPerManga(get()) }
|
addFactory { GetTracksPerManga(get()) }
|
||||||
addFactory { GetTracks(get()) }
|
addFactory { GetTracks(get()) }
|
||||||
addFactory { InsertTrack(get()) }
|
addFactory { InsertTrack(get()) }
|
||||||
|
addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
|
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
|
||||||
addFactory { GetChapter(get()) }
|
addFactory { GetChapter(get()) }
|
||||||
addFactory { GetChapterByMangaId(get()) }
|
addFactory { GetChaptersByMangaId(get()) }
|
||||||
|
addFactory { GetChapterByUrlAndMangaId(get()) }
|
||||||
addFactory { UpdateChapter(get()) }
|
addFactory { UpdateChapter(get()) }
|
||||||
addFactory { SetReadStatus(get(), get(), get(), get()) }
|
addFactory { SetReadStatus(get(), get(), get(), get()) }
|
||||||
addFactory { ShouldUpdateDbChapter() }
|
addFactory { ShouldUpdateDbChapter() }
|
||||||
addFactory { SyncChaptersWithSource(get(), get(), get(), get()) }
|
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
|
||||||
addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get()) }
|
addFactory { GetAvailableScanlators(get()) }
|
||||||
|
addFactory { FilterChaptersForDownload(get(), get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||||
addFactory { GetHistory(get()) }
|
addFactory { GetHistory(get()) }
|
||||||
@ -133,7 +173,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { GetUpdates(get()) }
|
addFactory { GetUpdates(get()) }
|
||||||
|
|
||||||
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
||||||
addSingletonFactory<SourceDataRepository> { SourceDataRepositoryImpl(get()) }
|
addSingletonFactory<StubSourceRepository> { StubSourceRepositoryImpl(get()) }
|
||||||
addFactory { GetEnabledSources(get(), get()) }
|
addFactory { GetEnabledSources(get(), get()) }
|
||||||
addFactory { GetLanguagesWithSources(get(), get()) }
|
addFactory { GetLanguagesWithSources(get(), get()) }
|
||||||
addFactory { GetRemoteManga(get()) }
|
addFactory { GetRemoteManga(get()) }
|
||||||
@ -143,5 +183,17 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
|
addFactory { TrustExtension(get(), get()) }
|
||||||
|
|
||||||
|
addSingletonFactory<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||||
|
addFactory { ExtensionRepoService(get(), get()) }
|
||||||
|
addFactory { GetExtensionRepo(get()) }
|
||||||
|
addFactory { GetExtensionRepoCount(get()) }
|
||||||
|
addFactory { CreateExtensionRepo(get(), get()) }
|
||||||
|
addFactory { DeleteExtensionRepo(get()) }
|
||||||
|
addFactory { ReplaceExtensionRepo(get()) }
|
||||||
|
addFactory { UpdateExtensionRepo(get(), get()) }
|
||||||
|
addFactory { ToggleIncognito(get()) }
|
||||||
|
addFactory { GetIncognitoState(get(), get(), get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package eu.kanade.domain.backup.service
|
|
||||||
|
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
|
||||||
import tachiyomi.core.provider.FolderProvider
|
|
||||||
|
|
||||||
class BackupPreferences(
|
|
||||||
private val folderProvider: FolderProvider,
|
|
||||||
private val preferenceStore: PreferenceStore,
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun backupsDirectory() = preferenceStore.getString("backup_directory", folderProvider.path())
|
|
||||||
|
|
||||||
fun numberOfBackups() = preferenceStore.getInt("backup_slots", 2)
|
|
||||||
|
|
||||||
fun backupInterval() = preferenceStore.getInt("backup_interval", 12)
|
|
||||||
}
|
|
@ -1,24 +1,38 @@
|
|||||||
package eu.kanade.domain.base
|
package eu.kanade.domain.base
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
import dev.icerock.moko.resources.StringResource
|
||||||
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
import eu.kanade.tachiyomi.util.system.GLUtil
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
class BasePreferences(
|
class BasePreferences(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun confirmExit() = preferenceStore.getBoolean("pref_confirm_exit", false)
|
fun downloadedOnly() = preferenceStore.getBoolean(
|
||||||
|
Preference.appStateKey("pref_downloaded_only"),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
fun downloadedOnly() = preferenceStore.getBoolean("pref_downloaded_only", false)
|
fun incognitoMode() = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false)
|
||||||
|
|
||||||
fun incognitoMode() = preferenceStore.getBoolean("incognito_mode", false)
|
|
||||||
|
|
||||||
fun automaticExtUpdates() = preferenceStore.getBoolean("automatic_ext_updates", true)
|
|
||||||
|
|
||||||
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
|
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
|
||||||
|
|
||||||
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType)
|
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false)
|
||||||
|
|
||||||
|
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
|
||||||
|
LEGACY(MR.strings.ext_installer_legacy, true),
|
||||||
|
PACKAGEINSTALLER(MR.strings.ext_installer_packageinstaller, true),
|
||||||
|
SHIZUKU(MR.strings.ext_installer_shizuku, false),
|
||||||
|
PRIVATE(MR.strings.ext_installer_private, false),
|
||||||
|
}
|
||||||
|
|
||||||
|
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
||||||
|
|
||||||
|
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
|
||||||
|
|
||||||
|
fun alwaysDecodeLongStripWithSSIV() = preferenceStore.getBoolean("pref_always_decode_long_strip_with_ssiv", false)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package eu.kanade.domain.base
|
package eu.kanade.domain.base
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues.ExtensionInstaller
|
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,
|
||||||
@ -18,7 +18,7 @@ class ExtensionInstallerPreference(
|
|||||||
|
|
||||||
override fun key() = "extension_installer"
|
override fun key() = "extension_installer"
|
||||||
|
|
||||||
val entries get() = ExtensionInstaller.values().run {
|
val entries get() = ExtensionInstaller.entries.run {
|
||||||
if (context.hasMiuiPackageInstaller) {
|
if (context.hasMiuiPackageInstaller) {
|
||||||
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
|
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package eu.kanade.domain.category.interactor
|
|
||||||
|
|
||||||
import logcat.LogPriority
|
|
||||||
import tachiyomi.core.util.lang.withNonCancellableContext
|
|
||||||
import tachiyomi.core.util.system.logcat
|
|
||||||
import tachiyomi.domain.category.model.CategoryUpdate
|
|
||||||
import tachiyomi.domain.category.repository.CategoryRepository
|
|
||||||
|
|
||||||
class DeleteCategory(
|
|
||||||
private val categoryRepository: CategoryRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(categoryId: Long) = withNonCancellableContext {
|
|
||||||
try {
|
|
||||||
categoryRepository.delete(categoryId)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.ERROR, e)
|
|
||||||
return@withNonCancellableContext Result.InternalError(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
val categories = categoryRepository.getAll()
|
|
||||||
val updates = categories.mapIndexed { index, category ->
|
|
||||||
CategoryUpdate(
|
|
||||||
id = category.id,
|
|
||||||
order = index.toLong(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
categoryRepository.updatePartial(updates)
|
|
||||||
Result.Success
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.ERROR, e)
|
|
||||||
Result.InternalError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Result {
|
|
||||||
object Success : Result()
|
|
||||||
data class InternalError(val error: Throwable) : Result()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package eu.kanade.domain.category.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
|
||||||
import tachiyomi.domain.category.repository.CategoryRepository
|
|
||||||
import tachiyomi.domain.library.model.plus
|
|
||||||
|
|
||||||
class ResetCategoryFlags(
|
|
||||||
private val preferences: LibraryPreferences,
|
|
||||||
private val categoryRepository: CategoryRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await() {
|
|
||||||
val display = preferences.libraryDisplayMode().get()
|
|
||||||
val sort = preferences.librarySortingMode().get()
|
|
||||||
categoryRepository.updateAllFlags(display + sort.type + sort.direction)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package eu.kanade.domain.category.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
|
||||||
import tachiyomi.domain.category.model.Category
|
|
||||||
import tachiyomi.domain.category.model.CategoryUpdate
|
|
||||||
import tachiyomi.domain.category.repository.CategoryRepository
|
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
|
||||||
import tachiyomi.domain.library.model.plus
|
|
||||||
|
|
||||||
class SetDisplayModeForCategory(
|
|
||||||
private val preferences: LibraryPreferences,
|
|
||||||
private val categoryRepository: CategoryRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(categoryId: Long, display: LibraryDisplayMode) {
|
|
||||||
val category = categoryRepository.get(categoryId) ?: return
|
|
||||||
val flags = category.flags + display
|
|
||||||
if (preferences.categorizedDisplaySettings().get()) {
|
|
||||||
categoryRepository.updatePartial(
|
|
||||||
CategoryUpdate(
|
|
||||||
id = category.id,
|
|
||||||
flags = flags,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
preferences.libraryDisplayMode().set(display)
|
|
||||||
categoryRepository.updateAllFlags(flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun await(category: Category, display: LibraryDisplayMode) {
|
|
||||||
await(category.id, display)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package eu.kanade.domain.category.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
|
||||||
import tachiyomi.domain.category.model.Category
|
|
||||||
import tachiyomi.domain.category.model.CategoryUpdate
|
|
||||||
import tachiyomi.domain.category.repository.CategoryRepository
|
|
||||||
import tachiyomi.domain.library.model.LibrarySort
|
|
||||||
import tachiyomi.domain.library.model.plus
|
|
||||||
|
|
||||||
class SetSortModeForCategory(
|
|
||||||
private val preferences: LibraryPreferences,
|
|
||||||
private val categoryRepository: CategoryRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(categoryId: Long, type: LibrarySort.Type, direction: LibrarySort.Direction) {
|
|
||||||
val category = categoryRepository.get(categoryId) ?: return
|
|
||||||
val flags = category.flags + type + direction
|
|
||||||
if (preferences.categorizedDisplaySettings().get()) {
|
|
||||||
categoryRepository.updatePartial(
|
|
||||||
CategoryUpdate(
|
|
||||||
id = category.id,
|
|
||||||
flags = flags,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
preferences.librarySortingMode().set(LibrarySort(type, direction))
|
|
||||||
categoryRepository.updateAllFlags(flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun await(category: Category, type: LibrarySort.Type, direction: LibrarySort.Direction) {
|
|
||||||
await(category.id, type, direction)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
package eu.kanade.domain.chapter.interactor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
|
|
||||||
|
class GetAvailableScanlators(
|
||||||
|
private val repository: ChapterRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private fun List<String>.cleanupAvailableScanlators(): Set<String> {
|
||||||
|
return mapNotNull { it.ifBlank { null } }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun await(mangaId: Long): Set<String> {
|
||||||
|
return repository.getScanlatorsByMangaId(mangaId)
|
||||||
|
.cleanupAvailableScanlators()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(mangaId: Long): Flow<Set<String>> {
|
||||||
|
return repository.getScanlatorsByMangaIdAsFlow(mangaId)
|
||||||
|
.map { it.cleanupAvailableScanlators() }
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package eu.kanade.domain.chapter.interactor
|
package eu.kanade.domain.chapter.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.download.interactor.DeleteDownload
|
import eu.kanade.domain.download.interactor.DeleteDownload
|
||||||
import eu.kanade.domain.download.service.DownloadPreferences
|
|
||||||
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
|
||||||
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
|
|
||||||
@ -72,9 +72,9 @@ class SetReadStatus(
|
|||||||
suspend fun await(manga: Manga, read: Boolean) =
|
suspend fun await(manga: Manga, read: Boolean) =
|
||||||
await(manga.id, read)
|
await(manga.id, read)
|
||||||
|
|
||||||
sealed class Result {
|
sealed interface Result {
|
||||||
object Success : Result()
|
data object Success : Result
|
||||||
object NoChapters : Result()
|
data object NoChapters : Result
|
||||||
data class InternalError(val error: Throwable) : Result()
|
data class InternalError(val error: Throwable) : Result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,36 +2,40 @@ package eu.kanade.domain.chapter.interactor
|
|||||||
|
|
||||||
import eu.kanade.domain.chapter.model.copyFromSChapter
|
import eu.kanade.domain.chapter.model.copyFromSChapter
|
||||||
import eu.kanade.domain.chapter.model.toSChapter
|
import eu.kanade.domain.chapter.model.toSChapter
|
||||||
|
import eu.kanade.domain.manga.interactor.GetExcludedScanlators
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.isLocal
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
|
|
||||||
import tachiyomi.data.chapter.ChapterSanitizer
|
import tachiyomi.data.chapter.ChapterSanitizer
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
|
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
|
||||||
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.model.NoChaptersException
|
import tachiyomi.domain.chapter.model.NoChaptersException
|
||||||
import tachiyomi.domain.chapter.model.toChapterUpdate
|
import tachiyomi.domain.chapter.model.toChapterUpdate
|
||||||
import tachiyomi.domain.chapter.repository.ChapterRepository
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
|
import tachiyomi.domain.chapter.service.ChapterRecognition
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import uy.kohesive.injekt.Injekt
|
import tachiyomi.source.local.isLocal
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.lang.Long.max
|
import java.lang.Long.max
|
||||||
import java.util.Date
|
import java.time.ZonedDateTime
|
||||||
import java.util.TreeSet
|
import java.util.TreeSet
|
||||||
|
|
||||||
class SyncChaptersWithSource(
|
class SyncChaptersWithSource(
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager,
|
||||||
private val downloadProvider: DownloadProvider = Injekt.get(),
|
private val downloadProvider: DownloadProvider,
|
||||||
private val chapterRepository: ChapterRepository = Injekt.get(),
|
private val chapterRepository: ChapterRepository,
|
||||||
private val shouldUpdateDbChapter: ShouldUpdateDbChapter = Injekt.get(),
|
private val shouldUpdateDbChapter: ShouldUpdateDbChapter,
|
||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga,
|
||||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
private val updateChapter: UpdateChapter,
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
private val getExcludedScanlators: GetExcludedScanlators,
|
||||||
|
private val libraryPreferences: LibraryPreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,11 +50,16 @@ class SyncChaptersWithSource(
|
|||||||
rawSourceChapters: List<SChapter>,
|
rawSourceChapters: List<SChapter>,
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
source: Source,
|
source: Source,
|
||||||
|
manualFetch: Boolean = false,
|
||||||
|
fetchWindow: Pair<Long, Long> = Pair(0, 0),
|
||||||
): List<Chapter> {
|
): List<Chapter> {
|
||||||
if (rawSourceChapters.isEmpty() && !source.isLocal()) {
|
if (rawSourceChapters.isEmpty() && !source.isLocal()) {
|
||||||
throw NoChaptersException()
|
throw NoChaptersException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val now = ZonedDateTime.now()
|
||||||
|
val nowMillis = now.toInstant().toEpochMilli()
|
||||||
|
|
||||||
val sourceChapters = rawSourceChapters
|
val sourceChapters = rawSourceChapters
|
||||||
.distinctBy { it.url }
|
.distinctBy { it.url }
|
||||||
.mapIndexed { i, sChapter ->
|
.mapIndexed { i, sChapter ->
|
||||||
@ -60,36 +69,27 @@ class SyncChaptersWithSource(
|
|||||||
.copy(mangaId = manga.id, sourceOrder = i.toLong())
|
.copy(mangaId = manga.id, sourceOrder = i.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chapters from db.
|
val dbChapters = getChaptersByMangaId.await(manga.id)
|
||||||
val dbChapters = getChapterByMangaId.await(manga.id)
|
|
||||||
|
|
||||||
// Chapters from the source not in db.
|
val newChapters = mutableListOf<Chapter>()
|
||||||
val toAdd = mutableListOf<Chapter>()
|
val updatedChapters = mutableListOf<Chapter>()
|
||||||
|
val removedChapters = dbChapters.filterNot { dbChapter ->
|
||||||
// Chapters whose metadata have changed.
|
|
||||||
val toChange = mutableListOf<Chapter>()
|
|
||||||
|
|
||||||
// Chapters from the db not in source.
|
|
||||||
val toDelete = dbChapters.filterNot { dbChapter ->
|
|
||||||
sourceChapters.any { sourceChapter ->
|
sourceChapters.any { sourceChapter ->
|
||||||
dbChapter.url == sourceChapter.url
|
dbChapter.url == sourceChapter.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val rightNow = Date().time
|
|
||||||
|
|
||||||
// Used to not set upload date of older chapters
|
// Used to not set upload date of older chapters
|
||||||
// to a higher value than newer chapters
|
// to a higher value than newer chapters
|
||||||
var maxSeenUploadDate = 0L
|
var maxSeenUploadDate = 0L
|
||||||
|
|
||||||
val sManga = manga.toSManga()
|
|
||||||
for (sourceChapter in sourceChapters) {
|
for (sourceChapter in sourceChapters) {
|
||||||
var chapter = sourceChapter
|
var chapter = sourceChapter
|
||||||
|
|
||||||
// Update metadata from source if necessary.
|
// Update metadata from source if necessary.
|
||||||
if (source is HttpSource) {
|
if (source is HttpSource) {
|
||||||
val sChapter = chapter.toSChapter()
|
val sChapter = chapter.toSChapter()
|
||||||
source.prepareNewChapter(sChapter, sManga)
|
source.prepareNewChapter(sChapter, manga.toSManga())
|
||||||
chapter = chapter.copyFromSChapter(sChapter)
|
chapter = chapter.copyFromSChapter(sChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,17 +101,22 @@ class SyncChaptersWithSource(
|
|||||||
|
|
||||||
if (dbChapter == null) {
|
if (dbChapter == null) {
|
||||||
val toAddChapter = if (chapter.dateUpload == 0L) {
|
val toAddChapter = if (chapter.dateUpload == 0L) {
|
||||||
val altDateUpload = if (maxSeenUploadDate == 0L) rightNow else maxSeenUploadDate
|
val altDateUpload = if (maxSeenUploadDate == 0L) nowMillis else maxSeenUploadDate
|
||||||
chapter.copy(dateUpload = altDateUpload)
|
chapter.copy(dateUpload = altDateUpload)
|
||||||
} else {
|
} else {
|
||||||
maxSeenUploadDate = max(maxSeenUploadDate, sourceChapter.dateUpload)
|
maxSeenUploadDate = max(maxSeenUploadDate, sourceChapter.dateUpload)
|
||||||
chapter
|
chapter
|
||||||
}
|
}
|
||||||
toAdd.add(toAddChapter)
|
newChapters.add(toAddChapter)
|
||||||
} else {
|
} else {
|
||||||
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
||||||
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
||||||
downloadManager.isChapterDownloaded(dbChapter.name, dbChapter.scanlator, manga.title, manga.source)
|
downloadManager.isChapterDownloaded(
|
||||||
|
dbChapter.name,
|
||||||
|
dbChapter.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
|
)
|
||||||
|
|
||||||
if (shouldRenameChapter) {
|
if (shouldRenameChapter) {
|
||||||
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
||||||
@ -125,38 +130,59 @@ class SyncChaptersWithSource(
|
|||||||
if (chapter.dateUpload != 0L) {
|
if (chapter.dateUpload != 0L) {
|
||||||
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
||||||
}
|
}
|
||||||
toChange.add(toChangeChapter)
|
updatedChapters.add(toChangeChapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
// Return if there's nothing to add, delete, or update to avoid unnecessary db transactions.
|
||||||
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
if (newChapters.isEmpty() && removedChapters.isEmpty() && updatedChapters.isEmpty()) {
|
||||||
|
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchWindow.first) {
|
||||||
|
updateManga.awaitUpdateFetchInterval(
|
||||||
|
manga,
|
||||||
|
now,
|
||||||
|
fetchWindow,
|
||||||
|
)
|
||||||
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val reAdded = mutableListOf<Chapter>()
|
val changedOrDuplicateReadUrls = mutableSetOf<String>()
|
||||||
|
|
||||||
val deletedChapterNumbers = TreeSet<Float>()
|
val deletedChapterNumbers = TreeSet<Double>()
|
||||||
val deletedReadChapterNumbers = TreeSet<Float>()
|
val deletedReadChapterNumbers = TreeSet<Double>()
|
||||||
val deletedBookmarkedChapterNumbers = TreeSet<Float>()
|
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
|
||||||
|
|
||||||
toDelete.forEach { chapter ->
|
val readChapterNumbers = dbChapters
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.read && it.isRecognizedNumber }
|
||||||
|
.map { it.chapterNumber }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
removedChapters.forEach { chapter ->
|
||||||
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
||||||
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
||||||
deletedChapterNumbers.add(chapter.chapterNumber)
|
deletedChapterNumbers.add(chapter.chapterNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
val deletedChapterNumberDateFetchMap = toDelete.sortedByDescending { it.dateFetch }
|
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
|
||||||
.associate { it.chapterNumber to it.dateFetch }
|
.associate { it.chapterNumber to it.dateFetch }
|
||||||
|
|
||||||
|
val markDuplicateAsRead = libraryPreferences.markDuplicateReadChapterAsRead().get()
|
||||||
|
.contains(LibraryPreferences.MARK_DUPLICATE_CHAPTER_READ_NEW)
|
||||||
|
|
||||||
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
||||||
// Sources MUST return the chapters from most to less recent, which is common.
|
// Sources MUST return the chapters from most to less recent, which is common.
|
||||||
var itemCount = toAdd.size
|
var itemCount = newChapters.size
|
||||||
var updatedToAdd = toAdd.map { toAddItem ->
|
var updatedToAdd = newChapters.map { toAddItem ->
|
||||||
var chapter = toAddItem.copy(dateFetch = rightNow + itemCount--)
|
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||||
|
|
||||||
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
if (chapter.chapterNumber in readChapterNumbers && markDuplicateAsRead) {
|
||||||
|
changedOrDuplicateReadUrls.add(chapter.url)
|
||||||
|
chapter = chapter.copy(read = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
@ -168,13 +194,13 @@ class SyncChaptersWithSource(
|
|||||||
chapter = chapter.copy(dateFetch = it)
|
chapter = chapter.copy(dateFetch = it)
|
||||||
}
|
}
|
||||||
|
|
||||||
reAdded.add(chapter)
|
changedOrDuplicateReadUrls.add(chapter.url)
|
||||||
|
|
||||||
chapter
|
chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toDelete.isNotEmpty()) {
|
if (removedChapters.isNotEmpty()) {
|
||||||
val toDeleteIds = toDelete.map { it.id }
|
val toDeleteIds = removedChapters.map { it.id }
|
||||||
chapterRepository.removeChaptersWithIds(toDeleteIds)
|
chapterRepository.removeChaptersWithIds(toDeleteIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,17 +208,18 @@ class SyncChaptersWithSource(
|
|||||||
updatedToAdd = chapterRepository.addAll(updatedToAdd)
|
updatedToAdd = chapterRepository.addAll(updatedToAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toChange.isNotEmpty()) {
|
if (updatedChapters.isNotEmpty()) {
|
||||||
val chapterUpdates = toChange.map { it.toChapterUpdate() }
|
val chapterUpdates = updatedChapters.map { it.toChapterUpdate() }
|
||||||
updateChapter.awaitAll(chapterUpdates)
|
updateChapter.awaitAll(chapterUpdates)
|
||||||
}
|
}
|
||||||
|
updateManga.awaitUpdateFetchInterval(manga, now, fetchWindow)
|
||||||
|
|
||||||
// Set this manga as updated since chapters were changed
|
// Set this manga as updated since chapters were changed
|
||||||
// Note that last_update actually represents last time the chapter list changed at all
|
// Note that last_update actually represents last time the chapter list changed at all
|
||||||
updateManga.awaitUpdateLastUpdate(manga.id)
|
updateManga.awaitUpdateLastUpdate(manga.id)
|
||||||
|
|
||||||
val reAddedUrls = reAdded.map { it.url }.toHashSet()
|
val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet()
|
||||||
|
|
||||||
return updatedToAdd.filterNot { it.url in reAddedUrls }
|
return updatedToAdd.filterNot { it.url in changedOrDuplicateReadUrls || it.scanlator in excludedScanlators }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package eu.kanade.domain.chapter.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
|
||||||
import logcat.LogPriority
|
|
||||||
import tachiyomi.core.util.system.logcat
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
|
||||||
import tachiyomi.domain.chapter.model.toChapterUpdate
|
|
||||||
import tachiyomi.domain.track.model.Track
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class SyncChaptersWithTrackServiceTwoWay(
|
|
||||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(
|
|
||||||
chapters: List<Chapter>,
|
|
||||||
remoteTrack: Track,
|
|
||||||
service: TrackService,
|
|
||||||
) {
|
|
||||||
val sortedChapters = chapters.sortedBy { it.chapterNumber }
|
|
||||||
val chapterUpdates = sortedChapters
|
|
||||||
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
|
|
||||||
.map { it.copy(read = true).toChapterUpdate() }
|
|
||||||
|
|
||||||
// only take into account continuous reading
|
|
||||||
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
|
|
||||||
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
|
|
||||||
|
|
||||||
try {
|
|
||||||
service.update(updatedTrack.toDbTrack())
|
|
||||||
updateChapter.awaitAll(chapterUpdates)
|
|
||||||
insertTrack.await(updatedTrack)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
logcat(LogPriority.WARN, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ fun Chapter.toSChapter(): SChapter {
|
|||||||
it.url = url
|
it.url = url
|
||||||
it.name = name
|
it.name = name
|
||||||
it.date_upload = dateUpload
|
it.date_upload = dateUpload
|
||||||
it.chapter_number = chapterNumber
|
it.chapter_number = chapterNumber.toFloat()
|
||||||
it.scanlator = scanlator
|
it.scanlator = scanlator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,8 +21,8 @@ fun Chapter.copyFromSChapter(sChapter: SChapter): Chapter {
|
|||||||
name = sChapter.name,
|
name = sChapter.name,
|
||||||
url = sChapter.url,
|
url = sChapter.url,
|
||||||
dateUpload = sChapter.date_upload,
|
dateUpload = sChapter.date_upload,
|
||||||
chapterNumber = sChapter.chapter_number,
|
chapterNumber = sChapter.chapter_number.toDouble(),
|
||||||
scanlator = sChapter.scanlator?.ifBlank { null },
|
scanlator = sChapter.scanlator?.ifBlank { null }?.trim(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +37,6 @@ fun Chapter.toDbChapter(): DbChapter = ChapterImpl().also {
|
|||||||
it.last_page_read = lastPageRead.toInt()
|
it.last_page_read = lastPageRead.toInt()
|
||||||
it.date_fetch = dateFetch
|
it.date_fetch = dateFetch
|
||||||
it.date_upload = dateUpload
|
it.date_upload = dateUpload
|
||||||
it.chapter_number = chapterNumber
|
it.chapter_number = chapterNumber.toFloat()
|
||||||
it.source_order = sourceOrder.toInt()
|
it.source_order = sourceOrder.toInt()
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package eu.kanade.domain.chapter.model
|
package eu.kanade.domain.chapter.model
|
||||||
|
|
||||||
import eu.kanade.domain.manga.model.downloadedFilter
|
import eu.kanade.domain.manga.model.downloadedFilter
|
||||||
import eu.kanade.domain.manga.model.isLocal
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.ui.manga.ChapterList
|
||||||
import eu.kanade.tachiyomi.ui.manga.ChapterItem
|
|
||||||
import eu.kanade.tachiyomi.util.chapter.getChapterSort
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
|
import tachiyomi.domain.chapter.service.getChapterSort
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.TriStateFilter
|
import tachiyomi.domain.manga.model.applyFilter
|
||||||
|
import tachiyomi.source.local.isLocal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the view filters to the list of chapters obtained from the database.
|
* Applies the view filters to the list of chapters obtained from the database.
|
||||||
@ -20,30 +19,17 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager):
|
|||||||
val downloadedFilter = manga.downloadedFilter
|
val downloadedFilter = manga.downloadedFilter
|
||||||
val bookmarkedFilter = manga.bookmarkedFilter
|
val bookmarkedFilter = manga.bookmarkedFilter
|
||||||
|
|
||||||
return filter { chapter ->
|
return filter { chapter -> applyFilter(unreadFilter) { !chapter.read } }
|
||||||
when (unreadFilter) {
|
.filter { chapter -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
||||||
TriStateFilter.DISABLED -> true
|
|
||||||
TriStateFilter.ENABLED_IS -> !chapter.read
|
|
||||||
TriStateFilter.ENABLED_NOT -> chapter.read
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter { chapter ->
|
.filter { chapter ->
|
||||||
when (bookmarkedFilter) {
|
applyFilter(downloadedFilter) {
|
||||||
TriStateFilter.DISABLED -> true
|
val downloaded = downloadManager.isChapterDownloaded(
|
||||||
TriStateFilter.ENABLED_IS -> chapter.bookmark
|
chapter.name,
|
||||||
TriStateFilter.ENABLED_NOT -> !chapter.bookmark
|
chapter.scanlator,
|
||||||
}
|
manga.title,
|
||||||
}
|
manga.source,
|
||||||
.filter { chapter ->
|
)
|
||||||
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source)
|
downloaded || isLocalManga
|
||||||
val downloadState = when {
|
|
||||||
downloaded -> Download.State.DOWNLOADED
|
|
||||||
else -> Download.State.NOT_DOWNLOADED
|
|
||||||
}
|
|
||||||
when (downloadedFilter) {
|
|
||||||
TriStateFilter.DISABLED -> true
|
|
||||||
TriStateFilter.ENABLED_IS -> downloadState == Download.State.DOWNLOADED || isLocalManga
|
|
||||||
TriStateFilter.ENABLED_NOT -> downloadState != Download.State.DOWNLOADED && !isLocalManga
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sortedWith(getChapterSort(manga))
|
.sortedWith(getChapterSort(manga))
|
||||||
@ -53,32 +39,14 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager):
|
|||||||
* Applies the view filters to the list of chapters obtained from the database.
|
* Applies the view filters to the list of chapters obtained from the database.
|
||||||
* @return an observable of the list of chapters filtered and sorted.
|
* @return an observable of the list of chapters filtered and sorted.
|
||||||
*/
|
*/
|
||||||
fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
|
fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item> {
|
||||||
val isLocalManga = manga.isLocal()
|
val isLocalManga = manga.isLocal()
|
||||||
val unreadFilter = manga.unreadFilter
|
val unreadFilter = manga.unreadFilter
|
||||||
val downloadedFilter = manga.downloadedFilter
|
val downloadedFilter = manga.downloadedFilter
|
||||||
val bookmarkedFilter = manga.bookmarkedFilter
|
val bookmarkedFilter = manga.bookmarkedFilter
|
||||||
return asSequence()
|
return asSequence()
|
||||||
.filter { (chapter) ->
|
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
|
||||||
when (unreadFilter) {
|
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
||||||
TriStateFilter.DISABLED -> true
|
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
||||||
TriStateFilter.ENABLED_IS -> !chapter.read
|
|
||||||
TriStateFilter.ENABLED_NOT -> chapter.read
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter { (chapter) ->
|
|
||||||
when (bookmarkedFilter) {
|
|
||||||
TriStateFilter.DISABLED -> true
|
|
||||||
TriStateFilter.ENABLED_IS -> chapter.bookmark
|
|
||||||
TriStateFilter.ENABLED_NOT -> !chapter.bookmark
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.filter {
|
|
||||||
when (downloadedFilter) {
|
|
||||||
TriStateFilter.DISABLED -> true
|
|
||||||
TriStateFilter.ENABLED_IS -> it.isDownloaded || isLocalManga
|
|
||||||
TriStateFilter.ENABLED_NOT -> !it.isDownloaded && !isLocalManga
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
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 eu.kanade.tachiyomi.source.SourceManager
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.core.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
|
||||||
|
|
||||||
class DeleteDownload(
|
class DeleteDownload(
|
||||||
private val sourceManager: SourceManager,
|
private val sourceManager: SourceManager,
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package eu.kanade.domain.download.service
|
|
||||||
|
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
|
||||||
import tachiyomi.core.provider.FolderProvider
|
|
||||||
|
|
||||||
class DownloadPreferences(
|
|
||||||
private val folderProvider: FolderProvider,
|
|
||||||
private val preferenceStore: PreferenceStore,
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun downloadsDirectory() = preferenceStore.getString("download_directory", folderProvider.path())
|
|
||||||
|
|
||||||
fun downloadOnlyOverWifi() = preferenceStore.getBoolean("pref_download_only_over_wifi_key", true)
|
|
||||||
|
|
||||||
fun saveChaptersAsCBZ() = preferenceStore.getBoolean("save_chapter_as_cbz", true)
|
|
||||||
|
|
||||||
fun splitTallImages() = preferenceStore.getBoolean("split_tall_images", false)
|
|
||||||
|
|
||||||
fun autoDownloadWhileReading() = preferenceStore.getInt("auto_download_while_reading", 0)
|
|
||||||
|
|
||||||
fun removeAfterReadSlots() = preferenceStore.getInt("remove_after_read_slots", -1)
|
|
||||||
|
|
||||||
fun removeAfterMarkedAsRead() = preferenceStore.getBoolean("pref_remove_after_marked_as_read_key", false)
|
|
||||||
|
|
||||||
fun removeBookmarkedChapters() = preferenceStore.getBoolean("pref_remove_bookmarked", false)
|
|
||||||
|
|
||||||
fun removeExcludeCategories() = preferenceStore.getStringSet("remove_exclude_categories", emptySet())
|
|
||||||
|
|
||||||
fun downloadNewChapters() = preferenceStore.getBoolean("download_new", false)
|
|
||||||
|
|
||||||
fun downloadNewChapterCategories() = preferenceStore.getStringSet("download_new_categories", emptySet())
|
|
||||||
|
|
||||||
fun downloadNewChapterCategoriesExclude() = preferenceStore.getStringSet("download_new_categories_exclude", emptySet())
|
|
||||||
}
|
|
@ -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,
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package eu.kanade.domain.extension.interactor
|
||||||
|
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
|
class TrustExtension(
|
||||||
|
private val extensionRepoRepository: ExtensionRepoRepository,
|
||||||
|
private val preferences: SourcePreferences,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
|
||||||
|
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
|
||||||
|
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) {
|
||||||
|
preferences.trustedExtensions().getAndSet { exts ->
|
||||||
|
// Remove previously trusted versions
|
||||||
|
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
|
||||||
|
|
||||||
|
removed.also { it += "$pkgName:$versionCode:$signatureHash" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun revokeAll() {
|
||||||
|
preferences.trustedExtensions().delete()
|
||||||
|
}
|
||||||
|
}
|
@ -1,111 +0,0 @@
|
|||||||
package eu.kanade.domain.library.service
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
|
||||||
import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD
|
|
||||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
|
|
||||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
|
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
|
||||||
import tachiyomi.core.preference.PreferenceStore
|
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
|
||||||
import tachiyomi.domain.library.model.LibrarySort
|
|
||||||
import tachiyomi.domain.manga.model.Manga
|
|
||||||
|
|
||||||
class LibraryPreferences(
|
|
||||||
private val preferenceStore: PreferenceStore,
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun libraryDisplayMode() = preferenceStore.getObject("pref_display_mode_library", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
|
|
||||||
|
|
||||||
fun librarySortingMode() = preferenceStore.getObject("library_sorting_mode", LibrarySort.default, LibrarySort.Serializer::serialize, LibrarySort.Serializer::deserialize)
|
|
||||||
|
|
||||||
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
|
|
||||||
|
|
||||||
fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0)
|
|
||||||
|
|
||||||
fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0)
|
|
||||||
fun libraryUpdateLastTimestamp() = preferenceStore.getLong("library_update_last_timestamp", 0L)
|
|
||||||
|
|
||||||
fun libraryUpdateDeviceRestriction() = preferenceStore.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
|
|
||||||
fun libraryUpdateMangaRestriction() = preferenceStore.getStringSet("library_update_manga_restriction", setOf(MANGA_HAS_UNREAD, MANGA_NON_COMPLETED, MANGA_NON_READ))
|
|
||||||
|
|
||||||
fun autoUpdateMetadata() = preferenceStore.getBoolean("auto_update_metadata", false)
|
|
||||||
|
|
||||||
fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false)
|
|
||||||
|
|
||||||
fun showContinueReadingButton() = preferenceStore.getBoolean("display_continue_reading_button", false)
|
|
||||||
|
|
||||||
// region Filter
|
|
||||||
|
|
||||||
fun filterDownloaded() = preferenceStore.getInt("pref_filter_library_downloaded", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
fun filterUnread() = preferenceStore.getInt("pref_filter_library_unread", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
fun filterStarted() = preferenceStore.getInt("pref_filter_library_started", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
fun filterBookmarked() = preferenceStore.getInt("pref_filter_library_bookmarked", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
fun filterCompleted() = preferenceStore.getInt("pref_filter_library_completed", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
fun filterTracking(name: Int) = preferenceStore.getInt("pref_filter_library_tracked_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Badges
|
|
||||||
|
|
||||||
fun downloadBadge() = preferenceStore.getBoolean("display_download_badge", false)
|
|
||||||
|
|
||||||
fun localBadge() = preferenceStore.getBoolean("display_local_badge", true)
|
|
||||||
|
|
||||||
fun languageBadge() = preferenceStore.getBoolean("display_language_badge", false)
|
|
||||||
|
|
||||||
fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true)
|
|
||||||
fun newUpdatesCount() = preferenceStore.getInt("library_unseen_updates_count", 0)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Category
|
|
||||||
|
|
||||||
fun defaultCategory() = preferenceStore.getInt("default_category", -1)
|
|
||||||
|
|
||||||
fun lastUsedCategory() = preferenceStore.getInt("last_used_category", 0)
|
|
||||||
|
|
||||||
fun categoryTabs() = preferenceStore.getBoolean("display_category_tabs", true)
|
|
||||||
|
|
||||||
fun categoryNumberOfItems() = preferenceStore.getBoolean("display_number_of_items", false)
|
|
||||||
|
|
||||||
fun categorizedDisplaySettings() = preferenceStore.getBoolean("categorized_display", false)
|
|
||||||
|
|
||||||
fun libraryUpdateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet())
|
|
||||||
|
|
||||||
fun libraryUpdateCategoriesExclude() = preferenceStore.getStringSet("library_update_categories_exclude", emptySet())
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Chapter
|
|
||||||
|
|
||||||
fun filterChapterByRead() = preferenceStore.getLong("default_chapter_filter_by_read", Manga.SHOW_ALL)
|
|
||||||
|
|
||||||
fun filterChapterByDownloaded() = preferenceStore.getLong("default_chapter_filter_by_downloaded", Manga.SHOW_ALL)
|
|
||||||
|
|
||||||
fun filterChapterByBookmarked() = preferenceStore.getLong("default_chapter_filter_by_bookmarked", Manga.SHOW_ALL)
|
|
||||||
|
|
||||||
// and upload date
|
|
||||||
fun sortChapterBySourceOrNumber() = preferenceStore.getLong("default_chapter_sort_by_source_or_number", Manga.CHAPTER_SORTING_SOURCE)
|
|
||||||
|
|
||||||
fun displayChapterByNameOrNumber() = preferenceStore.getLong("default_chapter_display_by_name_or_number", Manga.CHAPTER_DISPLAY_NAME)
|
|
||||||
|
|
||||||
fun sortChapterByAscendingOrDescending() = preferenceStore.getLong("default_chapter_sort_by_ascending_or_descending", Manga.CHAPTER_SORT_DESC)
|
|
||||||
|
|
||||||
fun setChapterSettingsDefault(manga: Manga) {
|
|
||||||
filterChapterByRead().set(manga.unreadFilterRaw)
|
|
||||||
filterChapterByDownloaded().set(manga.downloadedFilterRaw)
|
|
||||||
filterChapterByBookmarked().set(manga.bookmarkedFilterRaw)
|
|
||||||
sortChapterBySourceOrNumber().set(manga.sorting)
|
|
||||||
displayChapterByNameOrNumber().set(manga.displayMode)
|
|
||||||
sortChapterByAscendingOrDescending().set(if (manga.sortDescending()) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun autoClearChapterCache() = preferenceStore.getBoolean("auto_clear_chapter_cache", false)
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
|
||||||
|
class GetExcludedScanlators(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(mangaId: Long): Set<String> {
|
||||||
|
return handler.awaitList {
|
||||||
|
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
|
||||||
|
}
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(mangaId: Long): Flow<Set<String>> {
|
||||||
|
return handler.subscribeToList {
|
||||||
|
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
|
||||||
|
}
|
||||||
|
.map { it.toSet() }
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package eu.kanade.domain.manga.interactor
|
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
|
||||||
|
|
||||||
class GetLibraryManga(
|
|
||||||
private val mangaRepository: MangaRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(): List<LibraryManga> {
|
|
||||||
return mangaRepository.getLibraryManga()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun subscribe(): Flow<List<LibraryManga>> {
|
|
||||||
return mangaRepository.getLibraryMangaAsFlow()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,22 @@
|
|||||||
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
|
import tachiyomi.data.DatabaseHandler
|
||||||
|
|
||||||
|
class SetExcludedScanlators(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(mangaId: Long, excludedScanlators: Set<String>) {
|
||||||
|
handler.await(inTransaction = true) {
|
||||||
|
val currentExcluded = handler.awaitList {
|
||||||
|
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
|
||||||
|
}.toSet()
|
||||||
|
val toAdd = excludedScanlators.minus(currentExcluded)
|
||||||
|
for (scanlator in toAdd) {
|
||||||
|
excluded_scanlatorsQueries.insert(mangaId, scanlator)
|
||||||
|
}
|
||||||
|
val toRemove = currentExcluded.minus(excludedScanlators)
|
||||||
|
excluded_scanlatorsQueries.remove(mangaId, toRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.domain.manga.interactor
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||||
import tachiyomi.domain.manga.model.MangaUpdate
|
import tachiyomi.domain.manga.model.MangaUpdate
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
|
|
||||||
@ -9,22 +9,22 @@ class SetMangaViewerFlags(
|
|||||||
private val mangaRepository: MangaRepository,
|
private val mangaRepository: MangaRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun awaitSetMangaReadingMode(id: Long, flag: Long) {
|
suspend fun awaitSetReadingMode(id: Long, flag: Long) {
|
||||||
val manga = mangaRepository.getMangaById(id)
|
val manga = mangaRepository.getMangaById(id)
|
||||||
mangaRepository.update(
|
mangaRepository.update(
|
||||||
MangaUpdate(
|
MangaUpdate(
|
||||||
id = id,
|
id = id,
|
||||||
viewerFlags = manga.viewerFlags.setFlag(flag, ReadingModeType.MASK.toLong()),
|
viewerFlags = manga.viewerFlags.setFlag(flag, ReadingMode.MASK.toLong()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun awaitSetOrientationType(id: Long, flag: Long) {
|
suspend fun awaitSetOrientation(id: Long, flag: Long) {
|
||||||
val manga = mangaRepository.getMangaById(id)
|
val manga = mangaRepository.getMangaById(id)
|
||||||
mangaRepository.update(
|
mangaRepository.update(
|
||||||
MangaUpdate(
|
MangaUpdate(
|
||||||
id = id,
|
id = id,
|
||||||
viewerFlags = manga.viewerFlags.setFlag(flag, OrientationType.MASK.toLong()),
|
viewerFlags = manga.viewerFlags.setFlag(flag, ReaderOrientation.MASK.toLong()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package eu.kanade.domain.manga.interactor
|
package eu.kanade.domain.manga.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.manga.model.hasCustomCover
|
import eu.kanade.domain.manga.model.hasCustomCover
|
||||||
import eu.kanade.domain.manga.model.isLocal
|
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaUpdate
|
import tachiyomi.domain.manga.model.MangaUpdate
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
|
import tachiyomi.source.local.isLocal
|
||||||
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.ZonedDateTime
|
||||||
|
|
||||||
class UpdateManga(
|
class UpdateManga(
|
||||||
private val mangaRepository: MangaRepository,
|
private val mangaRepository: MangaRepository,
|
||||||
|
private val fetchInterval: FetchInterval,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
|
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
|
||||||
@ -43,14 +46,14 @@ class UpdateManga(
|
|||||||
// Never refresh covers if the url is empty to avoid "losing" existing covers
|
// Never refresh covers if the url is empty to avoid "losing" existing covers
|
||||||
remoteManga.thumbnail_url.isNullOrEmpty() -> null
|
remoteManga.thumbnail_url.isNullOrEmpty() -> null
|
||||||
!manualFetch && localManga.thumbnailUrl == remoteManga.thumbnail_url -> null
|
!manualFetch && localManga.thumbnailUrl == remoteManga.thumbnail_url -> null
|
||||||
localManga.isLocal() -> Date().time
|
localManga.isLocal() -> Instant.now().toEpochMilli()
|
||||||
localManga.hasCustomCover(coverCache) -> {
|
localManga.hasCustomCover(coverCache) -> {
|
||||||
coverCache.deleteFromCache(localManga, false)
|
coverCache.deleteFromCache(localManga, false)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
coverCache.deleteFromCache(localManga, false)
|
coverCache.deleteFromCache(localManga, false)
|
||||||
Date().time
|
Instant.now().toEpochMilli()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,17 +76,27 @@ class UpdateManga(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun awaitUpdateFetchInterval(
|
||||||
|
manga: Manga,
|
||||||
|
dateTime: ZonedDateTime = ZonedDateTime.now(),
|
||||||
|
window: Pair<Long, Long> = fetchInterval.getWindow(dateTime),
|
||||||
|
): Boolean {
|
||||||
|
return mangaRepository.update(
|
||||||
|
fetchInterval.toMangaUpdate(manga, dateTime, window),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean {
|
suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean {
|
||||||
return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Date().time))
|
return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Instant.now().toEpochMilli()))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun awaitUpdateCoverLastModified(mangaId: Long): Boolean {
|
suspend fun awaitUpdateCoverLastModified(mangaId: Long): Boolean {
|
||||||
return mangaRepository.update(MangaUpdate(id = mangaId, coverLastModified = Date().time))
|
return mangaRepository.update(MangaUpdate(id = mangaId, coverLastModified = Instant.now().toEpochMilli()))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun awaitUpdateFavorite(mangaId: Long, favorite: Boolean): Boolean {
|
suspend fun awaitUpdateFavorite(mangaId: Long, favorite: Boolean): Boolean {
|
||||||
val dateAdded = when (favorite) {
|
val dateAdded = when (favorite) {
|
||||||
true -> Date().time
|
true -> Instant.now().toEpochMilli()
|
||||||
false -> 0
|
false -> 0
|
||||||
}
|
}
|
||||||
return mangaRepository.update(
|
return mangaRepository.update(
|
||||||
|
@ -2,38 +2,37 @@ package eu.kanade.domain.manga.model
|
|||||||
|
|
||||||
import eu.kanade.domain.base.BasePreferences
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
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.ComicInfoPublishingStatus
|
||||||
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.TriStateFilter
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
// TODO: move these into the domain model
|
// TODO: move these into the domain model
|
||||||
val Manga.readingModeType: Long
|
val Manga.readingMode: Long
|
||||||
get() = viewerFlags and ReadingModeType.MASK.toLong()
|
get() = viewerFlags and ReadingMode.MASK.toLong()
|
||||||
|
|
||||||
val Manga.orientationType: Long
|
val Manga.readerOrientation: Long
|
||||||
get() = viewerFlags and OrientationType.MASK.toLong()
|
get() = viewerFlags and ReaderOrientation.MASK.toLong()
|
||||||
|
|
||||||
val Manga.downloadedFilter: TriStateFilter
|
val Manga.downloadedFilter: TriState
|
||||||
get() {
|
get() {
|
||||||
if (forceDownloaded()) return TriStateFilter.ENABLED_IS
|
if (Injekt.get<BasePreferences>().downloadedOnly().get()) return TriState.ENABLED_IS
|
||||||
return when (downloadedFilterRaw) {
|
return when (downloadedFilterRaw) {
|
||||||
Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS
|
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
|
||||||
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT
|
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
|
||||||
else -> TriStateFilter.DISABLED
|
else -> TriState.DISABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun Manga.chaptersFiltered(): Boolean {
|
fun Manga.chaptersFiltered(): Boolean {
|
||||||
return unreadFilter != TriStateFilter.DISABLED ||
|
return unreadFilter != TriState.DISABLED ||
|
||||||
downloadedFilter != TriStateFilter.DISABLED ||
|
downloadedFilter != TriState.DISABLED ||
|
||||||
bookmarkedFilter != TriStateFilter.DISABLED
|
bookmarkedFilter != TriState.DISABLED
|
||||||
}
|
|
||||||
fun Manga.forceDownloaded(): Boolean {
|
|
||||||
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Manga.toSManga(): SManga = SManga.create().also {
|
fun Manga.toSManga(): SManga = SManga.create().also {
|
||||||
@ -86,8 +85,43 @@ fun SManga.toDomainManga(sourceId: Long): Manga {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Manga.isLocal(): Boolean = source == LocalSource.ID
|
|
||||||
|
|
||||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
||||||
return coverCache.getCustomCoverFile(id).exists()
|
return coverCache.getCustomCoverFile(id).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ComicInfo instance based on the manga and chapter metadata.
|
||||||
|
*/
|
||||||
|
fun getComicInfo(
|
||||||
|
manga: Manga,
|
||||||
|
chapter: Chapter,
|
||||||
|
urls: List<String>,
|
||||||
|
categories: List<String>?,
|
||||||
|
sourceName: String,
|
||||||
|
) = ComicInfo(
|
||||||
|
title = ComicInfo.Title(chapter.name),
|
||||||
|
series = ComicInfo.Series(manga.title),
|
||||||
|
number = chapter.chapterNumber.takeIf { it >= 0 }?.let {
|
||||||
|
if ((it.rem(1) == 0.0)) {
|
||||||
|
ComicInfo.Number(it.toInt().toString())
|
||||||
|
} else {
|
||||||
|
ComicInfo.Number(it.toString())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
web = ComicInfo.Web(urls.joinToString(" ")),
|
||||||
|
summary = manga.description?.let { ComicInfo.Summary(it) },
|
||||||
|
writer = manga.author?.let { ComicInfo.Writer(it) },
|
||||||
|
penciller = manga.artist?.let { ComicInfo.Penciller(it) },
|
||||||
|
translator = chapter.scanlator?.let { ComicInfo.Translator(it) },
|
||||||
|
genre = manga.genre?.let { ComicInfo.Genre(it.joinToString()) },
|
||||||
|
publishingStatus = ComicInfo.PublishingStatusTachiyomi(
|
||||||
|
ComicInfoPublishingStatus.toComicInfoValue(manga.status),
|
||||||
|
),
|
||||||
|
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
|
||||||
|
source = ComicInfo.SourceMihon(sourceName),
|
||||||
|
inker = null,
|
||||||
|
colorist = null,
|
||||||
|
letterer = null,
|
||||||
|
coverArtist = null,
|
||||||
|
tags = null,
|
||||||
|
)
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import tachiyomi.domain.source.model.Pin
|
import tachiyomi.domain.source.model.Pin
|
||||||
import tachiyomi.domain.source.model.Pins
|
import tachiyomi.domain.source.model.Pins
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
|
import tachiyomi.source.local.isLocal
|
||||||
|
|
||||||
class GetEnabledSources(
|
class GetEnabledSources(
|
||||||
private val repository: SourceRepository,
|
private val repository: SourceRepository,
|
||||||
@ -24,7 +24,7 @@ class GetEnabledSources(
|
|||||||
repository.getSources(),
|
repository.getSources(),
|
||||||
) { pinnedSourceIds, enabledLanguages, disabledSources, lastUsedSource, sources ->
|
) { pinnedSourceIds, enabledLanguages, disabledSources, lastUsedSource, sources ->
|
||||||
sources
|
sources
|
||||||
.filter { it.lang in enabledLanguages || it.id == LocalSource.ID }
|
.filter { it.lang in enabledLanguages || it.isLocal() }
|
||||||
.filterNot { it.id.toString() in disabledSources }
|
.filterNot { it.id.toString() in disabledSources }
|
||||||
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
|
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
|
||||||
.flatMap {
|
.flatMap {
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
||||||
|
class GetIncognitoState(
|
||||||
|
private val basePreferences: BasePreferences,
|
||||||
|
private val sourcePreferences: SourcePreferences,
|
||||||
|
private val extensionManager: ExtensionManager,
|
||||||
|
) {
|
||||||
|
fun await(sourceId: Long?): Boolean {
|
||||||
|
if (basePreferences.incognitoMode().get()) return true
|
||||||
|
if (sourceId == null) return false
|
||||||
|
val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false
|
||||||
|
|
||||||
|
return extensionPackage in sourcePreferences.incognitoExtensions().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(sourceId: Long?): Flow<Boolean> {
|
||||||
|
if (sourceId == null) return basePreferences.incognitoMode().changes()
|
||||||
|
|
||||||
|
return combine(
|
||||||
|
basePreferences.incognitoMode().changes(),
|
||||||
|
sourcePreferences.incognitoExtensions().changes(),
|
||||||
|
extensionManager.getExtensionPackageAsFlow(sourceId),
|
||||||
|
) { incognito, incognitoExtensions, extensionPackage ->
|
||||||
|
incognito || (extensionPackage in incognitoExtensions)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,19 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
|
import java.util.SortedMap
|
||||||
|
|
||||||
class GetLanguagesWithSources(
|
class GetLanguagesWithSources(
|
||||||
private val repository: SourceRepository,
|
private val repository: SourceRepository,
|
||||||
private val preferences: SourcePreferences,
|
private val preferences: SourcePreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun subscribe(): Flow<Map<String, List<Source>>> {
|
fun subscribe(): Flow<SortedMap<String, List<Source>>> {
|
||||||
return combine(
|
return combine(
|
||||||
preferences.enabledLanguages().changes(),
|
preferences.enabledLanguages().changes(),
|
||||||
preferences.disabledSources().changes(),
|
preferences.disabledSources().changes(),
|
||||||
@ -23,7 +24,8 @@ class GetLanguagesWithSources(
|
|||||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||||
)
|
)
|
||||||
|
|
||||||
sortedSources.groupBy { it.lang }
|
sortedSources
|
||||||
|
.groupBy { it.lang }
|
||||||
.toSortedMap(
|
.toSortedMap(
|
||||||
compareBy<String> { it !in enabledLanguage }.then(LocaleHelper.comparator),
|
compareBy<String> { it !in enabledLanguage }.then(LocaleHelper.comparator),
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
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.common.util.lang.compareToWithCollator
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import java.text.Collator
|
import tachiyomi.domain.source.repository.SourceRepository
|
||||||
|
import tachiyomi.source.local.isLocal
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class GetSourcesWithFavoriteCount(
|
class GetSourcesWithFavoriteCount(
|
||||||
private val repository: SourceRepository,
|
private val repository: SourceRepository,
|
||||||
@ -20,7 +20,9 @@ class GetSourcesWithFavoriteCount(
|
|||||||
preferences.migrationSortingMode().changes(),
|
preferences.migrationSortingMode().changes(),
|
||||||
repository.getSourcesWithFavoriteCount(),
|
repository.getSourcesWithFavoriteCount(),
|
||||||
) { direction, mode, list ->
|
) { direction, mode, list ->
|
||||||
list.sortedWith(sortFn(direction, mode))
|
list
|
||||||
|
.filterNot { it.first.isLocal() }
|
||||||
|
.sortedWith(sortFn(direction, mode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,23 +30,19 @@ class GetSourcesWithFavoriteCount(
|
|||||||
direction: SetMigrateSorting.Direction,
|
direction: SetMigrateSorting.Direction,
|
||||||
sorting: SetMigrateSorting.Mode,
|
sorting: SetMigrateSorting.Mode,
|
||||||
): java.util.Comparator<Pair<Source, Long>> {
|
): java.util.Comparator<Pair<Source, Long>> {
|
||||||
val locale = Locale.getDefault()
|
|
||||||
val collator = Collator.getInstance(locale).apply {
|
|
||||||
strength = Collator.PRIMARY
|
|
||||||
}
|
|
||||||
val sortFn: (Pair<Source, Long>, Pair<Source, Long>) -> Int = { a, b ->
|
val sortFn: (Pair<Source, Long>, Pair<Source, Long>) -> Int = { a, b ->
|
||||||
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 -> collator.compare(a.first.name.lowercase(locale), b.first.name.lowercase(locale))
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
|
class ToggleIncognito(
|
||||||
|
private val preferences: SourcePreferences,
|
||||||
|
) {
|
||||||
|
fun await(extensions: String, enable: Boolean) {
|
||||||
|
preferences.incognitoExtensions().getAndSet {
|
||||||
|
if (enable) it.plus(extensions) else it.minus(extensions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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(
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package eu.kanade.domain.source.model
|
|
||||||
|
|
||||||
import androidx.paging.PagingSource
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
|
|
||||||
typealias SourcePagingSourceType = PagingSource<Long, SManga>
|
|
@ -2,33 +2,57 @@ 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.PreferenceStore
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.preference.getEnum
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.getEnum
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
|
|
||||||
class SourcePreferences(
|
class SourcePreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun sourceDisplayMode() = preferenceStore.getObject("pref_display_mode_catalogue", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
|
fun sourceDisplayMode() = preferenceStore.getObject(
|
||||||
|
"pref_display_mode_catalogue",
|
||||||
|
LibraryDisplayMode.default,
|
||||||
|
LibraryDisplayMode.Serializer::serialize,
|
||||||
|
LibraryDisplayMode.Serializer::deserialize,
|
||||||
|
)
|
||||||
|
|
||||||
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
||||||
|
|
||||||
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
|
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
|
||||||
|
|
||||||
|
fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet())
|
||||||
|
|
||||||
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
|
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
|
||||||
|
|
||||||
fun lastUsedSource() = preferenceStore.getLong("last_catalogue_source", -1)
|
fun lastUsedSource() = preferenceStore.getLong(
|
||||||
|
Preference.appStateKey("last_catalogue_source"),
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
|
||||||
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true)
|
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true)
|
||||||
|
|
||||||
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
||||||
|
|
||||||
fun migrationSortingDirection() = preferenceStore.getEnum("pref_migration_direction", SetMigrateSorting.Direction.ASCENDING)
|
fun migrationSortingDirection() = preferenceStore.getEnum(
|
||||||
|
"pref_migration_direction",
|
||||||
|
SetMigrateSorting.Direction.ASCENDING,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)
|
||||||
|
|
||||||
|
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
|
||||||
|
|
||||||
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
||||||
|
|
||||||
fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet())
|
fun trustedExtensions() = preferenceStore.getStringSet(
|
||||||
|
Preference.appStateKey("trusted_extensions"),
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
fun searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false)
|
fun globalSearchFilterState() = preferenceStore.getBoolean(
|
||||||
|
Preference.appStateKey("has_filters_toggle_state"),
|
||||||
|
false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
107
app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package eu.kanade.domain.track.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Track
|
||||||
|
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
|
import tachiyomi.domain.history.interactor.GetHistory
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
|
class AddTracks(
|
||||||
|
private val insertTrack: InsertTrack,
|
||||||
|
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
||||||
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
private val trackerManager: TrackerManager,
|
||||||
|
) {
|
||||||
|
|
||||||
|
// TODO: update all trackers based on common data
|
||||||
|
suspend fun bind(tracker: Tracker, item: Track, mangaId: Long) = withNonCancellableContext {
|
||||||
|
withIOContext {
|
||||||
|
val allChapters = getChaptersByMangaId.await(mangaId)
|
||||||
|
val hasReadChapters = allChapters.any { it.read }
|
||||||
|
tracker.bind(item, hasReadChapters)
|
||||||
|
|
||||||
|
var track = item.toDomainTrack(idRequired = false) ?: return@withIOContext
|
||||||
|
|
||||||
|
insertTrack.await(track)
|
||||||
|
|
||||||
|
// TODO: merge into [SyncChapterProgressWithTrack]?
|
||||||
|
// Update chapter progress if newer chapters marked read locally
|
||||||
|
if (hasReadChapters) {
|
||||||
|
val latestLocalReadChapterNumber = allChapters
|
||||||
|
.sortedBy { it.chapterNumber }
|
||||||
|
.takeWhile { it.read }
|
||||||
|
.lastOrNull()
|
||||||
|
?.chapterNumber ?: -1.0
|
||||||
|
|
||||||
|
if (latestLocalReadChapterNumber > track.lastChapterRead) {
|
||||||
|
track = track.copy(
|
||||||
|
lastChapterRead = latestLocalReadChapterNumber,
|
||||||
|
)
|
||||||
|
tracker.setRemoteLastChapterRead(track.toDbTrack(), latestLocalReadChapterNumber.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track.startDate <= 0) {
|
||||||
|
val firstReadChapterDate = Injekt.get<GetHistory>().await(mangaId)
|
||||||
|
.sortedBy { it.readAt }
|
||||||
|
.firstOrNull()
|
||||||
|
?.readAt
|
||||||
|
|
||||||
|
firstReadChapterDate?.let {
|
||||||
|
val startDate = firstReadChapterDate.time.convertEpochMillisZone(
|
||||||
|
ZoneOffset.systemDefault(),
|
||||||
|
ZoneOffset.UTC,
|
||||||
|
)
|
||||||
|
track = track.copy(
|
||||||
|
startDate = startDate,
|
||||||
|
)
|
||||||
|
tracker.setRemoteStartDate(track.toDbTrack(), startDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncChapterProgressWithTrack.await(mangaId, track, tracker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
|
||||||
|
withIOContext {
|
||||||
|
trackerManager.loggedInTrackers()
|
||||||
|
.filterIsInstance<EnhancedTracker>()
|
||||||
|
.filter { it.accept(source) }
|
||||||
|
.forEach { service ->
|
||||||
|
try {
|
||||||
|
service.match(manga)?.let { track ->
|
||||||
|
track.manga_id = manga.id
|
||||||
|
(service as Tracker).bind(track)
|
||||||
|
insertTrack.await(track.toDomainTrack(idRequired = false)!!)
|
||||||
|
|
||||||
|
syncChapterProgressWithTrack.await(
|
||||||
|
manga.id,
|
||||||
|
track.toDomainTrack(idRequired = false)!!,
|
||||||
|
service,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(
|
||||||
|
LogPriority.WARN,
|
||||||
|
e,
|
||||||
|
) { "Could not match manga: ${manga.title} with service $service" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package eu.kanade.domain.track.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.supervisorScope
|
||||||
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
|
||||||
|
class RefreshTracks(
|
||||||
|
private val getTracks: GetTracks,
|
||||||
|
private val trackerManager: TrackerManager,
|
||||||
|
private val insertTrack: InsertTrack,
|
||||||
|
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches updated tracking data from all logged in trackers.
|
||||||
|
*
|
||||||
|
* @return Failed updates.
|
||||||
|
*/
|
||||||
|
suspend fun await(mangaId: Long): List<Pair<Tracker?, Throwable>> {
|
||||||
|
return supervisorScope {
|
||||||
|
return@supervisorScope getTracks.await(mangaId)
|
||||||
|
.map { it to trackerManager.get(it.trackerId) }
|
||||||
|
.filter { (_, service) -> service?.isLoggedIn == true }
|
||||||
|
.map { (track, service) ->
|
||||||
|
async {
|
||||||
|
return@async try {
|
||||||
|
val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!!
|
||||||
|
insertTrack.await(updatedTrack)
|
||||||
|
syncChapterProgressWithTrack.await(mangaId, updatedTrack, service)
|
||||||
|
null
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
service to e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.awaitAll()
|
||||||
|
.filterNotNull()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package eu.kanade.domain.track.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
import eu.kanade.tachiyomi.data.track.EnhancedTracker
|
||||||
|
import eu.kanade.tachiyomi.data.track.Tracker
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
|
import tachiyomi.domain.chapter.model.toChapterUpdate
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
import tachiyomi.domain.track.model.Track
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class SyncChapterProgressWithTrack(
|
||||||
|
private val updateChapter: UpdateChapter,
|
||||||
|
private val insertTrack: InsertTrack,
|
||||||
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
remoteTrack: Track,
|
||||||
|
tracker: Tracker,
|
||||||
|
) {
|
||||||
|
if (tracker !is EnhancedTracker) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val sortedChapters = getChaptersByMangaId.await(mangaId)
|
||||||
|
.sortedBy { it.chapterNumber }
|
||||||
|
.filter { it.isRecognizedNumber }
|
||||||
|
|
||||||
|
val chapterUpdates = sortedChapters
|
||||||
|
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
|
||||||
|
.map { it.copy(read = true).toChapterUpdate() }
|
||||||
|
|
||||||
|
// only take into account continuous reading
|
||||||
|
val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapterNumber ?: 0F
|
||||||
|
val lastRead = max(remoteTrack.lastChapterRead, localLastRead.toDouble())
|
||||||
|
val updatedTrack = remoteTrack.copy(lastChapterRead = lastRead)
|
||||||
|
|
||||||
|
try {
|
||||||
|
tracker.update(updatedTrack.toDbTrack())
|
||||||
|
updateChapter.awaitAll(chapterUpdates)
|
||||||
|
insertTrack.await(updatedTrack)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
logcat(LogPriority.WARN, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package eu.kanade.domain.track.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
import eu.kanade.domain.track.model.toDomainTrack
|
||||||
|
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
||||||
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
|
import tachiyomi.core.common.util.system.logcat
|
||||||
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
|
||||||
|
class TrackChapter(
|
||||||
|
private val getTracks: GetTracks,
|
||||||
|
private val trackerManager: TrackerManager,
|
||||||
|
private val insertTrack: InsertTrack,
|
||||||
|
private val delayedTrackingStore: DelayedTrackingStore,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) {
|
||||||
|
withNonCancellableContext {
|
||||||
|
val tracks = getTracks.await(mangaId)
|
||||||
|
if (tracks.isEmpty()) return@withNonCancellableContext
|
||||||
|
|
||||||
|
tracks.mapNotNull { track ->
|
||||||
|
val service = trackerManager.get(track.trackerId)
|
||||||
|
if (service == null || !service.isLoggedIn || chapterNumber <= track.lastChapterRead) {
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
|
||||||
|
async {
|
||||||
|
runCatching {
|
||||||
|
try {
|
||||||
|
val updatedTrack = service.refresh(track.toDbTrack())
|
||||||
|
.toDomainTrack(idRequired = true)!!
|
||||||
|
.copy(lastChapterRead = chapterNumber)
|
||||||
|
service.update(updatedTrack.toDbTrack(), true)
|
||||||
|
insertTrack.await(updatedTrack)
|
||||||
|
delayedTrackingStore.remove(track.id)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
delayedTrackingStore.add(track.id, chapterNumber)
|
||||||
|
if (setupJobOnFailure) {
|
||||||
|
DelayedTrackingUpdateJob.setupTask(context)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.awaitAll()
|
||||||
|
.mapNotNull { it.exceptionOrNull() }
|
||||||
|
.forEach { logcat(LogPriority.WARN, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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),
|
||||||
|
}
|
@ -10,39 +10,42 @@ fun Track.copyPersonalFrom(other: Track): Track {
|
|||||||
status = other.status,
|
status = other.status,
|
||||||
startDate = other.startDate,
|
startDate = other.startDate,
|
||||||
finishDate = other.finishDate,
|
finishDate = other.finishDate,
|
||||||
|
private = other.private,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also {
|
fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
|
||||||
it.id = id
|
it.id = id
|
||||||
it.manga_id = mangaId
|
it.manga_id = mangaId
|
||||||
it.media_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
|
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
|
||||||
|
it.private = private
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
syncId = sync_id.toLong(),
|
trackerId = tracker_id,
|
||||||
remoteId = media_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,
|
||||||
score = score,
|
score = score,
|
||||||
remoteUrl = tracking_url,
|
remoteUrl = tracking_url,
|
||||||
startDate = started_reading_date,
|
startDate = started_reading_date,
|
||||||
finishDate = finished_reading_date,
|
finishDate = finished_reading_date,
|
||||||
|
private = private,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -7,31 +7,32 @@ import androidx.work.CoroutineWorker
|
|||||||
import androidx.work.ExistingWorkPolicy
|
import androidx.work.ExistingWorkPolicy
|
||||||
import androidx.work.NetworkType
|
import androidx.work.NetworkType
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import androidx.work.WorkManager
|
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
import eu.kanade.domain.track.interactor.InsertTrack
|
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
|
||||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
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 uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters) :
|
class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) :
|
||||||
CoroutineWorker(context, workerParams) {
|
CoroutineWorker(context, workerParams) {
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val getTracks = Injekt.get<GetTracks>()
|
if (runAttemptCount > 3) {
|
||||||
val insertTrack = Injekt.get<InsertTrack>()
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
val getTracks = Injekt.get<GetTracks>()
|
||||||
|
val trackChapter = Injekt.get<TrackChapter>()
|
||||||
|
|
||||||
val trackManager = Injekt.get<TrackManager>()
|
|
||||||
val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
|
val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
|
||||||
|
|
||||||
val results = withIOContext {
|
withIOContext {
|
||||||
delayedTrackingStore.getItems()
|
delayedTrackingStore.getItems()
|
||||||
.mapNotNull {
|
.mapNotNull {
|
||||||
val track = getTracks.awaitOne(it.trackId)
|
val track = getTracks.awaitOne(it.trackId)
|
||||||
@ -40,42 +41,32 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
|
|||||||
}
|
}
|
||||||
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||||
}
|
}
|
||||||
.mapNotNull { track ->
|
.forEach { track ->
|
||||||
try {
|
logcat(LogPriority.DEBUG) {
|
||||||
val service = trackManager.getService(track.syncId)
|
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
|
||||||
if (service != null && service.isLogged) {
|
|
||||||
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" }
|
|
||||||
service.update(track.toDbTrack(), true)
|
|
||||||
insertTrack.await(track)
|
|
||||||
}
|
|
||||||
delayedTrackingStore.remove(track.id)
|
|
||||||
null
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.ERROR, e)
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (results.isNotEmpty()) Result.failure() else Result.success()
|
return if (delayedTrackingStore.getItems().isEmpty()) Result.success() else Result.retry()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "DelayedTrackingUpdate"
|
private const val TAG = "DelayedTrackingUpdate"
|
||||||
|
|
||||||
fun setupTask(context: Context) {
|
fun setupTask(context: Context) {
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints(
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
requiredNetworkType = NetworkType.CONNECTED,
|
||||||
.build()
|
)
|
||||||
|
|
||||||
val request = OneTimeWorkRequestBuilder<DelayedTrackingUpdateJob>()
|
val request = OneTimeWorkRequestBuilder<DelayedTrackingUpdateJob>()
|
||||||
.setConstraints(constraints)
|
.setConstraints(constraints)
|
||||||
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 20, TimeUnit.SECONDS)
|
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES)
|
||||||
.addTag(TAG)
|
.addTag(TAG)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(context)
|
context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
|
||||||
.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,45 @@
|
|||||||
package eu.kanade.domain.track.service
|
package eu.kanade.domain.track.service
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.domain.track.model.AutoTrackState
|
||||||
|
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.PreferenceStore
|
import tachiyomi.core.common.preference.Preference
|
||||||
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
|
||||||
class TrackPreferences(
|
class TrackPreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun trackUsername(sync: TrackService) = preferenceStore.getString(trackUsername(sync.id), "")
|
fun trackUsername(tracker: Tracker) = preferenceStore.getString(
|
||||||
|
Preference.privateKey("pref_mangasync_username_${tracker.id}"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
fun trackPassword(sync: TrackService) = preferenceStore.getString(trackPassword(sync.id), "")
|
fun trackPassword(tracker: Tracker) = preferenceStore.getString(
|
||||||
|
Preference.privateKey("pref_mangasync_password_${tracker.id}"),
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
|
fun trackAuthExpired(tracker: Tracker) = preferenceStore.getBoolean(
|
||||||
trackUsername(sync).set(username)
|
Preference.privateKey("pref_tracker_auth_expired_${tracker.id}"),
|
||||||
trackPassword(sync).set(password)
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun setCredentials(tracker: Tracker, username: String, password: String) {
|
||||||
|
trackUsername(tracker).set(username)
|
||||||
|
trackPassword(tracker).set(password)
|
||||||
|
trackAuthExpired(tracker).set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun trackToken(sync: TrackService) = preferenceStore.getString(trackToken(sync.id), "")
|
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
companion object {
|
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum(
|
||||||
fun trackUsername(syncId: Long) = "pref_mangasync_username_$syncId"
|
"pref_auto_update_manga_on_mark_read",
|
||||||
|
AutoTrackState.ALWAYS,
|
||||||
private fun trackPassword(syncId: Long) = "pref_mangasync_password_$syncId"
|
)
|
||||||
|
|
||||||
private fun trackToken(syncId: Long) = "track_token_$syncId"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +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
|
||||||
import tachiyomi.domain.track.model.Track
|
|
||||||
|
|
||||||
class DelayedTrackingStore(context: Context) {
|
class DelayedTrackingStore(context: Context) {
|
||||||
|
|
||||||
@ -13,13 +12,12 @@ class DelayedTrackingStore(context: Context) {
|
|||||||
*/
|
*/
|
||||||
private val preferences = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
private val preferences = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
fun addItem(track: Track) {
|
fun add(trackId: Long, lastChapterRead: Double) {
|
||||||
val trackId = track.id.toString()
|
val previousLastChapterRead = preferences.getFloat(trackId.toString(), 0f)
|
||||||
val lastChapterRead = preferences.getFloat(trackId, 0f)
|
if (lastChapterRead > previousLastChapterRead) {
|
||||||
if (track.lastChapterRead > lastChapterRead) {
|
logcat(LogPriority.DEBUG) { "Queuing track item: $trackId, last chapter read: $lastChapterRead" }
|
||||||
logcat(LogPriority.DEBUG) { "Queuing track item: $trackId, last chapter read: ${track.lastChapterRead}" }
|
|
||||||
preferences.edit {
|
preferences.edit {
|
||||||
putFloat(trackId, track.lastChapterRead.toFloat())
|
putFloat(trackId.toString(), lastChapterRead.toFloat())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,43 @@
|
|||||||
package eu.kanade.domain.ui
|
package eu.kanade.domain.ui
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import eu.kanade.domain.ui.model.AppTheme
|
import eu.kanade.domain.ui.model.AppTheme
|
||||||
import eu.kanade.domain.ui.model.TabletUiMode
|
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(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun themeMode() = preferenceStore.getEnum(
|
fun themeMode() = preferenceStore.getEnum("pref_theme_mode_key", ThemeMode.SYSTEM)
|
||||||
"pref_theme_mode_key",
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ThemeMode.SYSTEM } else { ThemeMode.LIGHT },
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
fun relativeTime() = preferenceStore.getInt("relative_time", 7)
|
fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true)
|
||||||
|
|
||||||
fun dateFormat() = preferenceStore.getString("app_date_format", "")
|
fun dateFormat() = preferenceStore.getString("app_date_format", "")
|
||||||
|
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
package eu.kanade.domain.ui.model
|
package eu.kanade.domain.ui.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.R
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
enum class AppTheme(val titleResId: Int?) {
|
enum class AppTheme(val titleRes: StringResource?) {
|
||||||
DEFAULT(R.string.label_default),
|
DEFAULT(MR.strings.label_default),
|
||||||
MONET(R.string.theme_monet),
|
MONET(MR.strings.theme_monet),
|
||||||
GREEN_APPLE(R.string.theme_greenapple),
|
GREEN_APPLE(MR.strings.theme_greenapple),
|
||||||
LAVENDER(R.string.theme_lavender),
|
LAVENDER(MR.strings.theme_lavender),
|
||||||
MIDNIGHT_DUSK(R.string.theme_midnightdusk),
|
MIDNIGHT_DUSK(MR.strings.theme_midnightdusk),
|
||||||
STRAWBERRY_DAIQUIRI(R.string.theme_strawberrydaiquiri),
|
|
||||||
TAKO(R.string.theme_tako),
|
// TODO: re-enable for preview
|
||||||
TEALTURQUOISE(R.string.theme_tealturquoise),
|
NORD(MR.strings.theme_nord.takeUnless { isReleaseBuildType }),
|
||||||
TIDAL_WAVE(R.string.theme_tidalwave),
|
STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri),
|
||||||
YINYANG(R.string.theme_yinyang),
|
TAKO(MR.strings.theme_tako),
|
||||||
YOTSUBA(R.string.theme_yotsuba),
|
TEALTURQUOISE(MR.strings.theme_tealturquoise),
|
||||||
|
TIDAL_WAVE(MR.strings.theme_tidalwave),
|
||||||
|
YINYANG(MR.strings.theme_yinyang),
|
||||||
|
YOTSUBA(MR.strings.theme_yotsuba),
|
||||||
|
MONOCHROME(MR.strings.theme_monochrome),
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
DARK_BLUE(null),
|
DARK_BLUE(null),
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package eu.kanade.domain.ui.model
|
package eu.kanade.domain.ui.model
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.R
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
enum class TabletUiMode(val titleResId: Int) {
|
enum class TabletUiMode(val titleRes: StringResource) {
|
||||||
AUTOMATIC(R.string.automatic_background),
|
AUTOMATIC(MR.strings.automatic_background),
|
||||||
ALWAYS(R.string.lock_always),
|
ALWAYS(MR.strings.lock_always),
|
||||||
LANDSCAPE(R.string.landscape),
|
LANDSCAPE(MR.strings.landscape),
|
||||||
NEVER(R.string.lock_never),
|
NEVER(MR.strings.lock_never),
|
||||||
}
|
}
|
||||||
|