Compare commits
883
Commits
1.8.8.2
...
1.8.9.2-gms
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b5a2a9a33 | ||
|
|
7719312355 | ||
|
|
4594ac592c | ||
|
|
eee0f2dded | ||
|
|
b4080302c3 | ||
|
|
337e210b5b | ||
|
|
48f1b0a958 | ||
|
|
9abeba6b15 | ||
|
|
a05b9174d4 | ||
|
|
aa8adfb6f7 | ||
|
|
1034fcd7b0 | ||
|
|
69a26aeedb | ||
|
|
dd607b41e9 | ||
|
|
fd45319715 | ||
|
|
c34ba56896 | ||
|
|
6e00a4133c | ||
|
|
75bec4314e | ||
|
|
b767c041b9 | ||
|
|
4e916731b3 | ||
|
|
6c24407fb7 | ||
|
|
9924b6ec0b | ||
|
|
4d09504abc | ||
|
|
f901bd2336 | ||
|
|
187c35fe88 | ||
|
|
cc94bed772 | ||
|
|
8c9cdf46db | ||
|
|
09dd200655 | ||
|
|
38294e8cfc | ||
|
|
87a42cf53d | ||
|
|
2adf2c15fa | ||
|
|
99ac2a287e | ||
|
|
371ccab596 | ||
|
|
590c505f33 | ||
|
|
01c2f3ea69 | ||
|
|
4c4eec118d | ||
|
|
310be050d8 | ||
|
|
181e0301aa | ||
|
|
a86ca2e82c | ||
|
|
85c911cf35 | ||
|
|
db57e2dde6 | ||
|
|
c163ad42a6 | ||
|
|
a2af3a4652 | ||
|
|
59a9448d70 | ||
|
|
22b87de29d | ||
|
|
d98292da1d | ||
|
|
5422c04c53 | ||
|
|
0a5783de89 | ||
|
|
717e2191b0 | ||
|
|
eedc3ba5eb | ||
|
|
30ee1816df | ||
|
|
940047de11 | ||
|
|
c0e2041efe | ||
|
|
8f9d2adcfe | ||
|
|
1bfda83665 | ||
|
|
39b896e1b9 | ||
|
|
cfe7ee7f9c | ||
|
|
2a95d6707a | ||
|
|
f4fe5f1783 | ||
|
|
10040b2923 | ||
|
|
42c56616fb | ||
|
|
f8c926e1cd | ||
|
|
2adf620059 | ||
|
|
4bdc182520 | ||
|
|
bc4c7f62a0 | ||
|
|
b2d0c59913 | ||
|
|
ceb5424772 | ||
|
|
5a5a5da2d9 | ||
|
|
0ff9b03596 | ||
|
|
acbe4eafa9 | ||
|
|
0a5600d949 | ||
|
|
63a83257fe | ||
|
|
d115e28403 | ||
|
|
3d1ae0acfc | ||
|
|
a3a01065c0 | ||
|
|
ae33d692bf | ||
|
|
39f55d7db1 | ||
|
|
a86f8d6096 | ||
|
|
b28fc3ffc4 | ||
|
|
5d4f544b94 | ||
|
|
b0f0686821 | ||
|
|
b7c8afe7d2 | ||
|
|
02855b68e6 | ||
|
|
cf8097babc | ||
|
|
2d5a2022b4 | ||
|
|
2fac22970e | ||
|
|
8c10836e6e | ||
|
|
69bba560f0 | ||
|
|
a9d5c15bd9 | ||
|
|
1d0812ddaf | ||
|
|
02bdc9c1a3 | ||
|
|
6a08460e8e | ||
|
|
091ad5cf0f | ||
|
|
f911e4629c | ||
|
|
0265ddc6e1 | ||
|
|
ead2300d90 | ||
|
|
b3a8547527 | ||
|
|
37a5dbd20d | ||
|
|
c20dc66db3 | ||
|
|
31c32123c9 | ||
|
|
14faa38de3 | ||
|
|
4827b7e077 | ||
|
|
2d9a17bd25 | ||
|
|
ce3ad45c09 | ||
|
|
44fcc21d9c | ||
|
|
5c1a6808d7 | ||
|
|
d461b1b8b6 | ||
|
|
d74b0448d5 | ||
|
|
ed2a37c615 | ||
|
|
34c51d3115 | ||
|
|
f85d36d14d | ||
|
|
9818f227d7 | ||
|
|
01353e05cc | ||
|
|
02eaedd6a0 | ||
|
|
c71c872aa2 | ||
|
|
d79dc0c630 | ||
|
|
46cf695bde | ||
|
|
c913a7eb9e | ||
|
|
887c46c87f | ||
|
|
e4d3501f93 | ||
|
|
add98bb630 | ||
|
|
05651584dc | ||
|
|
37171840d3 | ||
|
|
4ee8fbedbf | ||
|
|
274e6a0666 | ||
|
|
b26686823e | ||
|
|
64354ea511 | ||
|
|
07cce5bbad | ||
|
|
bbab9522df | ||
|
|
1a6d839ca0 | ||
|
|
0d666a633f | ||
|
|
b24679adbd | ||
|
|
90b4d643d4 | ||
|
|
469f2871f7 | ||
|
|
c7766de766 | ||
|
|
314de6a6a7 | ||
|
|
9c148dd58d | ||
|
|
1ae469facc | ||
|
|
eada6641e4 | ||
|
|
2eb50803b5 | ||
|
|
29e3e876c7 | ||
|
|
1c38dbaa0b | ||
|
|
31dcd349df | ||
|
|
ee504e3361 | ||
|
|
11426e1c4c | ||
|
|
13346f1124 | ||
|
|
f38f3989e7 | ||
|
|
5c33ca1c9a | ||
|
|
3599ffb805 | ||
|
|
344cd1305c | ||
|
|
84fe0b9cb7 | ||
|
|
e5963208b1 | ||
|
|
8bc84b404d | ||
|
|
80d75661c1 | ||
|
|
1c74a422bc | ||
|
|
f06c2d6805 | ||
|
|
fdbe06e8c5 | ||
|
|
15a9c4ee45 | ||
|
|
38c90f35f2 | ||
|
|
bf3ee4cce2 | ||
|
|
9553491040 | ||
|
|
0253d92c7f | ||
|
|
e2ac340b6f | ||
|
|
44b60185cc | ||
|
|
9b3ffd1f2c | ||
|
|
935c8e7d15 | ||
|
|
8f5094efef | ||
|
|
91accb6655 | ||
|
|
4f23cf5d8d | ||
|
|
39f76246c8 | ||
|
|
8ac2b027ec | ||
|
|
919d22ebf1 | ||
|
|
14a28c681a | ||
|
|
388bccc468 | ||
|
|
639d94795c | ||
|
|
51e2f44868 | ||
|
|
92b9b9879c | ||
|
|
c867a74aea | ||
|
|
e9a1bf22b1 | ||
|
|
46271fb0d2 | ||
|
|
7438b17366 | ||
|
|
8306640ed6 | ||
|
|
39b7623688 | ||
|
|
41b46035e4 | ||
|
|
10045494b8 | ||
|
|
ea67a022d8 | ||
|
|
203c89e3a8 | ||
|
|
1c88be6f82 | ||
|
|
2a19bcd73b | ||
|
|
9f29594ad6 | ||
|
|
9677e6ac80 | ||
|
|
1590ca4b80 | ||
|
|
effe7530ed | ||
|
|
19f270e320 | ||
|
|
25d876f8b9 | ||
|
|
179e470154 | ||
|
|
5cf122c30c | ||
|
|
36895de83b | ||
|
|
fed5285a31 | ||
|
|
b1fbefc09e | ||
|
|
2fd9982c37 | ||
|
|
89a108574a | ||
|
|
28ce6ee13e | ||
|
|
4441ea2c5d | ||
|
|
02489893ec | ||
|
|
62c3e9290e | ||
|
|
3f53710c5b | ||
|
|
389f27dfa1 | ||
|
|
e2d03eb928 | ||
|
|
37ec49ed69 | ||
|
|
4694245b51 | ||
|
|
60b77145a9 | ||
|
|
28a6f8cd2b | ||
|
|
02a3c1c54b | ||
|
|
09df7cc39b | ||
|
|
a4ce60c849 | ||
|
|
d74f18012b | ||
|
|
3609379284 | ||
|
|
19f9105640 | ||
|
|
24664d4e83 | ||
|
|
e16b5b0437 | ||
|
|
94747da3d0 | ||
|
|
df8c70d8e6 | ||
|
|
7fd3361608 | ||
|
|
d9522e4e89 | ||
|
|
a8c96fa7df | ||
|
|
5570933be4 | ||
|
|
fb6cb8d3fb | ||
|
|
4ca7b6b11c | ||
|
|
5c377d346e | ||
|
|
1dd0eeb2ed | ||
|
|
3f6abd6380 | ||
|
|
407c55dbdc | ||
|
|
74afa6c764 | ||
|
|
60ccd988cb | ||
|
|
505ba6e4e3 | ||
|
|
d4d6fe56a9 | ||
|
|
740fff2178 | ||
|
|
54a2ea64bc | ||
|
|
200882e13c | ||
|
|
dac28b9b6a | ||
|
|
7a8ae183da | ||
|
|
c163e24434 | ||
|
|
eed30084bc | ||
|
|
137a51dad0 | ||
|
|
b71d326620 | ||
|
|
371622770b | ||
|
|
2b6bcf6698 | ||
|
|
bc33ea84f2 | ||
|
|
3d7210ec08 | ||
|
|
e7bed5f5eb | ||
|
|
a3237a024e | ||
|
|
662fc6859b | ||
|
|
5bb61959bd | ||
|
|
8ae75101ef | ||
|
|
95fcab0980 | ||
|
|
d2b141e218 | ||
|
|
73eb91f4b4 | ||
|
|
61c51f8882 | ||
|
|
cb9f36dd52 | ||
|
|
80b0203af2 | ||
|
|
66155eed1c | ||
|
|
144e8a2951 | ||
|
|
7c1f521960 | ||
|
|
dbe528269d | ||
|
|
9b843e84b2 | ||
|
|
c9ef0e5406 | ||
|
|
1a49b3d00b | ||
|
|
33502c88db | ||
|
|
6554735c63 | ||
|
|
2360e6376e | ||
|
|
b1e186a5f1 | ||
|
|
ffd2b5a342 | ||
|
|
d5a16f50c5 | ||
|
|
5b03e67108 | ||
|
|
eac5b2641b | ||
|
|
e46008df55 | ||
|
|
07b1f39796 | ||
|
|
64d3849b62 | ||
|
|
9d766f47c5 | ||
|
|
64841e6e3b | ||
|
|
571fbabf4f | ||
|
|
12c3ad6d5f | ||
|
|
7c987ec868 | ||
|
|
27686e952a | ||
|
|
de06c24924 | ||
|
|
4e1b4ddf28 | ||
|
|
ca21b5e713 | ||
|
|
5a6a01f813 | ||
|
|
16c6353459 | ||
|
|
d1623d411a | ||
|
|
4e4b072c16 | ||
|
|
f2601fb672 | ||
|
|
125c467c1b | ||
|
|
0b95c340d6 | ||
|
|
79e0b33aad | ||
|
|
f1f7b8dc88 | ||
|
|
e97e539f92 | ||
|
|
4b562f98f0 | ||
|
|
bd06d76656 | ||
|
|
38ddca2342 | ||
|
|
1119798ce8 | ||
|
|
1eeab2f03d | ||
|
|
bfbe6b8e6a | ||
|
|
4905c955bc | ||
|
|
4cf698a10a | ||
|
|
4b66f58fd3 | ||
|
|
325909401c | ||
|
|
652d6a2f28 | ||
|
|
7672a82260 | ||
|
|
0f37d17bf3 | ||
|
|
c923d74fec | ||
|
|
2ba8640996 | ||
|
|
0b1a0cade8 | ||
|
|
78aab4f6c7 | ||
|
|
77502e1a7c | ||
|
|
b90f221ede | ||
|
|
ee68b421d6 | ||
|
|
88be232e70 | ||
|
|
da9de7a823 | ||
|
|
f08e124df6 | ||
|
|
ce888a0982 | ||
|
|
f59789a11e | ||
|
|
c8a3db8a3e | ||
|
|
23366c8ea1 | ||
|
|
6e65afa7e2 | ||
|
|
29b81aa90e | ||
|
|
d85090787f | ||
|
|
0eb3cc9073 | ||
|
|
dfd9af8ee3 | ||
|
|
1120c23709 | ||
|
|
975421044c | ||
|
|
802b63ed5a | ||
|
|
d32a0d6b7f | ||
|
|
0c5a1a6533 | ||
|
|
61796f4cd3 | ||
|
|
80726a7ff0 | ||
|
|
a2fdcc57f8 | ||
|
|
bffb32eafd | ||
|
|
2c452d236c | ||
|
|
e7e036f7c6 | ||
|
|
fbb702cacd | ||
|
|
598438a5f9 | ||
|
|
24a1c94f04 | ||
|
|
e4e053a49b | ||
|
|
8c0e217117 | ||
|
|
174e899ead | ||
|
|
84cc915d95 | ||
|
|
7668f568f0 | ||
|
|
35a7cc2067 | ||
|
|
efa0823293 | ||
|
|
1473f6326d | ||
|
|
861c05db44 | ||
|
|
09b92cc709 | ||
|
|
a2d9113be2 | ||
|
|
a64c7d9f22 | ||
|
|
97090a4dc6 | ||
|
|
cf2c5b7e82 | ||
|
|
6ee5b636c2 | ||
|
|
de9fa5be5a | ||
|
|
ffc3693674 | ||
|
|
a3abd224e8 | ||
|
|
ac680afad3 | ||
|
|
52c21319d6 | ||
|
|
7b38171a9d | ||
|
|
c9ef60db67 | ||
|
|
aff75c2dec | ||
|
|
63fcca3c7d | ||
|
|
2e3a148c7a | ||
|
|
a7b239f0b9 | ||
|
|
227cbd6fba | ||
|
|
8d19dffa01 | ||
|
|
f3e1400d4c | ||
|
|
ef891ea23d | ||
|
|
043a1d8fe7 | ||
|
|
e428695052 | ||
|
|
459dd50e91 | ||
|
|
4c286a279f | ||
|
|
88c394480e | ||
|
|
cdd5585124 | ||
|
|
b0346b4e9d | ||
|
|
beecd6b109 | ||
|
|
54d7abd880 | ||
|
|
9f77a4ce0a | ||
|
|
754a20ee3c | ||
|
|
b5b096aeae | ||
|
|
d0ecac07aa | ||
|
|
67e6387fa8 | ||
|
|
bc34f25a3c | ||
|
|
afc38c2de8 | ||
|
|
c020af3083 | ||
|
|
3f509f8c11 | ||
|
|
75eb25db18 | ||
|
|
79d173cb0d | ||
|
|
f3eb2c8c17 | ||
|
|
d15685e1ca | ||
|
|
11535f7d7b | ||
|
|
0b526040a3 | ||
|
|
15696f4e6e | ||
|
|
0c560518c8 | ||
|
|
302cba9f61 | ||
|
|
3a33a2425f | ||
|
|
a5df907bbe | ||
|
|
6879a02a63 | ||
|
|
d86d72212f | ||
|
|
de528ec277 | ||
|
|
6f41ddda51 | ||
|
|
ffaf8b9c46 | ||
|
|
aab1c12aed | ||
|
|
4e427f2761 | ||
|
|
2a6247a89d | ||
|
|
62c3e9fd99 | ||
|
|
1690651428 | ||
|
|
ea95b1c525 | ||
|
|
f67c80a3a3 | ||
|
|
6c1ffeacb2 | ||
|
|
753206c2fb | ||
|
|
2ae8265d05 | ||
|
|
7df7a783f6 | ||
|
|
2829d081cc | ||
|
|
bb3a88813b | ||
|
|
2da8cfde01 | ||
|
|
2e23167e94 | ||
|
|
9131f6f8cb | ||
|
|
a77a1fd3e1 | ||
|
|
9dfc1324ad | ||
|
|
c84e86a5a1 | ||
|
|
be867d5d07 | ||
|
|
5457044dd5 | ||
|
|
be2330f1b6 | ||
|
|
eb18e74884 | ||
|
|
fd05d03bca | ||
|
|
69db5bd400 | ||
|
|
a87d720ae3 | ||
|
|
7b81a9aa50 | ||
|
|
b07fd2bc4f | ||
|
|
3c64486c3b | ||
|
|
8cb4045589 | ||
|
|
7edac11d53 | ||
|
|
838f8ac98c | ||
|
|
3a57d43aa4 | ||
|
|
69870803f7 | ||
|
|
eef5e5485a | ||
|
|
ebbe26e5e6 | ||
|
|
0cd83d3e4c | ||
|
|
1c07c61307 | ||
|
|
b602fa1204 | ||
|
|
273bed9ba1 | ||
|
|
028405ecce | ||
|
|
ada4158c8e | ||
|
|
fc99c611a6 | ||
|
|
fce4a2751a | ||
|
|
557818e59f | ||
|
|
a85f3cbe0b | ||
|
|
5d8acd9577 | ||
|
|
e2bf887b94 | ||
|
|
4a0893c573 | ||
|
|
839d074df1 | ||
|
|
9be7705050 | ||
|
|
470a2baa6a | ||
|
|
fb8de517a0 | ||
|
|
635de44299 | ||
|
|
1d4bd9a294 | ||
|
|
a0b48a5cc7 | ||
|
|
eb726ccbf4 | ||
|
|
3f484a49e8 | ||
|
|
09cd08bfaa | ||
|
|
e6312a37c7 | ||
|
|
862b3e5e46 | ||
|
|
0042482107 | ||
|
|
85bc215e00 | ||
|
|
2db5a749a0 | ||
|
|
8c439b1167 | ||
|
|
4eb1a4141f | ||
|
|
4f47febc06 | ||
|
|
c61afc7a04 | ||
|
|
b59966e8a4 | ||
|
|
457cf2e856 | ||
|
|
4a7626c6f6 | ||
|
|
947452e3f1 | ||
|
|
b6de84c369 | ||
|
|
0adc43b243 | ||
|
|
0c9a8624bc | ||
|
|
09e9d3ff5a | ||
|
|
7240ca4eba | ||
|
|
0a2c29a693 | ||
|
|
bba875238c | ||
|
|
c33248b1bf | ||
|
|
24431051ae | ||
|
|
1758e25388 | ||
|
|
a27e8b99d9 | ||
|
|
a06e76e934 | ||
|
|
14609583b5 | ||
|
|
751a89c107 | ||
|
|
619c8f6deb | ||
|
|
c3835b8a65 | ||
|
|
5fd890cf9b | ||
|
|
40c6135214 | ||
|
|
cfa1ce8ab6 | ||
|
|
260dafcd79 | ||
|
|
73b3cd4625 | ||
|
|
d895a07583 | ||
|
|
b84872aca2 | ||
|
|
0ef4bccb7c | ||
|
|
dfc6c6a030 | ||
|
|
b62f1d0c9f | ||
|
|
8abb5d9fb4 | ||
|
|
5f13725173 | ||
|
|
7f8b41f998 | ||
|
|
1319060311 | ||
|
|
d1f914c2fa | ||
|
|
46cd1013fb | ||
|
|
2ed516cf85 | ||
|
|
2ce55b4aef | ||
|
|
df7071a22c | ||
|
|
707529972e | ||
|
|
cf2796ebaa | ||
|
|
9611d81969 | ||
|
|
fb31f627a1 | ||
|
|
4741e4f858 | ||
|
|
b44ddf980b | ||
|
|
47b97243c3 | ||
|
|
b695b06507 | ||
|
|
110fb9fb59 | ||
|
|
fe7c979d78 | ||
|
|
0d10899417 | ||
|
|
ff89afb8c1 | ||
|
|
188248c634 | ||
|
|
0d3c942984 | ||
|
|
2b520be693 | ||
|
|
4b831a2ee1 | ||
|
|
59037d60e2 | ||
|
|
61917285d5 | ||
|
|
453f77dd3e | ||
|
|
b40004d9ba | ||
|
|
f8f2d020eb | ||
|
|
4f09de8a69 | ||
|
|
2d20da70b8 | ||
|
|
cf95e7b034 | ||
|
|
b5709a49f4 | ||
|
|
75a7143442 | ||
|
|
3e47d82713 | ||
|
|
62f9361d25 | ||
|
|
3a97b93b6d | ||
|
|
3b9850b90d | ||
|
|
c0636e93c6 | ||
|
|
5ff1131dee | ||
|
|
8aca589b0f | ||
|
|
10f6bd4bf6 | ||
|
|
dbe84e8bda | ||
|
|
f4de403cdd | ||
|
|
9024afed19 | ||
|
|
d8921377b3 | ||
|
|
b80deafaa8 | ||
|
|
506c63380a | ||
|
|
9e4fc2e7af | ||
|
|
0a8df26549 | ||
|
|
d2bbd1b842 | ||
|
|
5974ce11a1 | ||
|
|
3ec700da56 | ||
|
|
cb7b601f58 | ||
|
|
8e08a4881f | ||
|
|
2fac1c8e4c | ||
|
|
d240fb7e05 | ||
|
|
326d378979 | ||
|
|
71f91bf054 | ||
|
|
40e4d40683 | ||
|
|
c9d439e748 | ||
|
|
0ec90655c0 | ||
|
|
c6949cd09b | ||
|
|
1e47170c3b | ||
|
|
b7d2e4e5da | ||
|
|
28c7a701ca | ||
|
|
52560b6b95 | ||
|
|
1c2c18bff1 | ||
|
|
ed1603eec4 | ||
|
|
2206dd85bc | ||
|
|
62509e1500 | ||
|
|
56cad66aec | ||
|
|
660d917f97 | ||
|
|
0ca5572684 | ||
|
|
cbebdb542d | ||
|
|
84d13fe9c5 | ||
|
|
c6919c39c6 | ||
|
|
92b6069654 | ||
|
|
804dffc81a | ||
|
|
5831761fc0 | ||
|
|
c887379b8b | ||
|
|
82c92203b6 | ||
|
|
881948554e | ||
|
|
b0357201a5 | ||
|
|
7ddeaf6d16 | ||
|
|
942abd7dbb | ||
|
|
9f78b69390 | ||
|
|
6e561ca9a9 | ||
|
|
cec86576c1 | ||
|
|
9eb8e984b6 | ||
|
|
20798b5d25 | ||
|
|
29fafdf216 | ||
|
|
2fbda42e53 | ||
|
|
285feb5f5b | ||
|
|
cfdb8793c6 | ||
|
|
55b3753e66 | ||
|
|
ad04681460 | ||
|
|
2e132f06b7 | ||
|
|
dc74266927 | ||
|
|
e002e15a66 | ||
|
|
e149c7065e | ||
|
|
107a5ce626 | ||
|
|
b6d69f7a02 | ||
|
|
6de6c89439 | ||
|
|
c1ca6dcd5d | ||
|
|
223ebd6acd | ||
|
|
cc8311e116 | ||
|
|
a66f53e562 | ||
|
|
de699dacfe | ||
|
|
431007a731 | ||
|
|
797d511c2c | ||
|
|
0c40be5da8 | ||
|
|
2fc04caa05 | ||
|
|
78ffe38f54 | ||
|
|
8152a04155 | ||
|
|
64b39275e2 | ||
|
|
23c6291cc8 | ||
|
|
60fe67b97a | ||
|
|
96264a28a6 | ||
|
|
b86cb676d6 | ||
|
|
a05fc8447d | ||
|
|
68a617b952 | ||
|
|
f18bfab35f | ||
|
|
c04f30a5b7 | ||
|
|
7a35d60b71 | ||
|
|
cd3fa38161 | ||
|
|
6a65eabf28 | ||
|
|
0905a4dfba | ||
|
|
c68c011e69 | ||
|
|
a57066b6db | ||
|
|
0b19cc71c9 | ||
|
|
38e6b60005 | ||
|
|
35a18ecc0f | ||
|
|
5fb2ec2fda | ||
|
|
61a2ae9431 | ||
|
|
cba05171af | ||
|
|
816d63e432 | ||
|
|
56c2e6fcde | ||
|
|
d6aee24295 | ||
|
|
13b28c0f38 | ||
|
|
2f69b942d9 | ||
|
|
5977d77381 | ||
|
|
fe336ebf6a | ||
|
|
c44776870e | ||
|
|
67d5942aff | ||
|
|
48e3b44658 | ||
|
|
89347768b3 | ||
|
|
2ed726427f | ||
|
|
8ee20070b6 | ||
|
|
ca9e1e93d9 | ||
|
|
d9ea549b51 | ||
|
|
0860bb488f | ||
|
|
8efc5401cb | ||
|
|
13e5649948 | ||
|
|
bb77c4ecfc | ||
|
|
3d16151bd2 | ||
|
|
9053b200b2 | ||
|
|
a450dd29f9 | ||
|
|
2f309fe9bb | ||
|
|
5f9f7d0ba7 | ||
|
|
6f1a08f2c7 | ||
|
|
563f6810be | ||
|
|
66ed7a04a4 | ||
|
|
626475a91f | ||
|
|
f99575771c | ||
|
|
bc5a0f9140 | ||
|
|
40958179ca | ||
|
|
d98006c2cd | ||
|
|
5134c9b378 | ||
|
|
1379953a9c | ||
|
|
cc02b69020 | ||
|
|
55b4edac4d | ||
|
|
b56f136da0 | ||
|
|
d2b5e5ec01 | ||
|
|
623ab63d8d | ||
|
|
3b1beba570 | ||
|
|
4e6210c1f8 | ||
|
|
281154be7a | ||
|
|
9b4477766c | ||
|
|
53bbabab75 | ||
|
|
b1671eacb7 | ||
|
|
b41e564411 | ||
|
|
48e12154c3 | ||
|
|
5c25e2801c | ||
|
|
c2d2c1c4f6 | ||
|
|
59959bb9ee | ||
|
|
3664ca846c | ||
|
|
8c6d28d6e2 | ||
|
|
b3acd1d603 | ||
|
|
d76e056b91 | ||
|
|
5de5dbabef | ||
|
|
866ea6369e | ||
|
|
ae2f7204fa | ||
|
|
5be2b77e42 | ||
|
|
8f1f76c3d1 | ||
|
|
6f488c1d00 | ||
|
|
ee7724e754 | ||
|
|
6c4848b6f9 | ||
|
|
b6c36c20b6 | ||
|
|
a29fc4ce94 | ||
|
|
6490598fc3 | ||
|
|
f3519f6f8a | ||
|
|
d44192079d | ||
|
|
140fac4f3b | ||
|
|
802db256c1 | ||
|
|
c8a9cfa8a3 | ||
|
|
2213969c6e | ||
|
|
524a3a94c4 | ||
|
|
4ec58a0f62 | ||
|
|
e25227e39d | ||
|
|
7efaa64562 | ||
|
|
a2b95f6c61 | ||
|
|
9a6aa1a591 | ||
|
|
de780635e4 | ||
|
|
3dbb40967f | ||
|
|
f39d17abc0 | ||
|
|
a015e54569 | ||
|
|
a256864c84 | ||
|
|
d74b5bff7b | ||
|
|
31e7943d22 | ||
|
|
b2d1660e95 | ||
|
|
5d13e29300 | ||
|
|
aaf9ef06dd | ||
|
|
6725ef992a | ||
|
|
b5baecf8d0 | ||
|
|
58d1ace612 | ||
|
|
a4eca58275 | ||
|
|
c97974eb72 | ||
|
|
c46c386fea | ||
|
|
3f4ca1bcbf | ||
|
|
e4e04a4508 | ||
|
|
6d87dc0b1c | ||
|
|
db16a0c17c | ||
|
|
8fc411a30b | ||
|
|
c4a15f8e00 | ||
|
|
90e6918db0 | ||
|
|
62d7d09886 | ||
|
|
d9c933bd0a | ||
|
|
06c3e3c8a0 | ||
|
|
f284ac71b5 | ||
|
|
cd969ac33b | ||
|
|
ac43df5e03 | ||
|
|
8ea6417ea6 | ||
|
|
19295e1d30 | ||
|
|
f84b10f2eb | ||
|
|
184edf9d4c | ||
|
|
257e247e23 | ||
|
|
a2f0b4a64f | ||
|
|
9f1ec2be7e | ||
|
|
046ba62256 | ||
|
|
c9f18b0b71 | ||
|
|
b61e1bdbe3 | ||
|
|
1a01caee2c | ||
|
|
2fed610470 | ||
|
|
2026843628 | ||
|
|
9997d31421 | ||
|
|
1079e4a759 | ||
|
|
ce5c258cf9 | ||
|
|
c1b76b35ec | ||
|
|
e9cb083672 | ||
|
|
034a5c2071 | ||
|
|
6c7e3019ff | ||
|
|
fc02902ccb | ||
|
|
f33ff6d380 | ||
|
|
2c30deb298 | ||
|
|
6ea635a1e9 | ||
|
|
858134927a | ||
|
|
0eec51ef8e | ||
|
|
de065076df | ||
|
|
ad4c418736 | ||
|
|
9ab368a760 | ||
|
|
59f3cf20a5 | ||
|
|
8a58b6ee0f | ||
|
|
d6816c89e5 | ||
|
|
7ca37ee54a | ||
|
|
4b4304629f | ||
|
|
dba557b942 | ||
|
|
fe158e7f62 | ||
|
|
ac7fed649a | ||
|
|
cb67a51686 | ||
|
|
4d85a2aa41 | ||
|
|
0df875d65a | ||
|
|
fff1ec0947 | ||
|
|
250a7ef25e | ||
|
|
805d22802c | ||
|
|
9745ce7d34 | ||
|
|
c165c15099 | ||
|
|
351231aab9 | ||
|
|
0e78494f39 | ||
|
|
886ce38543 | ||
|
|
f542c8a12e | ||
|
|
c35cac860f | ||
|
|
afcdc5ec5b | ||
|
|
b9f24001ed | ||
|
|
1f0319c242 | ||
|
|
5e8e95f716 | ||
|
|
dd73b20b3f | ||
|
|
c6a83ac6b2 | ||
|
|
6ea7a1405f | ||
|
|
774db8d3a4 | ||
|
|
a7187d62d3 | ||
|
|
e5788b9ec5 | ||
|
|
be96491091 | ||
|
|
ea0b83476f | ||
|
|
187f77d32f | ||
|
|
46655e3326 | ||
|
|
4f1615b736 | ||
|
|
8b3ab44c1c | ||
|
|
10b6258175 | ||
|
|
13b532c12a | ||
|
|
126f4238d6 | ||
|
|
9666feac57 | ||
|
|
d464d00067 | ||
|
|
6e96571cc8 | ||
|
|
2b70c983bf | ||
|
|
3a2c18d8a9 | ||
|
|
29fb40eecc | ||
|
|
684d25780e | ||
|
|
51a292e5d0 | ||
|
|
4f99107ac5 | ||
|
|
e4685c7018 | ||
|
|
6a42793a59 | ||
|
|
4a81df4174 | ||
|
|
512e961820 | ||
|
|
cbccf7e7fa | ||
|
|
f1890c2d3e | ||
|
|
0c30b20a66 | ||
|
|
14b3f13338 | ||
|
|
0f75f5ec69 | ||
|
|
bdbc5438aa | ||
|
|
525131dbaf | ||
|
|
f8938a46bb | ||
|
|
50ce18d201 | ||
|
|
bba06321c8 | ||
|
|
3439b684e1 | ||
|
|
0ffba596e5 | ||
|
|
c112322daf | ||
|
|
a942a487a0 | ||
|
|
7f02c27a3a | ||
|
|
a69edd5c55 | ||
|
|
f8a0d8b9c5 | ||
|
|
a7214740ec | ||
|
|
17fb0f724c | ||
|
|
5e14dad961 | ||
|
|
dc48363398 | ||
|
|
df4a8ec15e | ||
|
|
810e3ff5fe | ||
|
|
aef065a057 | ||
|
|
1824786a80 | ||
|
|
c8b7fed168 | ||
|
|
4118390283 | ||
|
|
13a9fd36d3 | ||
|
|
9ad933934f | ||
|
|
92240c6c2c | ||
|
|
689a78d2ee | ||
|
|
7ed0b93faa | ||
|
|
3a004c41f8 | ||
|
|
90ed83efd5 | ||
|
|
3a2ccf86fd | ||
|
|
4e6f906903 | ||
|
|
c73ca48e35 | ||
|
|
e1d7b3132d | ||
|
|
1f4f99079b | ||
|
|
bf495336d9 | ||
|
|
cc636bcae0 | ||
|
|
cf185a6148 | ||
|
|
f92ce637bf | ||
|
|
2a34d6eb8b | ||
|
|
a8e3f67371 | ||
|
|
4a7d4dedd0 | ||
|
|
7d3178015b | ||
|
|
54079990d0 | ||
|
|
a379391862 | ||
|
|
dfb2fbf88b | ||
|
|
68f201980a | ||
|
|
9a079083e9 | ||
|
|
1ee1b122fe |
No files matched your search
@@ -25,7 +25,18 @@ jobs:
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
|
||||
|
||||
- name: Rustfmt Check
|
||||
working-directory: ./app/src/main/rust
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Clippy Check
|
||||
working-directory: ./app/src/main/rust
|
||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
- name: Ktlint Check
|
||||
run: ./gradlew ktlintCheck
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Pull Request Check
|
||||
|
||||
on: [pull_request]
|
||||
on: [ pull_request ]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
@@ -20,7 +20,18 @@ jobs:
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android && rustup component add rustfmt clippy
|
||||
|
||||
- name: Rustfmt Check
|
||||
working-directory: ./app/src/main/rust
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Clippy Check
|
||||
working-directory: ./app/src/main/rust
|
||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
- name: Ktlint Check
|
||||
run: ./gradlew ktlintCheck
|
||||
|
||||
@@ -23,7 +23,10 @@ jobs:
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.2.9519653" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;26.0.10404224" --channel=1 --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
|
||||
- name: Install Rust Toolchain
|
||||
run: rustup update && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
|
||||
|
||||
- name: Gradle Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
@@ -42,7 +45,6 @@ jobs:
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: Bump Version
|
||||
discussion_category_name: Announcements
|
||||
files: |
|
||||
app/build/outputs/apk/release/EhViewer-universal.apk
|
||||
app/build/outputs/apk/release/EhViewer-arm64-v8a.apk
|
||||
@@ -50,24 +52,3 @@ jobs:
|
||||
app/build/outputs/apk/release/EhViewer-x86.apk
|
||||
app/build/outputs/apk/release/EhViewer-armeabi-v7a.apk
|
||||
app/build/outputs/mapping/release/mapping.txt
|
||||
|
||||
- name: Collect Apks
|
||||
run: |
|
||||
mkdir app/build/outputs/apk/release/fdroid
|
||||
cp app/build/outputs/apk/release/app-arm64-v8a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME.apk
|
||||
cp app/build/outputs/apk/release/app-armeabi-v7a-release.apk app/build/outputs/apk/release/fdroid/EhViewer-v$GITHUB_REF_NAME-armv7.apk
|
||||
|
||||
- name: Push Apks
|
||||
uses: Ehviewer-Overhauled/github-action-push-to-another-repository@main
|
||||
env:
|
||||
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
|
||||
with:
|
||||
source-directory: app/build/outputs/apk/release/fdroid
|
||||
destination-github-username: Ehviewer-Overhauled
|
||||
destination-repository-name: fdroid-repo
|
||||
user-name: 'github-actions[bot]'
|
||||
user-email: 'github-actions[bot]@users.noreply.github.com'
|
||||
target-branch: deploy
|
||||
create-target-branch: true
|
||||
commit-message: Add apks from $GITHUB_REF
|
||||
target-directory: fdroid/tmp-apks
|
||||
+107
-46
@@ -1,27 +1,33 @@
|
||||
import java.io.ByteArrayOutputStream
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateRule.GROUP
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
val isRelease: Boolean
|
||||
get() = gradle.startParameter.taskNames.any { it.contains("Release") }
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
kotlin("android")
|
||||
id("kotlin-parcelize")
|
||||
kotlin("plugin.serialization")
|
||||
id("com.google.devtools.ksp")
|
||||
id("com.mikepenz.aboutlibraries.plugin")
|
||||
id("org.mozilla.rust-android-gradle.rust-android")
|
||||
id("dev.shreyaspatil.compose-compiler-report-generator")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 33
|
||||
buildToolsVersion = "33.0.2"
|
||||
ndkVersion = "25.2.9519653"
|
||||
androidResources.generateLocaleConfig = true
|
||||
compileSdk = 34
|
||||
buildToolsVersion = "34.0.0"
|
||||
ndkVersion = "26.0.10404224"
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
reset()
|
||||
if (gradle.startParameter.taskNames.any { it.contains("Release") }) {
|
||||
if (isRelease) {
|
||||
include("arm64-v8a", "x86_64", "armeabi-v7a", "x86")
|
||||
isUniversalApk = true
|
||||
} else {
|
||||
@@ -39,26 +45,26 @@ android {
|
||||
enableV4Signing = true
|
||||
}
|
||||
|
||||
val commitSha by lazy {
|
||||
val stdout = ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine = "git rev-parse --short=7 HEAD".split(' ')
|
||||
standardOutput = stdout
|
||||
}
|
||||
stdout.toString().trim()
|
||||
}
|
||||
val commitSha = providers.exec {
|
||||
commandLine = "git rev-parse --short=7 HEAD".split(' ')
|
||||
}.standardOutput.asText.get().trim()
|
||||
|
||||
val buildTime by lazy {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm").withZone(ZoneOffset.UTC)
|
||||
formatter.format(Instant.now())
|
||||
}
|
||||
|
||||
val repoName = providers.exec {
|
||||
commandLine = "git remote get-url origin".split(' ')
|
||||
}.standardOutput.asText.get().trim().removePrefix("https://github.com/").removePrefix("git@github.com:")
|
||||
.removeSuffix(".git")
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "moe.tarsin.ehviewer"
|
||||
minSdk = 28
|
||||
targetSdk = 33
|
||||
versionCode = 180039
|
||||
versionName = "1.8.8.0"
|
||||
targetSdk = 34
|
||||
versionCode = 180043
|
||||
versionName = "1.8.9.2-gms"
|
||||
resourceConfigurations.addAll(
|
||||
listOf(
|
||||
"zh",
|
||||
@@ -77,6 +83,7 @@ android {
|
||||
)
|
||||
buildConfigField("String", "COMMIT_SHA", "\"$commitSha\"")
|
||||
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
|
||||
buildConfigField("String", "REPO_NAME", "\"$repoName\"")
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
@@ -97,6 +104,7 @@ android {
|
||||
"-progressive",
|
||||
"-Xjvm-default=all",
|
||||
"-Xlambdas=indy",
|
||||
"-XXLanguage:+BreakContinueInInlineLambdas",
|
||||
|
||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
@@ -109,8 +117,9 @@ android {
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
"-opt-in=splitties.experimental.ExperimentalSplittiesApi",
|
||||
"-opt-in=splitties.preferences.DataStorePreferencesPreview",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -150,7 +159,7 @@ android {
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.7"
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
|
||||
namespace = "com.hippo.ehviewer"
|
||||
@@ -158,41 +167,43 @@ android {
|
||||
|
||||
dependencies {
|
||||
// https://developer.android.com/jetpack/androidx/releases/activity
|
||||
implementation("androidx.activity:activity-compose:1.7.2")
|
||||
implementation("androidx.activity:activity-compose:1.8.0-alpha06")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")
|
||||
implementation("androidx.browser:browser:1.5.0")
|
||||
implementation("androidx.collection:collection-ktx:1.3.0-alpha04")
|
||||
implementation("androidx.browser:browser:1.6.0")
|
||||
implementation("androidx.collection:collection-ktx:1.3.0-beta01")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/compose-material3
|
||||
// api(platform("androidx.compose:compose-bom:2023.05.00"))
|
||||
api(platform("dev.chrisbanes.compose:compose-bom:2023.04.00-beta01"))
|
||||
api(platform("dev.chrisbanes.compose:compose-bom:2023.07.00-alpha02"))
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.compose.material3:material3-window-size-class")
|
||||
implementation("androidx.compose.ui:ui-util")
|
||||
|
||||
implementation("androidx.core:core-ktx:1.11.0-beta01")
|
||||
implementation("androidx.core:core-ktx:1.12.0-rc01")
|
||||
|
||||
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha10")
|
||||
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha12")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
|
||||
implementation("androidx.drawerlayout:drawerlayout:1.2.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.7.0-alpha02")
|
||||
// https://developer.android.com/jetpack/androidx/releases/lifecycle
|
||||
implementation("androidx.lifecycle:lifecycle-process:2.6.1")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/navigation
|
||||
val nav_version = "2.5.3"
|
||||
val nav_version = "2.7.0"
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
|
||||
implementation("androidx.navigation:navigation-compose:$nav_version")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/paging
|
||||
implementation("androidx.paging:paging-common-ktx:3.2.0-alpha06")
|
||||
implementation("androidx.paging:paging-compose:1.0.0-alpha20")
|
||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||
val paging = "3.2.0"
|
||||
implementation("androidx.paging:paging-compose:$paging")
|
||||
implementation("androidx.paging:paging-runtime-ktx:$paging")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||
implementation("androidx.recyclerview:recyclerview-selection:1.2.0-alpha01")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/room
|
||||
val room_version = "2.6.0-alpha01"
|
||||
val room_version = "2.6.0-alpha03"
|
||||
ksp("androidx.room:room-compiler:$room_version")
|
||||
implementation("androidx.room:room-paging:$room_version")
|
||||
|
||||
@@ -201,25 +212,33 @@ dependencies {
|
||||
implementation("com.drakeet.drawer:drawer:1.0.3")
|
||||
implementation("com.github.chrisbanes:PhotoView:2.3.0") // Dead Dependency
|
||||
implementation("com.github.tachiyomiorg:DirectionalViewPager:1.0.0") // Dead Dependency
|
||||
implementation("com.github.nanihadesuka:LazyColumnScrollbar:1.6.3")
|
||||
// https://github.com/google/accompanist/releases
|
||||
implementation("com.google.accompanist:accompanist-themeadapter-material3:0.30.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
val accompanist_version = "0.31.6-rc"
|
||||
implementation("com.google.accompanist:accompanist-themeadapter-material3:$accompanist_version")
|
||||
implementation("com.google.accompanist:accompanist-webview:$accompanist_version")
|
||||
implementation("com.google.android.material:material:1.11.0-alpha02")
|
||||
|
||||
val splitties = "3.0.0"
|
||||
implementation("com.louiscad.splitties:splitties-appctx:$splitties")
|
||||
implementation("com.louiscad.splitties:splitties-systemservices:$splitties")
|
||||
implementation("com.louiscad.splitties:splitties-preferences:$splitties")
|
||||
implementation("com.louiscad.splitties:splitties-arch-room:$splitties")
|
||||
|
||||
// https://square.github.io/okhttp/changelogs/changelog/
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11"))
|
||||
implementation("com.squareup.okhttp3:okhttp-android")
|
||||
implementation("com.squareup.okhttp3:okhttp-coroutines")
|
||||
|
||||
implementation("com.squareup.okio:okio-jvm:3.3.0")
|
||||
implementation("com.squareup.okio:okio-jvm:3.5.0")
|
||||
|
||||
implementation("com.mikepenz:aboutlibraries-core:10.8.3")
|
||||
|
||||
implementation("dev.chrisbanes.insetter:insetter:0.6.1") // Dead Dependency
|
||||
implementation("dev.rikka.rikkax.core:core-ktx:1.4.1")
|
||||
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
|
||||
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
|
||||
implementation("dev.rikka.rikkax.preference:simplemenu-preference:1.0.3")
|
||||
implementation("dev.rikka.rikkax.material:material-preference:2.0.0")
|
||||
|
||||
implementation(platform("io.arrow-kt:arrow-stack:1.2.0-RC"))
|
||||
implementation(platform("io.arrow-kt:arrow-stack:1.2.0"))
|
||||
implementation("io.arrow-kt:arrow-fx-coroutines")
|
||||
|
||||
// https://coil-kt.github.io/coil/changelog/
|
||||
@@ -227,17 +246,59 @@ dependencies {
|
||||
implementation("io.coil-kt:coil-compose")
|
||||
implementation("io.coil-kt:coil-gif")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.5.1")
|
||||
implementation("org.jsoup:jsoup:1.16.1")
|
||||
}
|
||||
val ktor = "2.3.3"
|
||||
implementation("io.ktor:ktor-io-jvm:$ktor")
|
||||
implementation("io.ktor:ktor-utils-jvm:$ktor")
|
||||
|
||||
configurations.all {
|
||||
exclude("dev.rikka.rikkax.appcompat", "appcompat")
|
||||
exclude("dev.rikka.rikkax.material", "material")
|
||||
implementation("org.chromium.net:cronet-api:113.5672.61")
|
||||
implementation("com.google.android.gms:play-services-cronet:18.0.1")
|
||||
|
||||
val serialization = "1.5.1"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:$serialization")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
||||
implementation("org.jsoup:jsoup:1.16.1")
|
||||
|
||||
val chunker = "4.0.0"
|
||||
debugImplementation("com.github.chuckerteam.chucker:library:$chunker")
|
||||
releaseImplementation("com.github.chuckerteam.chucker:library-no-op:$chunker")
|
||||
|
||||
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
arg("room.generateKotlin", "true")
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
duplicationMode = MERGE
|
||||
duplicationRule = GROUP
|
||||
exclusionPatterns = listOf(
|
||||
"androidx\\..*".toPattern(),
|
||||
".*annotations".toPattern(),
|
||||
"com\\.google\\.code\\..*".toPattern(),
|
||||
"com\\.google\\.guava:listenablefuture".toPattern(),
|
||||
"com\\.google\\.protobuf:protobuf-javalite".toPattern(),
|
||||
"org\\.checkerframework:checker-qual".toPattern(),
|
||||
"org\\.chromium\\.net:cronet-embedded".toPattern(),
|
||||
"org\\.slf4j:slf4j-api".toPattern(),
|
||||
)
|
||||
}
|
||||
|
||||
cargo {
|
||||
module = "src/main/rust"
|
||||
libname = "ehviewer_rust"
|
||||
targets = if (isRelease) listOf("arm", "x86", "arm64", "x86_64") else listOf("arm64")
|
||||
if (isRelease) profile = "release"
|
||||
}
|
||||
|
||||
tasks.configureEach {
|
||||
if ((name == "mergeDebugJniLibFolders" || name == "mergeReleaseJniLibFolders")) {
|
||||
dependsOn("cargoBuild")
|
||||
// fix mergeDebugJniLibFolders UP-TO-DATE
|
||||
inputs.dir(buildDir.resolve("rustJniLibs/android"))
|
||||
}
|
||||
}
|
||||
Vendored
+8
@@ -13,6 +13,14 @@
|
||||
|
||||
-keep class androidx.viewpager.widget.ViewPager$LayoutParams { int position; }
|
||||
|
||||
-keep class com.hippo.ehviewer.client.parser.Torrent { *; }
|
||||
-keep class com.hippo.ehviewer.client.parser.GalleryListResult { *; }
|
||||
-keep class com.hippo.ehviewer.client.parser.Limits { *; }
|
||||
-keep class com.hippo.ehviewer.client.data.BaseGalleryInfo { *; }
|
||||
|
||||
# Ktor logger
|
||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
||||
|
||||
-keepattributes LineNumberTable,SourceFile
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 10,
|
||||
"identityHash": "613604f93bf815adeb87f03e2166e983",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '613604f93bf815adeb87f03e2166e983')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 11,
|
||||
"identityHash": "92915e937cb7ccc536d352fda5cf287f",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL DEFAULT 0, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL DEFAULT 0, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '92915e937cb7ccc536d352fda5cf287f')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 12,
|
||||
"identityHash": "d40b38b0ba3b1c53b773728cdee328c8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd40b38b0ba3b1c53b773728cdee328c8')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 13,
|
||||
"identityHash": "df15d8f70f45047a738c8911212ea73c",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "GALLERIES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'df15d8f70f45047a738c8911212ea73c')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 14,
|
||||
"identityHash": "54e4f2704cc47c5a6a2f4b96de978cce",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "GALLERIES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '54e4f2704cc47c5a6a2f4b96de978cce')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 15,
|
||||
"identityHash": "991a3a3ddc4fe8d4dbcb39b975ab73b8",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "GALLERIES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_DOWNLOAD_LABELS_LABEL",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"LABEL"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '991a3a3ddc4fe8d4dbcb39b975ab73b8')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 16,
|
||||
"identityHash": "06e5ff8fd6797d5801e86753fa5831b2",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "GALLERIES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_DOWNLOAD_LABELS_LABEL",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"LABEL"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`LABEL`) REFERENCES `DOWNLOAD_LABELS`(`LABEL`) ON UPDATE CASCADE ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_DOWNLOADS_LABEL",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"LABEL"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_DOWNLOADS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "DOWNLOAD_LABELS",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"LABEL"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"LABEL"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '06e5ff8fd6797d5801e86753fa5831b2')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 4,
|
||||
"identityHash": "f5752deda842dccabded1cdbf1c78384",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BOOKMARKS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "page",
|
||||
"columnName": "PAGE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumb",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumb",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumb",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumb",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f5752deda842dccabded1cdbf1c78384')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 5,
|
||||
"identityHash": "f5752deda842dccabded1cdbf1c78384",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BOOKMARKS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "page",
|
||||
"columnName": "PAGE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f5752deda842dccabded1cdbf1c78384')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 6,
|
||||
"identityHash": "e45f4380ef769c127604cb2a502a942b",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BOOKMARKS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`PAGE` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "page",
|
||||
"columnName": "PAGE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e45f4380ef769c127604cb2a502a942b')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 7,
|
||||
"identityHash": "9563c2017be4341dc1bada115426df77",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT, `ENABLE` INTEGER, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9563c2017be4341dc1bada115426df77')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 8,
|
||||
"identityHash": "f09bae277bbdab54ecbb0d698c83e734",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `LABEL` TEXT, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f09bae277bbdab54ecbb0d698c83e734')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,448 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 9,
|
||||
"identityHash": "64f51a531e13d2b0b971f3d8da0876b0",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `MODE` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlotBackingField",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '64f51a531e13d2b0b971f3d8da0876b0')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "69230710633494b4b331e10dae436fb7",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "suggestions",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `date` LONG, `query` TEXT, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "date",
|
||||
"columnName": "date",
|
||||
"affinity": "LONG",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "query",
|
||||
"columnName": "query",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '69230710633494b4b331e10dae436fb7')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "bf4a5b5f3cb9bc7383488bae14f5d4bd",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "suggestions",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` INTEGER NOT NULL, `query` TEXT NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "date",
|
||||
"columnName": "date",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "query",
|
||||
"columnName": "query",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bf4a5b5f3cb9bc7383488bae14f5d4bd')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,8 @@
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
@@ -22,21 +23,21 @@
|
||||
android:allowBackup="true"
|
||||
android:appCategory="image"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:fullBackupContent="@xml/backup_scheme"
|
||||
android:hasFragileUserData="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locale_config"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:targetApi="tiramisu">
|
||||
<activity
|
||||
android:name=".ui.login.LoginActivity"
|
||||
android:name=".ui.ConfigureActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:banner="@mipmap/ic_leanback_banner"
|
||||
@@ -69,8 +70,6 @@
|
||||
<data android:scheme="https" />
|
||||
<data android:host="exhentai.org" />
|
||||
<data android:host="e-hentai.org" />
|
||||
<data android:host="g.e-hentai.org" />
|
||||
<data android:host="lofi.e-hentai.org" />
|
||||
</intent-filter>
|
||||
<intent-filter android:label="@string/keyword_search">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
@@ -125,22 +124,18 @@
|
||||
<data android:mimeType="application/x-xz-compressed-tar" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.SettingsActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name=".ui.SecurityActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme" />
|
||||
android:label="@string/settings" />
|
||||
<activity
|
||||
android:name=".shortcuts.ShortcutsActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||
<activity
|
||||
android:name=".ui.WebViewActivity" />
|
||||
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
From fc9fb0ea245259e346ab46c334f03b557fe63b6b Mon Sep 17 00:00:00 2001
|
||||
From 2bda36800a5424c44378bbc0cc8bb5d26a0f34fa Mon Sep 17 00:00:00 2001
|
||||
From: tarsin <yuanqingxiang233@163.com>
|
||||
Date: Tue, 22 Nov 2022 12:37:12 +0800
|
||||
Date: Sat, 22 Jul 2023 08:30:20 +0800
|
||||
Subject: [PATCH] Don't build other binarys
|
||||
|
||||
---
|
||||
@@ -8,19 +8,19 @@ Subject: [PATCH] Don't build other binarys
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 01c73cd..b461e68 100644
|
||||
index 55cd358..52b57aa 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -592,7 +592,7 @@ check_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG)
|
||||
@@ -962,7 +962,7 @@ check_symbol_exists(getopt_long getopt.h HAVE_GETOPT_LONG)
|
||||
# xzdec
|
||||
#############################################################################
|
||||
|
||||
-if(HAVE_GETOPT_LONG)
|
||||
-if(HAVE_GETOPT_LONG AND HAVE_DECODERS)
|
||||
+if(0)
|
||||
add_executable(xzdec
|
||||
src/common/sysdefs.h
|
||||
src/common/tuklib_common.h
|
||||
@@ -630,7 +630,7 @@ endif()
|
||||
@@ -1008,7 +1008,7 @@ endif()
|
||||
# xz
|
||||
#############################################################################
|
||||
|
||||
@@ -30,5 +30,5 @@ index 01c73cd..b461e68 100644
|
||||
src/common/mythread.h
|
||||
src/common/sysdefs.h
|
||||
--
|
||||
2.39.2
|
||||
2.41.0
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
From 2a67861e6f06794f3671c4988bac12330d8422e3 Mon Sep 17 00:00:00 2001
|
||||
From: tarsin <yuanqingxiang233@163.com>
|
||||
Date: Fri, 27 May 2022 23:21:20 +0800
|
||||
Subject: [PATCH 1/2] Don't build shared lib
|
||||
|
||||
---
|
||||
libarchive/CMakeLists.txt | 8 +-------
|
||||
1 file changed, 1 insertion(+), 7 deletions(-)
|
||||
|
||||
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt
|
||||
index ff7ade00..00c2e170 100644
|
||||
--- a/libarchive/CMakeLists.txt
|
||||
+++ b/libarchive/CMakeLists.txt
|
||||
@@ -242,12 +242,6 @@ ELSEIF(ARCHIVE_ACL_SUNOS)
|
||||
LIST(APPEND libarchive_SOURCES archive_disk_acl_sunos.c)
|
||||
ENDIF()
|
||||
|
||||
-# Libarchive is a shared library
|
||||
-ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
|
||||
-TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .)
|
||||
-TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
|
||||
-SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
|
||||
-
|
||||
# archive_static is a static library
|
||||
ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS})
|
||||
TARGET_LINK_LIBRARIES(archive_static ${ADDITIONAL_LIBS})
|
||||
@@ -260,7 +254,7 @@ ENDIF(NOT WIN32 OR CYGWIN)
|
||||
|
||||
IF(ENABLE_INSTALL)
|
||||
# How to install the libraries
|
||||
- INSTALL(TARGETS archive archive_static
|
||||
+ INSTALL(TARGETS archive_static
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
--
|
||||
2.39.2
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
From 57da49f9b0ed8538573f6cfe289f835ee0078eec Mon Sep 17 00:00:00 2001
|
||||
From: tarsin <yuanqingxiang233@163.com>
|
||||
Date: Sat, 22 Jul 2023 17:50:08 +0800
|
||||
Subject: [PATCH] Hide symbols
|
||||
|
||||
---
|
||||
CMakeLists.txt | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 646484b7..67a71272 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -2060,10 +2060,10 @@ SET(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fvisibility=hidden -Werror")
|
||||
CHECK_C_SOURCE_COMPILES("void __attribute__((visibility(\"default\"))) foo(void);
|
||||
int main() { return 0; }" HAVE_VISIBILITY_ATTR)
|
||||
-IF (HAVE_VISIBILITY_ATTR)
|
||||
+IF (0)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
|
||||
ADD_DEFINITIONS(-D__LIBARCHIVE_ENABLE_VISIBILITY)
|
||||
-ENDIF(HAVE_VISIBILITY_ATTR)
|
||||
+ENDIF(0)
|
||||
SET(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||
|
||||
# Generate "config.h" from "build/cmake/config.h.in"
|
||||
--
|
||||
2.41.0
|
||||
|
||||
@@ -3,7 +3,7 @@ project(ehviewer C ASM)
|
||||
include(FetchContent)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -funroll-loops -flto=thin \
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -funroll-loops -flto \
|
||||
-mllvm -polly \
|
||||
-mllvm -polly-run-dce \
|
||||
-mllvm -polly-run-inliner \
|
||||
@@ -20,9 +20,11 @@ endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
get_filename_component(deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
set(FETCHCONTENT_BASE_DIR ${deps})
|
||||
|
||||
set(BUILD_TESTING 0)
|
||||
|
||||
FetchContent_Declare(liblzma
|
||||
GIT_REPOSITORY https://git.tukaani.org/xz.git
|
||||
GIT_TAG v5.4.1
|
||||
GIT_TAG v5.4.3
|
||||
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Don-t-build-other-binarys.patch || cd .
|
||||
)
|
||||
|
||||
@@ -50,13 +52,19 @@ SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
|
||||
SET(HAVE_LZMADEC_H 1)
|
||||
SET(HAVE_LIBLZMADEC 1)
|
||||
|
||||
option(ENABLE_TAR OFF)
|
||||
option(ENABLE_CPIO OFF)
|
||||
option(ENABLE_CAT OFF)
|
||||
option(ENABLE_UNZIP OFF)
|
||||
option(ENABLE_TEST OFF)
|
||||
|
||||
# Configure libarchive link's static lib
|
||||
SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
|
||||
|
||||
FetchContent_Declare(libarchive
|
||||
GIT_REPOSITORY https://github.com/libarchive/libarchive
|
||||
GIT_TAG v3.6.2
|
||||
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Don-t-build-shared-lib.patch ${CMAKE_CURRENT_LIST_DIR}/0002-Insert-link-libs.patch || cd .
|
||||
GIT_TAG v3.7.0
|
||||
PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/0001-Hide-symbols.patch ${CMAKE_CURRENT_LIST_DIR}/0002-Insert-link-libs.patch || cd .
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libarchive)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package androidx.navigation.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.navigation.ActivityNavigator
|
||||
@@ -8,8 +9,8 @@ import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
import androidx.navigation.NavOptions
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
|
||||
if (navController.currentDestination?.matchDestination(item.itemId) == true) return true
|
||||
val builder = NavOptions.Builder().setLaunchSingleTop(true)
|
||||
if (
|
||||
navController.currentDestination!!.parent!!.findNode(item.itemId)
|
||||
@@ -20,10 +21,10 @@ fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boo
|
||||
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
|
||||
.setPopExitAnim(R.anim.nav_default_pop_exit_anim)
|
||||
} else {
|
||||
builder.setEnterAnim(com.hippo.ehviewer.R.anim.scene_open_enter)
|
||||
.setExitAnim(com.hippo.ehviewer.R.anim.scene_open_exit)
|
||||
.setPopEnterAnim(com.hippo.ehviewer.R.anim.scene_close_enter)
|
||||
.setPopExitAnim(com.hippo.ehviewer.R.anim.scene_close_exit)
|
||||
builder.setEnterAnim(androidx.fragment.R.animator.fragment_open_enter)
|
||||
.setExitAnim(androidx.fragment.R.animator.fragment_open_exit)
|
||||
.setPopEnterAnim(androidx.fragment.R.animator.fragment_close_enter)
|
||||
.setPopExitAnim(androidx.fragment.R.animator.fragment_close_exit)
|
||||
}
|
||||
val options = builder.build()
|
||||
return try {
|
||||
|
||||
@@ -22,7 +22,7 @@ import androidx.annotation.IntDef
|
||||
import com.davemorrissey.labs.subscaleview.decoder.Decoder
|
||||
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -31,7 +31,6 @@ import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.ArrayList
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.locks.ReadWriteLock
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
@@ -442,8 +441,8 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
detector = GestureDetector(
|
||||
context,
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
if (panEnabled && isReady && vTranslate != null && (abs(e1.x - e2.x) > 50 || abs(e1.y - e2.y) > 50) && (abs(velocityX) > 500 || abs(velocityY) > 500) && !isZooming) {
|
||||
override fun onFling(e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
if (panEnabled && isReady && vTranslate != null && e1 != null && (abs(e1.x - e2.x) > 50 || abs(e1.y - e2.y) > 50) && (abs(velocityX) > 500 || abs(velocityY) > 500) && !isZooming) {
|
||||
val vTranslateEnd = PointF(vTranslate!!.x + velocityX * 0.25F, vTranslate!!.y + velocityY * 0.25F)
|
||||
val sCenterXEnd = (width / 2 - vTranslateEnd.x) / scale
|
||||
val sCenterYEnd = (height / 2 - vTranslateEnd.y) / scale
|
||||
@@ -463,7 +462,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
if (!ReaderActivity.readerPreferences.doubleTapToZoom().get()) return false
|
||||
if (!ReaderPreferences.doubleTapToZoom().get()) return false
|
||||
if (isZoomEnabled && isReady && vTranslate != null) {
|
||||
// Hacky solution for #15 - after a double tap the GestureDetector gets in a state
|
||||
// where the next fling is ignored, so here we replace it with a new one.
|
||||
@@ -1713,7 +1712,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
* @param sTarget target object for result. The same instance is also returned.
|
||||
* @return a coordinate representing the corresponding source coordinate. This is the same instance passed to the sTarget param.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun viewToSourceCoord(vx: Float, vy: Float, sTarget: PointF = PointF()): PointF? {
|
||||
if (vTranslate == null) {
|
||||
return null
|
||||
@@ -1765,7 +1763,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
* @param vTarget target object for result. The same instance is also returned.
|
||||
* @return view coordinates. This is the same instance passed to the vTarget param.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun sourceToViewCoord(sx: Float, sy: Float, vTarget: PointF = PointF()): PointF? {
|
||||
if (vTranslate == null) {
|
||||
return null
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.graphics.Bitmap
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import java.lang.Exception
|
||||
import kotlin.Throws
|
||||
|
||||
/**
|
||||
* Interface for image decoding classes.
|
||||
@@ -21,7 +19,6 @@ interface Decoder {
|
||||
* @return Dimensions of the image.
|
||||
* @throws Exception if initialisation fails.
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
fun init(context: Context, provider: InputProvider): Point
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,10 +7,7 @@ import android.graphics.Rect
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
|
||||
class ImageDecoder @JvmOverloads constructor(
|
||||
private val cropBorders: Boolean,
|
||||
bitmapConfig: Bitmap.Config? = null,
|
||||
) : Decoder {
|
||||
class ImageDecoder(private val cropBorders: Boolean, bitmapConfig: Bitmap.Config? = null) : Decoder {
|
||||
init {
|
||||
val globalBitmapConfig = SubsamplingScaleImageView.preferredBitmapConfig
|
||||
}
|
||||
@@ -24,7 +21,6 @@ class ImageDecoder @JvmOverloads constructor(
|
||||
* @return Dimensions of the image.
|
||||
* @throws Exception if initialisation fails.
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
override fun init(context: Context, provider: InputProvider): Point {
|
||||
TODO()
|
||||
}
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.ehviewer.util.ReadableTime;
|
||||
import com.hippo.ehviewer.yorozuya.FileUtils;
|
||||
import com.hippo.ehviewer.yorozuya.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class AppConfig {
|
||||
|
||||
public static final String APP_DIRNAME = "EhViewer";
|
||||
|
||||
private static final String DOWNLOAD = "download";
|
||||
private static final String TEMP = "temp";
|
||||
private static final String PARSE_ERROR = "parse_error";
|
||||
private static final String CRASH = "crash";
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Context sContext;
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalAppDir() {
|
||||
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
|
||||
File dir = sContext.getExternalFilesDir(null);
|
||||
return FileUtils.ensureDirectory(dir) ? dir : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* mkdirs and get
|
||||
*/
|
||||
@Nullable
|
||||
public static File getDirInExternalAppDir(String filename) {
|
||||
File appFolder = getExternalAppDir();
|
||||
if (appFolder != null) {
|
||||
File dir = new File(appFolder, filename);
|
||||
return FileUtils.ensureDirectory(dir) ? dir : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getFileInExternalAppDir(String filename) {
|
||||
File appFolder = getExternalAppDir();
|
||||
if (appFolder != null) {
|
||||
File file = new File(appFolder, filename);
|
||||
return FileUtils.ensureFile(file) ? file : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getDefaultDownloadDir() {
|
||||
return getDirInExternalAppDir(DOWNLOAD);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalTempDir() {
|
||||
File dir = sContext.getExternalCacheDir();
|
||||
File file;
|
||||
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, TEMP))) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalCopyTempDir() {
|
||||
File dir = sContext.getExternalCacheDir();
|
||||
File file;
|
||||
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, "copy"))) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalParseErrorDir() {
|
||||
return getDirInExternalAppDir(PARSE_ERROR);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalCrashDir() {
|
||||
return getDirInExternalAppDir(CRASH);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getTempDir() {
|
||||
File dir = sContext.getCacheDir();
|
||||
File file;
|
||||
if (null != dir && FileUtils.ensureDirectory(file = new File(dir, TEMP))) {
|
||||
return file;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File createTempFile() {
|
||||
return FileUtils.createTempFile(getTempDir(), null);
|
||||
}
|
||||
|
||||
public static void saveParseErrorBody(@NonNull ParseException e) {
|
||||
File dir = getExternalParseErrorDir();
|
||||
if (null == dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt");
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(file);
|
||||
String message = e.getMessage();
|
||||
String body = e.getBody();
|
||||
if (null != message) {
|
||||
os.write(message.getBytes(StandardCharsets.UTF_8));
|
||||
os.write('\n');
|
||||
}
|
||||
os.write(body.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
} catch (IOException e1) {
|
||||
// Ignore
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getFilesDir(String name) {
|
||||
File dir = sContext.getFilesDir();
|
||||
if (dir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
dir = new File(dir, name);
|
||||
if (dir.isDirectory() || dir.mkdirs()) {
|
||||
return dir;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,45 +20,53 @@ import android.app.Application
|
||||
import android.content.ComponentCallbacks2
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.LruCache
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.util.DebugLogger
|
||||
import com.google.android.gms.net.CronetProviderInstaller
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhDns
|
||||
import com.hippo.ehviewer.client.EhSSLSocketFactory
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.coil.MergeInterceptor
|
||||
import com.hippo.ehviewer.coil.diskCache
|
||||
import com.hippo.ehviewer.coil.installCronetHttpUriFetcher
|
||||
import com.hippo.ehviewer.dailycheck.checkDawn
|
||||
import com.hippo.ehviewer.dao.buildMainDB
|
||||
import com.hippo.ehviewer.dao.SearchDatabase
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.ktbuilder.cache
|
||||
import com.hippo.ehviewer.ktbuilder.chunker
|
||||
import com.hippo.ehviewer.ktbuilder.diskCache
|
||||
import com.hippo.ehviewer.ktbuilder.httpClient
|
||||
import com.hippo.ehviewer.ktbuilder.imageLoader
|
||||
import com.hippo.ehviewer.legacy.cleanObsoleteCache
|
||||
import com.hippo.ehviewer.legacy.migrateCookies
|
||||
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||
import com.hippo.ehviewer.ui.lockObserver
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.Crash
|
||||
import com.hippo.ehviewer.util.FavouriteStatusRouter
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.ReadableTime
|
||||
import com.hippo.ehviewer.yorozuya.FileUtils
|
||||
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import com.hippo.ehviewer.util.isAtLeastQ
|
||||
import com.hippo.ehviewer.util.isCronetSupported
|
||||
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import okio.FileSystem
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.kt.unreachable
|
||||
import okhttp3.AsyncDns
|
||||
import okhttp3.android.AndroidAsyncDns
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import java.net.Proxy
|
||||
import java.security.KeyStore
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import splitties.arch.room.roomDb
|
||||
import splitties.init.appCtx
|
||||
|
||||
class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
private val lifecycle = ProcessLifecycleOwner.get().lifecycle
|
||||
private val lifecycleScope = lifecycle.coroutineScope
|
||||
|
||||
class EhApplication : Application(), ImageLoaderFactory {
|
||||
override fun onCreate() {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
application = this
|
||||
lifecycle.addObserver(lockObserver)
|
||||
val handler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
Thread.setDefaultUncaughtExceptionHandler { t, e ->
|
||||
try {
|
||||
@@ -69,18 +77,26 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor
|
||||
}
|
||||
handler?.uncaughtException(t, e)
|
||||
}
|
||||
super<Application>.onCreate()
|
||||
super.onCreate()
|
||||
CronetProviderInstaller.installProvider(appCtx)
|
||||
System.loadLibrary("ehviewer")
|
||||
Settings.initialize()
|
||||
System.loadLibrary("ehviewer_rust")
|
||||
ReadableTime.initialize(this)
|
||||
AppConfig.initialize(this)
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.theme)
|
||||
launchIO {
|
||||
lifecycleScope.launchIO {
|
||||
val theme = Settings.theme
|
||||
withUIContext {
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchIO {
|
||||
launchIO {
|
||||
migrateCookies()
|
||||
}
|
||||
launchIO {
|
||||
EhTagDatabase.update()
|
||||
}
|
||||
launchIO {
|
||||
ehDatabase
|
||||
EhDB
|
||||
}
|
||||
launchIO {
|
||||
DownloadManager.isIdle
|
||||
@@ -111,11 +127,11 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor
|
||||
}
|
||||
|
||||
private fun clearTempDir() {
|
||||
var dir = AppConfig.getTempDir()
|
||||
var dir = AppConfig.tempDir
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir)
|
||||
}
|
||||
dir = AppConfig.getExternalTempDir()
|
||||
dir = AppConfig.externalTempDir
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir)
|
||||
}
|
||||
@@ -128,85 +144,67 @@ class EhApplication : Application(), DefaultLifecycleObserver, ImageLoaderFactor
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
if (!locked) {
|
||||
locked_last_leave_time = System.currentTimeMillis() / 1000
|
||||
}
|
||||
locked = true
|
||||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader.Builder(this).apply {
|
||||
okHttpClient(nonCacheOkHttpClient)
|
||||
components {
|
||||
add { result, options, _ -> ImageDecoderDecoder(result.source, options, false) }
|
||||
add(MergeInterceptor)
|
||||
override fun newImageLoader() = imageLoader {
|
||||
components {
|
||||
if (isCronetSupported) {
|
||||
callFactory { unreachable() }
|
||||
installCronetHttpUriFetcher()
|
||||
} else {
|
||||
callFactory(baseOkHttpClient)
|
||||
}
|
||||
diskCache(imageCache)
|
||||
crossfade(300)
|
||||
error(R.drawable.image_failed)
|
||||
if (BuildConfig.DEBUG) logger(DebugLogger())
|
||||
}.build()
|
||||
add { result, options, _ -> ImageDecoderDecoder(result.source, options, false) }
|
||||
add(MergeInterceptor)
|
||||
}
|
||||
diskCache(imageCache)
|
||||
crossfade(300)
|
||||
error(R.drawable.image_failed)
|
||||
if (BuildConfig.DEBUG) logger(DebugLogger())
|
||||
respectCacheHeaders(false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var locked = true
|
||||
var locked_last_leave_time: Long = 0
|
||||
|
||||
lateinit var application: EhApplication
|
||||
private set
|
||||
|
||||
val ehProxySelector by lazy { EhProxySelector() }
|
||||
|
||||
val nonCacheOkHttpClient by lazy {
|
||||
OkHttpClient.Builder().apply {
|
||||
val baseOkHttpClient by lazy {
|
||||
httpClient {
|
||||
cookieJar(EhCookieStore)
|
||||
dns(EhDns)
|
||||
proxySelector(ehProxySelector)
|
||||
if (Settings.dF) {
|
||||
val factory =
|
||||
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())!!
|
||||
factory.init(null as KeyStore?)
|
||||
val manager = factory.trustManagers!!
|
||||
val trustManager = manager.filterIsInstance<X509TrustManager>().first()
|
||||
sslSocketFactory(EhSSLSocketFactory, trustManager)
|
||||
proxy(Proxy.NO_PROXY)
|
||||
if (isAtLeastQ) {
|
||||
dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6))
|
||||
}
|
||||
}.build()
|
||||
chunker { alwaysReadResponseBody(false) }
|
||||
addInterceptor(CloudflareInterceptor(appCtx))
|
||||
}
|
||||
}
|
||||
|
||||
val noRedirectOkHttpClient by lazy {
|
||||
httpClient(baseOkHttpClient) {
|
||||
followRedirects(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Never use this okhttp client to download large blobs!!!
|
||||
val okHttpClient by lazy {
|
||||
nonCacheOkHttpClient.newBuilder().cache(
|
||||
Cache(
|
||||
application.cacheDir.toOkioPath() / "http_cache",
|
||||
httpClient(baseOkHttpClient) {
|
||||
cache(
|
||||
appCtx.cacheDir.toOkioPath() / "http_cache",
|
||||
20L * 1024L * 1024L,
|
||||
FileSystem.SYSTEM,
|
||||
),
|
||||
).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val galleryDetailCache by lazy {
|
||||
LruCache<Long, GalleryDetail>(25).also {
|
||||
favouriteStatusRouter.addListener { gid, slot ->
|
||||
it[gid]?.favoriteSlot = slot
|
||||
lifecycleScope.launch {
|
||||
FavouriteStatusRouter.globalFlow.collect { (gid, slot) -> it[gid]?.favoriteSlot = slot }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val hosts by lazy { Hosts(application, "hosts.db") }
|
||||
|
||||
val favouriteStatusRouter by lazy { FavouriteStatusRouter() }
|
||||
|
||||
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
|
||||
|
||||
val ehDatabase by lazy { buildMainDB(application) }
|
||||
|
||||
val imageCache by lazy {
|
||||
diskCache {
|
||||
directory(application.cacheDir.toOkioPath() / "image_cache")
|
||||
directory(appCtx.cacheDir.toOkioPath() / "image_cache")
|
||||
maxSizeBytes(Settings.readCacheSize.coerceIn(320, 5120).toLong() * 1024 * 1024)
|
||||
}
|
||||
}
|
||||
|
||||
val searchDatabase by lazy { roomDb<SearchDatabase>("search_database.db") }
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,12 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.Room.databaseBuilder
|
||||
import com.hippo.ehviewer.EhApplication.Companion.ehDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.dao.BasicDao
|
||||
import arrow.fx.coroutines.release
|
||||
import arrow.fx.coroutines.resource
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.dao.DownloadDirname
|
||||
import com.hippo.ehviewer.dao.DownloadInfo
|
||||
import com.hippo.ehviewer.dao.DownloadLabel
|
||||
import com.hippo.ehviewer.dao.EHDB_MIGRATION_4_5
|
||||
import com.hippo.ehviewer.dao.EhDatabase
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.HistoryInfo
|
||||
@@ -35,90 +32,76 @@ import com.hippo.ehviewer.dao.LocalFavoriteInfo
|
||||
import com.hippo.ehviewer.dao.QuickSearch
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.util.sendTo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import splitties.arch.room.roomDb
|
||||
|
||||
object EhDB {
|
||||
private const val CUR_DB_VER = 4
|
||||
private val db = ehDatabase
|
||||
private val db = roomDb<EhDatabase>("eh.db")
|
||||
|
||||
// Fix state
|
||||
@get:Synchronized
|
||||
val allDownloadInfo: List<DownloadInfo>
|
||||
get() = db.downloadsDao().list().onEach {
|
||||
if (it.state == DownloadInfo.STATE_WAIT || it.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
it.state = DownloadInfo.STATE_NONE
|
||||
}
|
||||
private suspend fun putGalleryInfo(galleryInfo: BaseGalleryInfo) {
|
||||
db.galleryDao().upsert(galleryInfo)
|
||||
}
|
||||
|
||||
private suspend fun deleteGalleryInfo(galleryInfo: BaseGalleryInfo) {
|
||||
runCatching { db.galleryDao().delete(galleryInfo) }
|
||||
}
|
||||
|
||||
suspend fun getAllDownloadInfo() = db.downloadsDao().joinList().onEach {
|
||||
if (it.state == DownloadInfo.STATE_WAIT || it.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
it.state = DownloadInfo.STATE_NONE
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateDownloadInfo(downloadInfos: List<DownloadInfo>) {
|
||||
suspend fun updateDownloadInfo(downloadInfo: List<DownloadInfo>) {
|
||||
val dao = db.downloadsDao()
|
||||
dao.update(downloadInfos)
|
||||
dao.update(downloadInfo.map(DownloadInfo::downloadInfo))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putDownloadInfo(downloadInfo: DownloadInfo) {
|
||||
db.downloadsDao().run {
|
||||
if (load(downloadInfo.gid) != null) {
|
||||
update(downloadInfo)
|
||||
} else {
|
||||
insert(downloadInfo)
|
||||
}
|
||||
suspend fun putDownloadInfo(downloadInfo: DownloadInfo) {
|
||||
putGalleryInfo(downloadInfo.galleryInfo)
|
||||
db.downloadsDao().upsert(downloadInfo.downloadInfo)
|
||||
}
|
||||
|
||||
suspend fun removeDownloadInfo(downloadInfo: DownloadInfo) {
|
||||
val dao = db.downloadsDao()
|
||||
dao.delete(downloadInfo.downloadInfo)
|
||||
dao.fill(downloadInfo.position)
|
||||
deleteGalleryInfo(downloadInfo.galleryInfo)
|
||||
}
|
||||
|
||||
suspend fun removeDownloadInfo(downloadInfo: List<DownloadInfo>) {
|
||||
val dao = db.downloadsDao()
|
||||
downloadInfo.forEach {
|
||||
dao.delete(it.downloadInfo)
|
||||
deleteGalleryInfo(it.galleryInfo)
|
||||
}
|
||||
dao.fill(downloadInfo.first().position, downloadInfo.size)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeDownloadInfo(downloadInfo: DownloadInfo) {
|
||||
db.downloadsDao().delete(downloadInfo)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getDownloadDirname(gid: Long): String? {
|
||||
suspend fun getDownloadDirname(gid: Long): String? {
|
||||
val dao = db.downloadDirnameDao()
|
||||
val raw = dao.load(gid)
|
||||
return raw?.dirname
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putDownloadDirname(gid: Long, dirname: String?) {
|
||||
suspend fun putDownloadDirname(gid: Long, dirname: String) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
var raw = dao.load(gid)
|
||||
if (raw != null) {
|
||||
raw.dirname = dirname
|
||||
dao.update(raw)
|
||||
} else {
|
||||
raw = DownloadDirname(gid, dirname)
|
||||
dao.insert(raw)
|
||||
}
|
||||
dao.upsert(DownloadDirname(gid, dirname))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeDownloadDirname(gid: Long) {
|
||||
suspend fun removeDownloadDirname(gid: Long) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
dao.deleteByKey(gid)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun clearDownloadDirname() {
|
||||
private suspend fun importDownloadDirname(downloadDirnameList: List<DownloadDirname>) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
dao.deleteAll()
|
||||
dao.insertOrIgnore(downloadDirnameList)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val allDownloadLabelList: List<DownloadLabel>
|
||||
get() = db.downloadLabelDao().list()
|
||||
suspend fun getAllDownloadLabelList() = db.downloadLabelDao().list()
|
||||
|
||||
@Synchronized
|
||||
fun addDownloadLabel(label: String): DownloadLabel {
|
||||
val dao = db.downloadLabelDao()
|
||||
val raw = DownloadLabel()
|
||||
raw.label = label
|
||||
raw.time = System.currentTimeMillis()
|
||||
raw.id = dao.insert(raw)
|
||||
return raw
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun addDownloadLabel(raw: DownloadLabel): DownloadLabel {
|
||||
suspend fun addDownloadLabel(raw: DownloadLabel): DownloadLabel {
|
||||
// Reset id
|
||||
raw.id = null
|
||||
val dao = db.downloadLabelDao()
|
||||
@@ -126,221 +109,122 @@ object EhDB {
|
||||
return raw
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateDownloadLabel(raw: DownloadLabel?) {
|
||||
suspend fun updateDownloadLabel(raw: DownloadLabel) {
|
||||
val dao = db.downloadLabelDao()
|
||||
dao.update(raw!!)
|
||||
dao.update(raw)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun moveDownloadLabel(fromPosition: Int, toPosition: Int) {
|
||||
if (fromPosition == toPosition) {
|
||||
return
|
||||
}
|
||||
val reverse = fromPosition > toPosition
|
||||
val offset = if (reverse) toPosition else fromPosition
|
||||
val limit = if (reverse) fromPosition - toPosition + 1 else toPosition - fromPosition + 1
|
||||
suspend fun updateDownloadLabel(downloadLabels: List<DownloadLabel>) {
|
||||
val dao = db.downloadLabelDao()
|
||||
val list = dao.list(offset, limit)
|
||||
val step = if (reverse) 1 else -1
|
||||
val start = if (reverse) limit - 1 else 0
|
||||
val end = if (reverse) 0 else limit - 1
|
||||
val toTime = list[end].time
|
||||
var i = end
|
||||
while (if (reverse) i < start else i > start) {
|
||||
list[i].time = list[i + step].time
|
||||
i += step
|
||||
}
|
||||
list[start].time = toTime
|
||||
dao.update(list)
|
||||
dao.update(downloadLabels)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeDownloadLabel(raw: DownloadLabel?) {
|
||||
suspend fun removeDownloadLabel(raw: DownloadLabel) {
|
||||
val dao = db.downloadLabelDao()
|
||||
dao.delete(raw!!)
|
||||
dao.delete(raw)
|
||||
dao.fill(raw.position)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val allLocalFavorites: List<GalleryInfo>
|
||||
get() {
|
||||
val dao = db.localFavoritesDao()
|
||||
val list = dao.list()
|
||||
return ArrayList<GalleryInfo>(list)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun searchLocalFavorites(query: String): List<GalleryInfo> {
|
||||
val dao = db.localFavoritesDao()
|
||||
val list = dao.list("%$query%")
|
||||
return ArrayList<GalleryInfo>(list)
|
||||
suspend fun removeLocalFavorites(galleryInfo: BaseGalleryInfo) {
|
||||
db.localFavoritesDao().deleteByKey(galleryInfo.gid)
|
||||
deleteGalleryInfo(galleryInfo)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeLocalFavorites(gid: Long) {
|
||||
db.localFavoritesDao().deleteByKey(gid)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeLocalFavorites(gidArray: LongArray) {
|
||||
val dao = db.localFavoritesDao()
|
||||
for (gid in gidArray) {
|
||||
dao.deleteByKey(gid)
|
||||
suspend fun removeLocalFavorites(galleryInfoList: List<BaseGalleryInfo>) {
|
||||
galleryInfoList.forEach {
|
||||
removeLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Synchronized
|
||||
fun containLocalFavorites(gid: Long): Boolean {
|
||||
suspend fun containLocalFavorites(gid: Long): Boolean {
|
||||
val dao = db.localFavoritesDao()
|
||||
return dao.contains(gid)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putLocalFavorites(galleryInfo: GalleryInfo) {
|
||||
val dao = db.localFavoritesDao()
|
||||
if (null == dao.load(galleryInfo.gid)) {
|
||||
val info: LocalFavoriteInfo
|
||||
if (galleryInfo is LocalFavoriteInfo) {
|
||||
info = galleryInfo
|
||||
} else {
|
||||
info = LocalFavoriteInfo(galleryInfo)
|
||||
info.time = System.currentTimeMillis()
|
||||
}
|
||||
dao.insert(info)
|
||||
suspend fun putLocalFavorites(galleryInfo: BaseGalleryInfo) {
|
||||
putGalleryInfo(galleryInfo)
|
||||
db.localFavoritesDao().upsert(LocalFavoriteInfo(galleryInfo.gid))
|
||||
}
|
||||
|
||||
suspend fun putLocalFavorites(galleryInfoList: List<BaseGalleryInfo>) {
|
||||
galleryInfoList.forEach {
|
||||
putLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putLocalFavorites(galleryInfoList: List<GalleryInfo>) {
|
||||
for (gi in galleryInfoList) {
|
||||
putLocalFavorites(gi)
|
||||
}
|
||||
private suspend fun importLocalFavorites(localFavorites: List<LocalFavoriteInfo>) {
|
||||
db.localFavoritesDao().insertOrIgnore(localFavorites)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val allQuickSearch: List<QuickSearch>
|
||||
get() {
|
||||
val dao = db.quickSearchDao()
|
||||
return dao.list()
|
||||
}
|
||||
suspend fun getAllQuickSearch() = db.quickSearchDao().list()
|
||||
|
||||
@Synchronized
|
||||
fun insertQuickSearch(quickSearch: QuickSearch) {
|
||||
suspend fun insertQuickSearch(quickSearch: QuickSearch) {
|
||||
val dao = db.quickSearchDao()
|
||||
quickSearch.id = null
|
||||
quickSearch.time = System.currentTimeMillis()
|
||||
quickSearch.id = dao.insert(quickSearch)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun importQuickSearch(quickSearchList: List<QuickSearch?>) {
|
||||
private suspend fun importQuickSearch(quickSearchList: List<QuickSearch>) {
|
||||
val dao = db.quickSearchDao()
|
||||
for (quickSearch in quickSearchList) {
|
||||
dao.insert(quickSearch!!)
|
||||
}
|
||||
dao.insert(quickSearchList)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun deleteQuickSearch(quickSearch: QuickSearch?) {
|
||||
suspend fun deleteQuickSearch(quickSearch: QuickSearch) {
|
||||
val dao = db.quickSearchDao()
|
||||
if (quickSearch != null) {
|
||||
dao.delete(quickSearch)
|
||||
}
|
||||
dao.delete(quickSearch)
|
||||
dao.fill(quickSearch.position)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun moveQuickSearch(fromPosition: Int, toPosition: Int) {
|
||||
if (fromPosition == toPosition) {
|
||||
return
|
||||
}
|
||||
val reverse = fromPosition > toPosition
|
||||
val offset = if (reverse) toPosition else fromPosition
|
||||
val limit = if (reverse) fromPosition - toPosition + 1 else toPosition - fromPosition + 1
|
||||
suspend fun updateQuickSearch(quickSearchList: List<QuickSearch>) {
|
||||
val dao = db.quickSearchDao()
|
||||
val list = dao.list(offset, limit)
|
||||
val step = if (reverse) 1 else -1
|
||||
val start = if (reverse) limit - 1 else 0
|
||||
val end = if (reverse) 0 else limit - 1
|
||||
val toTime = list[end].time
|
||||
var i = end
|
||||
while (if (reverse) i < start else i > start) {
|
||||
list[i].time = list[i + step].time
|
||||
i += step
|
||||
}
|
||||
list[start].time = toTime
|
||||
dao.update(list)
|
||||
dao.update(quickSearchList)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val historyLazyList: PagingSource<Int, HistoryInfo>
|
||||
get() = db.historyDao().listLazy()
|
||||
val historyLazyList
|
||||
get() = db.historyDao().joinListLazy()
|
||||
|
||||
@Synchronized
|
||||
fun putHistoryInfo(galleryInfo: GalleryInfo?) {
|
||||
val dao = db.historyDao()
|
||||
val info: HistoryInfo = if (galleryInfo is HistoryInfo) {
|
||||
galleryInfo
|
||||
} else {
|
||||
HistoryInfo(galleryInfo!!)
|
||||
}
|
||||
info.time = System.currentTimeMillis()
|
||||
if (null != dao.load(info.gid)) {
|
||||
dao.update(info)
|
||||
} else {
|
||||
dao.insert(info)
|
||||
val localFavLazyList
|
||||
get() = db.localFavoritesDao().joinListLazy()
|
||||
|
||||
val localFavCount: Flow<Int>
|
||||
get() = db.localFavoritesDao().count()
|
||||
|
||||
fun searchLocalFav(keyword: String) = db.localFavoritesDao().joinListLazy("%$keyword%")
|
||||
|
||||
suspend fun putHistoryInfo(galleryInfo: BaseGalleryInfo) {
|
||||
putGalleryInfo(galleryInfo)
|
||||
db.historyDao().upsert(HistoryInfo(galleryInfo.gid))
|
||||
}
|
||||
|
||||
suspend fun modifyHistoryInfoFavslotNonRefresh(gid: Long, slot: Int) {
|
||||
val dao = db.galleryDao()
|
||||
dao.load(gid)?.let {
|
||||
it.favoriteSlot = slot
|
||||
dao.update(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putHistoryInfoNonRefresh(info: GalleryInfo) {
|
||||
private suspend fun importHistoryInfo(historyInfoList: List<HistoryInfo>) {
|
||||
val dao = db.historyDao()
|
||||
val i = dao.load(info.gid)
|
||||
if (null != i) {
|
||||
val historyInfo: HistoryInfo
|
||||
if (info is HistoryInfo) {
|
||||
historyInfo = info
|
||||
} else {
|
||||
historyInfo = HistoryInfo(info)
|
||||
historyInfo.time = i.time
|
||||
}
|
||||
dao.update(historyInfo)
|
||||
}
|
||||
dao.insertOrIgnore(historyInfoList)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun putHistoryInfo(historyInfoList: List<HistoryInfo>) {
|
||||
suspend fun deleteHistoryInfo(galleryInfo: BaseGalleryInfo) {
|
||||
val dao = db.historyDao()
|
||||
for (info in historyInfoList) {
|
||||
if (null == dao.load(info.gid)) {
|
||||
dao.insert(info)
|
||||
}
|
||||
}
|
||||
dao.deleteByKey(galleryInfo.gid)
|
||||
deleteGalleryInfo(galleryInfo)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun deleteHistoryInfo(info: HistoryInfo?) {
|
||||
val dao = db.historyDao()
|
||||
dao.delete(info!!)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun clearHistoryInfo() {
|
||||
suspend fun clearHistoryInfo() {
|
||||
val dao = db.historyDao()
|
||||
val historyList = dao.list()
|
||||
dao.deleteAll()
|
||||
historyList.forEach { runCatching { db.galleryDao().deleteByKey(it.gid) } }
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val allFilter: List<Filter>
|
||||
get() = db.filterDao().list()
|
||||
suspend fun getAllFilter() = db.filterDao().list()
|
||||
|
||||
@Synchronized
|
||||
fun addFilter(filter: Filter): Boolean {
|
||||
val existFilter: Filter? = try {
|
||||
db.filterDao().load(filter.text!!, filter.mode)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
suspend fun addFilter(filter: Filter): Boolean {
|
||||
val existFilter = runCatching { db.filterDao().load(filter.text, filter.mode.field) }.getOrNull()
|
||||
return if (existFilter == null) {
|
||||
filter.id = null
|
||||
filter.id = db.filterDao().insert(filter)
|
||||
@@ -350,35 +234,31 @@ object EhDB {
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun deleteFilter(filter: Filter) {
|
||||
suspend fun deleteFilter(filter: Filter) {
|
||||
db.filterDao().delete(filter)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun triggerFilter(filter: Filter) {
|
||||
filter.enable = filter.enable?.not() ?: false
|
||||
suspend fun updateFilter(filter: Filter) {
|
||||
db.filterDao().update(filter)
|
||||
}
|
||||
|
||||
private fun <T> copyDao(from: BasicDao<T>, to: BasicDao<T>) {
|
||||
val list = from.list()
|
||||
for (item in list) to.insert(item)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun exportDB(context: Context, uri: Uri): Boolean {
|
||||
suspend fun exportDB(context: Context, uri: Uri) {
|
||||
val ehExportName = "eh.export.db"
|
||||
runCatching {
|
||||
resource {
|
||||
context.deleteDatabase(ehExportName)
|
||||
val newDb = databaseBuilder(context, EhDatabase::class.java, ehExportName).build()
|
||||
copyDao(db.downloadsDao(), newDb.downloadsDao())
|
||||
copyDao(db.downloadLabelDao(), newDb.downloadLabelDao())
|
||||
copyDao(db.downloadDirnameDao(), newDb.downloadDirnameDao())
|
||||
copyDao(db.historyDao(), newDb.historyDao())
|
||||
copyDao(db.quickSearchDao(), newDb.quickSearchDao())
|
||||
copyDao(db.localFavoritesDao(), newDb.localFavoritesDao())
|
||||
copyDao(db.filterDao(), newDb.filterDao())
|
||||
roomDb<EhDatabase>(ehExportName)
|
||||
} release {
|
||||
it.close()
|
||||
context.deleteDatabase(ehExportName)
|
||||
} use { newDb ->
|
||||
db.galleryDao().list().let { newDb.galleryDao().insertOrIgnore(it) }
|
||||
db.downloadLabelDao().list().let { newDb.downloadLabelDao().insert(it) }
|
||||
db.downloadDirnameDao().list().let { newDb.downloadDirnameDao().insertOrIgnore(it) }
|
||||
db.downloadsDao().list().let { newDb.downloadsDao().insert(it) }
|
||||
db.historyDao().list().let { newDb.historyDao().insertOrIgnore(it) }
|
||||
db.quickSearchDao().list().let { newDb.quickSearchDao().insert(it) }
|
||||
db.localFavoritesDao().list().let { newDb.localFavoritesDao().insertOrIgnore(it) }
|
||||
db.filterDao().list().let { newDb.filterDao().insert(it) }
|
||||
newDb.close()
|
||||
val dbFile = context.getDatabasePath(ehExportName)
|
||||
context.contentResolver.openFileDescriptor(uri, "rw")!!.use { toFd ->
|
||||
@@ -386,68 +266,52 @@ object EhDB {
|
||||
fromFd sendTo toFd
|
||||
}
|
||||
}
|
||||
return true
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @return error string, null for no error
|
||||
*/
|
||||
@Synchronized
|
||||
fun importDB(context: Context, uri: Uri): String? {
|
||||
runCatching {
|
||||
val oldDB = databaseBuilder(context, EhDatabase::class.java, "tmp.db")
|
||||
.createFromInputStream { context.contentResolver.openInputStream(uri) }
|
||||
.addMigrations(EHDB_MIGRATION_4_5)
|
||||
.build()
|
||||
|
||||
// Download label
|
||||
val manager = DownloadManager
|
||||
suspend fun importDB(context: Context, uri: Uri) {
|
||||
val tempDBName = "tmp.db"
|
||||
resource {
|
||||
context.deleteDatabase(tempDBName)
|
||||
roomDb<EhDatabase>(tempDBName) { createFromInputStream { context.contentResolver.openInputStream(uri) } }
|
||||
} release {
|
||||
it.close()
|
||||
context.deleteDatabase(tempDBName)
|
||||
} use { oldDB ->
|
||||
runCatching {
|
||||
db.galleryDao().insertOrIgnore(oldDB.galleryDao().list())
|
||||
}
|
||||
runCatching {
|
||||
val downloadLabelList = oldDB.downloadLabelDao().list()
|
||||
manager.addDownloadLabel(downloadLabelList)
|
||||
DownloadManager.addDownloadLabel(downloadLabelList)
|
||||
}
|
||||
|
||||
// Downloads
|
||||
runCatching {
|
||||
val downloadInfoList = oldDB.downloadsDao().list()
|
||||
manager.addDownload(downloadInfoList, false)
|
||||
}
|
||||
|
||||
// Download dirname
|
||||
runCatching {
|
||||
oldDB.downloadDirnameDao().list().forEach {
|
||||
putDownloadDirname(it.gid, it.dirname)
|
||||
oldDB.downloadDirnameDao().list().let {
|
||||
importDownloadDirname(it)
|
||||
}
|
||||
}
|
||||
|
||||
// History
|
||||
runCatching {
|
||||
val downloadInfoList = oldDB.downloadsDao().joinList().asReversed()
|
||||
DownloadManager.addDownload(downloadInfoList, false)
|
||||
}
|
||||
runCatching {
|
||||
val historyInfoList = oldDB.historyDao().list()
|
||||
putHistoryInfo(historyInfoList)
|
||||
importHistoryInfo(historyInfoList)
|
||||
}
|
||||
|
||||
// QuickSearch
|
||||
runCatching {
|
||||
val quickSearchList = oldDB.quickSearchDao().list()
|
||||
val currentQuickSearchList = db.quickSearchDao().list()
|
||||
val importList = quickSearchList.mapNotNull { newQS ->
|
||||
newQS.takeIf { currentQuickSearchList.find { it.name == newQS.name } == null }
|
||||
}
|
||||
val offset = currentQuickSearchList.size
|
||||
val importList = quickSearchList.filter { newQS ->
|
||||
currentQuickSearchList.none { it.name == newQS.name }
|
||||
}.onEachIndexed { index, q -> q.position = index + offset }
|
||||
importQuickSearch(importList)
|
||||
}
|
||||
|
||||
// LocalFavorites
|
||||
runCatching {
|
||||
oldDB.localFavoritesDao().list().forEach {
|
||||
putLocalFavorites(it)
|
||||
oldDB.localFavoritesDao().list().let {
|
||||
importLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter
|
||||
runCatching {
|
||||
val filterList = oldDB.filterDao().list()
|
||||
val currentFilterList = db.filterDao().list()
|
||||
@@ -455,11 +319,6 @@ object EhDB {
|
||||
if (it !in currentFilterList) addFilter(it)
|
||||
}
|
||||
}
|
||||
oldDB.close()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
return context.getString(R.string.cant_read_the_file)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.hippo.ehviewer.Settings.proxyIp
|
||||
import com.hippo.ehviewer.Settings.proxyPort
|
||||
import com.hippo.ehviewer.Settings.proxyType
|
||||
import com.hippo.ehviewer.network.InetValidator.isValidInetPort
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.net.ProxySelector
|
||||
import java.net.SocketAddress
|
||||
import java.net.URI
|
||||
|
||||
class EhProxySelector internal constructor() : ProxySelector() {
|
||||
private var delegation: ProxySelector? = null
|
||||
private var alternative: ProxySelector?
|
||||
|
||||
init {
|
||||
alternative = getDefault() ?: NullProxySelector()
|
||||
updateProxy()
|
||||
}
|
||||
|
||||
fun updateProxy() {
|
||||
delegation = when (proxyType) {
|
||||
TYPE_DIRECT -> NullProxySelector()
|
||||
TYPE_SYSTEM -> alternative
|
||||
TYPE_HTTP, TYPE_SOCKS -> null
|
||||
else -> alternative
|
||||
}
|
||||
}
|
||||
|
||||
override fun select(uri: URI): List<Proxy> {
|
||||
val type = proxyType
|
||||
if (type == TYPE_HTTP || type == TYPE_SOCKS) {
|
||||
runCatching {
|
||||
val ip = proxyIp
|
||||
val port = proxyPort
|
||||
if (!TextUtils.isEmpty(ip) && isValidInetPort(port)) {
|
||||
val inetAddress = InetAddress.getByName(ip)
|
||||
val socketAddress = InetSocketAddress(inetAddress, port)
|
||||
return listOf(
|
||||
Proxy(
|
||||
if (type == TYPE_HTTP) Proxy.Type.HTTP else Proxy.Type.SOCKS,
|
||||
socketAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
}.onFailure {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
return delegation?.select(uri) ?: alternative!!.select(uri)
|
||||
}
|
||||
|
||||
override fun connectFailed(uri: URI, sa: SocketAddress, ioe: IOException) {
|
||||
delegation?.select(uri)
|
||||
}
|
||||
|
||||
private class NullProxySelector : ProxySelector() {
|
||||
override fun select(uri: URI): List<Proxy> {
|
||||
return listOf(Proxy.NO_PROXY)
|
||||
}
|
||||
|
||||
override fun connectFailed(uri: URI, sa: SocketAddress, ioe: IOException) {}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_DIRECT = 0
|
||||
const val TYPE_SYSTEM = 1
|
||||
const val TYPE_HTTP = 2
|
||||
const val TYPE_SOCKS = 3
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.yorozuya.IntIdGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FavouriteStatusRouter {
|
||||
|
||||
private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id";
|
||||
|
||||
private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0));
|
||||
@SuppressLint("UseSparseArrays")
|
||||
private final HashMap<Integer, Map<Long, GalleryInfo>> maps = new HashMap<>();
|
||||
|
||||
private final List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
public int saveDataMap(Map<Long, GalleryInfo> map) {
|
||||
int id = idGenerator.nextId();
|
||||
maps.put(id, map);
|
||||
Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId());
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<Long, GalleryInfo> restoreDataMap(int id) {
|
||||
return maps.remove(id);
|
||||
}
|
||||
|
||||
public void modifyFavourites(long gid, int slot) {
|
||||
for (Map<Long, GalleryInfo> map : maps.values()) {
|
||||
GalleryInfo info = map.get(gid);
|
||||
if (info != null) {
|
||||
info.setFavoriteSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
for (Listener listener : listeners) {
|
||||
listener.onModifyFavourites(gid, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onModifyFavourites(long gid, int slot);
|
||||
}
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
/*
|
||||
* Created by Hippo on 2018/3/21.
|
||||
*/
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.database.MSQLiteBuilder;
|
||||
import com.hippo.ehviewer.util.SqlUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Hosts {
|
||||
|
||||
private static final int VERSION_1 = 1;
|
||||
private static final String TABLE_HOSTS = "HOSTS";
|
||||
private static final String COLUMN_HOST = "HOST";
|
||||
private static final String COLUMN_IP = "IP";
|
||||
|
||||
private static final int DB_VERSION = VERSION_1;
|
||||
|
||||
private final SQLiteDatabase db;
|
||||
|
||||
public Hosts(Context context, String name) {
|
||||
SQLiteOpenHelper helper = new MSQLiteBuilder()
|
||||
.version(VERSION_1)
|
||||
.createTable(TABLE_HOSTS)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_IP, String.class)
|
||||
.build(context, name, DB_VERSION);
|
||||
db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InetAddress toInetAddress(String host, String ip) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ip == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = parseV4(ip);
|
||||
if (bytes == null) {
|
||||
bytes = parseV6(ip);
|
||||
}
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return InetAddress.getByAddress(host, bytes);
|
||||
} catch (UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the host is valid.
|
||||
*/
|
||||
public static boolean isValidHost(String host) {
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host.length() > 253) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int labelLength = 0;
|
||||
for (int i = 0, n = host.length(); i < n; i++) {
|
||||
char ch = host.charAt(i);
|
||||
|
||||
if (ch == '.') {
|
||||
if (labelLength < 1 || labelLength > 63) {
|
||||
return false;
|
||||
}
|
||||
labelLength = 0;
|
||||
} else {
|
||||
labelLength++;
|
||||
}
|
||||
|
||||
if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return labelLength >= 1 && labelLength <= 63;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if ips are valid.
|
||||
*/
|
||||
public static boolean isValidIp(String ips) {
|
||||
if (ips == null) {
|
||||
return false;
|
||||
}
|
||||
String[] list = ips.split("\\+");
|
||||
for (String ip : list) {
|
||||
if (parseV4(ip) == null && parseV6(ip) == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV4
|
||||
@Nullable
|
||||
private static byte[] parseV4(String s) {
|
||||
int numDigits;
|
||||
int currentOctet;
|
||||
byte[] values = new byte[4];
|
||||
int currentValue;
|
||||
int length = s.length();
|
||||
|
||||
currentOctet = 0;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
/* Can't have more than 3 digits per octet. */
|
||||
if (numDigits == 3)
|
||||
return null;
|
||||
/* Octets shouldn't start with 0, unless they are 0. */
|
||||
if (numDigits > 0 && currentValue == 0)
|
||||
return null;
|
||||
numDigits++;
|
||||
currentValue *= 10;
|
||||
currentValue += (c - '0');
|
||||
/* 255 is the maximum value for an octet. */
|
||||
if (currentValue > 255)
|
||||
return null;
|
||||
} else if (c == '.') {
|
||||
/* Can't have more than 3 dots. */
|
||||
if (currentOctet == 3)
|
||||
return null;
|
||||
/* Two consecutive dots are bad. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet++] = (byte) currentValue;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
/* Must have 4 octets. */
|
||||
if (currentOctet != 3)
|
||||
return null;
|
||||
/* The fourth octet can't be empty. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet] = (byte) currentValue;
|
||||
return values;
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV6
|
||||
@Nullable
|
||||
private static byte[] parseV6(String s) {
|
||||
int range = -1;
|
||||
byte[] data = new byte[16];
|
||||
|
||||
String[] tokens = s.split(":", -1);
|
||||
|
||||
int first = 0;
|
||||
int last = tokens.length - 1;
|
||||
|
||||
if (tokens[0].length() == 0) {
|
||||
// If the first two tokens are empty, it means the string
|
||||
// started with ::, which is fine. If only the first is
|
||||
// empty, the string started with :, which is bad.
|
||||
if (last - first > 0 && tokens[1].length() == 0)
|
||||
first++;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tokens[last].length() == 0) {
|
||||
// If the last two tokens are empty, it means the string
|
||||
// ended with ::, which is fine. If only the last is
|
||||
// empty, the string ended with :, which is bad.
|
||||
if (last - first > 0 && tokens[last - 1].length() == 0)
|
||||
last--;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (last - first + 1 > 8)
|
||||
return null;
|
||||
|
||||
int i, j;
|
||||
for (i = first, j = 0; i <= last; i++) {
|
||||
if (tokens[i].length() == 0) {
|
||||
if (range >= 0)
|
||||
return null;
|
||||
range = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens[i].indexOf('.') >= 0) {
|
||||
// An IPv4 address must be the last component
|
||||
if (i < last)
|
||||
return null;
|
||||
// There can't have been more than 6 components.
|
||||
if (i > 6)
|
||||
return null;
|
||||
byte[] v4addr = parseV4(tokens[i]);
|
||||
if (v4addr == null)
|
||||
return null;
|
||||
for (int k = 0; k < 4; k++)
|
||||
data[j++] = v4addr[k];
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
for (int k = 0; k < tokens[i].length(); k++) {
|
||||
char c = tokens[i].charAt(k);
|
||||
if (Character.digit(c, 16) < 0)
|
||||
return null;
|
||||
}
|
||||
int x = Integer.parseInt(tokens[i], 16);
|
||||
if (x > 0xFFFF || x < 0)
|
||||
return null;
|
||||
data[j++] = (byte) (x >>> 8);
|
||||
data[j++] = (byte) (x & 0xFF);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < 16 && range < 0)
|
||||
return null;
|
||||
|
||||
if (range >= 0) {
|
||||
int empty = 16 - j;
|
||||
System.arraycopy(data, range, data, range + empty, j - range);
|
||||
for (i = range; i < range + empty; i++)
|
||||
data[i] = 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a InetAddress with the host.
|
||||
*/
|
||||
@Nullable
|
||||
public List<InetAddress> get(String host) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
}
|
||||
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host})) {
|
||||
if (cursor.moveToNext()) {
|
||||
String[] ips = SqlUtils.getString(cursor, COLUMN_IP, null).split("\\+");
|
||||
List<InetAddress> addresses = new ArrayList<>();
|
||||
for (String ip : ips) {
|
||||
addresses.add(toInetAddress(host, ip));
|
||||
}
|
||||
return addresses;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(String host) {
|
||||
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host})) {
|
||||
return cursor.moveToNext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the host-ip pair into this hosts.
|
||||
*/
|
||||
public boolean put(String host, String ip) {
|
||||
if (!isValidHost(host) || !isValidIp(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_HOST, host);
|
||||
values.put(COLUMN_IP, ip);
|
||||
|
||||
if (contains(host)) {
|
||||
db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[]{host});
|
||||
} else {
|
||||
db.insert(TABLE_HOSTS, null, values);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts delete the entry with the host.
|
||||
*/
|
||||
public void delete(String host) {
|
||||
db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[]{host});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from this host.
|
||||
*/
|
||||
public List<Pair<String, String>> getAll() {
|
||||
List<Pair<String, String>> result = new ArrayList<>();
|
||||
|
||||
try (Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
String host = SqlUtils.getString(cursor, COLUMN_HOST, null);
|
||||
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
|
||||
result.add(new Pair<>(host, ip));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,641 +1,124 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.hippo.ehviewer.EhApplication.Companion.application
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.ui.scene.GalleryListScene
|
||||
import com.hippo.ehviewer.yorozuya.LayoutUtils.dp2pix
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.unifile.UniFile
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import splitties.preferences.DataStorePreferences
|
||||
import splitties.preferences.edit
|
||||
import java.util.Locale
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
object Settings {
|
||||
/********************
|
||||
* Eh
|
||||
*/
|
||||
const val KEY_THEME = "theme"
|
||||
const val KEY_ACCOUNT = "account"
|
||||
const val KEY_IMAGE_LIMITS = "image_limits"
|
||||
const val KEY_U_CONFIG = "uconfig"
|
||||
const val KEY_MY_TAGS = "mytags"
|
||||
const val KEY_BLACK_DARK_THEME = "black_dark_theme"
|
||||
const val DEFAULT_BLACK_DARK_THEME = false
|
||||
const val THEME_SYSTEM = -1
|
||||
const val KEY_GALLERY_SITE = "gallery_site"
|
||||
const val KEY_LIST_MODE = "list_mode"
|
||||
const val KEY_DETAIL_SIZE = "detail_size"
|
||||
const val KEY_THUMB_SIZE = "thumb_size_"
|
||||
const val KEY_THUMB_RESOLUTION = "thumb_resolution"
|
||||
const val KEY_SHOW_JPN_TITLE = "show_jpn_title"
|
||||
const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||
const val KEY_TAG_TRANSLATIONS_SOURCE = "tag_translations_source"
|
||||
const val KEY_REQUEST_NEWS = "request_news"
|
||||
const val KEY_REQUEST_NEWS_TIMER = "request_news_timer"
|
||||
const val KEY_REQUEST_NEWS_TIMER_HOUR = "request_news_timer_hour"
|
||||
const val KEY_REQUEST_NEWS_TIMER_MINUTE = "request_news_timer_minute"
|
||||
const val KEY_HIDE_HV_EVENTS = "hide_hv_events"
|
||||
val SIGN_IN_REQUIRED = arrayOf(
|
||||
KEY_IMAGE_LIMITS,
|
||||
KEY_GALLERY_SITE,
|
||||
KEY_U_CONFIG,
|
||||
KEY_MY_TAGS,
|
||||
KEY_SHOW_JPN_TITLE,
|
||||
KEY_REQUEST_NEWS,
|
||||
KEY_REQUEST_NEWS_TIMER,
|
||||
KEY_HIDE_HV_EVENTS,
|
||||
)
|
||||
object Settings : DataStorePreferences(null) {
|
||||
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||
|
||||
/********************
|
||||
* Privacy and Security
|
||||
*/
|
||||
const val KEY_SEC_SECURITY = "enable_secure"
|
||||
const val VALUE_SEC_SECURITY = false
|
||||
private val _favFlow = MutableSharedFlow<Unit>()
|
||||
val favChangesFlow = _favFlow.debounce(1000)
|
||||
var favCat by stringArrayPref("fav_cat", 10, "Favorites").emitTo(_favFlow)
|
||||
var favCount by intArrayPref("fav_count", 10).emitTo(_favFlow)
|
||||
var favCloudCount by intPref("fav_cloud", 0).emitTo(_favFlow)
|
||||
|
||||
/********************
|
||||
* Download
|
||||
*/
|
||||
const val KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme"
|
||||
const val KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority"
|
||||
const val KEY_DOWNLOAD_SAVE_PATH = "image_path"
|
||||
const val KEY_NOTIFICATION_REQUIRED = "notification_required"
|
||||
const val KEY_DOWNLOAD_SAVE_QUERY = "image_query"
|
||||
const val KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment"
|
||||
const val KEY_MEDIA_SCAN = "media_scan"
|
||||
const val INVALID_DEFAULT_FAV_SLOT = -2
|
||||
var downloadScheme by stringOrNullPref("image_scheme", null)
|
||||
var downloadAuthority by stringOrNullPref("image_authority", null)
|
||||
var downloadPath by stringOrNullPref("image_path", null)
|
||||
var downloadQuery by stringOrNullPref("image_query", null)
|
||||
var downloadFragment by stringOrNullPref("image_fragment", null)
|
||||
var archivePasswds by stringSetOrNullPref("archive_passwds")
|
||||
var downloadDelay by intFromStrPref("download_delay", 0)
|
||||
var gallerySite by intFromStrPref("gallery_site", 0).observed { updateWhenGallerySiteChanges() }
|
||||
var multiThreadDownload by intFromStrPref("download_thread", 3)
|
||||
var preloadImage by intFromStrPref("preload_image", 5)
|
||||
var theme by intFromStrPref("theme", -1).observed { updateWhenThemeChanges() }
|
||||
var listMode by intFromStrPref("list_mode", 0)
|
||||
var detailSize by intFromStrPref("detail_size", 0)
|
||||
var thumbResolution by intFromStrPref("thumb_resolution", 0)
|
||||
var readCacheSize by intFromStrPref("read_cache_size", 640)
|
||||
var launchPage by intFromStrPref("launch_page", 0)
|
||||
var forceEhThumb by boolPref("force_eh_thumb", false)
|
||||
var showComments by boolPref("show_gallery_comments", true)
|
||||
var requestNews by boolPref("request_news", false).observed { updateWhenRequestNewsChanges() }
|
||||
var hideHvEvents by boolPref("hide_hv_events", false)
|
||||
var showJpnTitle by boolPref("show_jpn_title", false)
|
||||
var showGalleryPages by boolPref("show_gallery_pages", false)
|
||||
var showTagTranslations by boolPref(KEY_SHOW_TAG_TRANSLATIONS, false).observed { updateWhenTagTranslationChanges() }
|
||||
var meteredNetworkWarning by boolPref("cellular_network_warning", false)
|
||||
var appLinkVerifyTip by boolPref("app_link_verify_tip", false)
|
||||
var enabledSecurity by boolPref("enable_secure", false)
|
||||
var useCIUpdateChannel by boolPref("ci_update_channel", false)
|
||||
var mediaScan by boolPref("media_scan", false).observed { updateWhenKeepMediaStatusChanges() }
|
||||
var hasDefaultDownloadLabel by boolPref("has_default_download_label", false)
|
||||
var qSSaveProgress by boolPref("qs_save_progress", true)
|
||||
var saveParseErrorBody by boolPref("save_parse_error_body", true)
|
||||
var saveCrashLog by boolPref("save_crash_log", true)
|
||||
var security by boolPref("require_unlock", false)
|
||||
var removeImageFiles by boolPref("include_pic", true)
|
||||
var needSignIn by boolPref("need_sign_in", true)
|
||||
var blackDarkTheme by boolPref("black_dark_theme", false).observed { updateWhenAmoledModeChanges() }
|
||||
var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false)
|
||||
var downloadOriginImage by boolPref("download_origin_image", false)
|
||||
var enableQuic by boolPref("enable_quic", true)
|
||||
var thumbSizeDp by intPref("thumb_size_", 120)
|
||||
var recentFavCat by intPref("recent_fav_cat", FavListUrlBuilder.FAV_CAT_LOCAL)
|
||||
var defaultFavSlot by intPref("default_favorite_slot", -2)
|
||||
var securityDelay by intPref("require_unlock_delay", 0)
|
||||
var clipboardTextHashCode by intPref("clipboard_text_hash_code", 0)
|
||||
var listThumbSize by intPref("list_tile_size", 40)
|
||||
var searchCategory by intPref("search_pref", EhUtils.ALL_CATEGORY)
|
||||
var requestNewsTimerHour by intPref("request_news_timer_hour", -1)
|
||||
var requestNewsTimerMinute by intPref("request_news_timer_minute", -1)
|
||||
var updateIntervalDays by intPref("update_interval_days", 0)
|
||||
var recentDownloadLabel by stringOrNullPref("recent_download_label", null)
|
||||
var defaultDownloadLabel by stringOrNullPref("default_download_label", null)
|
||||
var displayName by stringOrNullPref("display_name", null)
|
||||
var avatar by stringOrNullPref("avatar", null)
|
||||
var language by stringPref("app_language", "system").observed { updateWhenLocaleChanges() }
|
||||
var lastDawnDay by longPref("last_dawn_day", 0)
|
||||
var lastUpdateDay by longPref("last_update_day", 0)
|
||||
|
||||
/********************
|
||||
* Advanced
|
||||
*/
|
||||
const val KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body"
|
||||
const val KEY_SECURITY = "require_unlock"
|
||||
const val KEY_SECURITY_DELAY = "require_unlock_delay"
|
||||
const val KEY_READ_CACHE_SIZE = "read_cache_size"
|
||||
const val DEFAULT_READ_CACHE_SIZE = 640
|
||||
const val KEY_BUILT_IN_HOSTS = "built_in_hosts_2"
|
||||
const val KEY_DOMAIN_FRONTING = "domain_fronting"
|
||||
const val KEY_BYPASS_VPN = "bypass_vpn"
|
||||
const val KEY_LIST_THUMB_SIZE = "list_tile_size"
|
||||
private const val KEY_LAST_DAWN_DAY = "last_dawn_day"
|
||||
private const val DEFAULT_HIDE_HV_EVENTS = false
|
||||
private const val KEY_SHOW_COMMENTS = "show_gallery_comments"
|
||||
private const val DEFAULT_SHOW_COMMENTS = true
|
||||
private val TAG = Settings::class.java.simpleName
|
||||
private const val KEY_DISPLAY_NAME = "display_name"
|
||||
private val DEFAULT_DISPLAY_NAME: String? = null
|
||||
private const val DEFAULT_LIST_THUMB_SIZE = 40
|
||||
private const val KEY_AVATAR = "avatar"
|
||||
private const val KEY_REMOVE_IMAGE_FILES = "include_pic"
|
||||
private const val DEFAULT_REMOVE_IMAGE_FILES = true
|
||||
private const val KEY_NEED_SIGN_IN = "need_sign_in"
|
||||
private const val DEFAULT_NEED_SIGN_IN = true
|
||||
private const val KEY_SELECT_SITE = "select_site"
|
||||
private const val DEFAULT_SELECT_SITE = true
|
||||
private const val DEFAULT_THEME = THEME_SYSTEM
|
||||
private const val DEFAULT_GALLERY_SITE = 0
|
||||
private const val KEY_LAUNCH_PAGE = "launch_page"
|
||||
private const val DEFAULT_LAUNCH_PAGE = 0
|
||||
private const val DEFAULT_LIST_MODE = 0
|
||||
private const val DEFAULT_DETAIL_SIZE = 0
|
||||
private const val DEFAULT_THUMB_SIZE = 120
|
||||
private const val DEFAULT_THUMB_RESOLUTION = 0
|
||||
private const val DEFAULT_SHOW_JPN_TITLE = false
|
||||
private const val KEY_SHOW_GALLERY_PAGES = "show_gallery_pages"
|
||||
private const val DEFAULT_SHOW_GALLERY_PAGES = false
|
||||
private const val DEFAULT_SHOW_TAG_TRANSLATIONS = false
|
||||
private const val KEY_METERED_NETWORK_WARNING = "cellular_network_warning"
|
||||
private const val DEFAULT_METERED_NETWORK_WARNING = false
|
||||
private const val KEY_APP_LINK_VERIFY_TIP = "app_link_verify_tip"
|
||||
private const val DEFAULT_APP_LINK_VERIFY_TIP = false
|
||||
private const val DEFAULT_MEDIA_SCAN = false
|
||||
private const val KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label"
|
||||
private val DEFAULT_RECENT_DOWNLOAD_LABEL: String? = null
|
||||
private const val KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label"
|
||||
private const val DEFAULT_HAS_DOWNLOAD_LABEL = false
|
||||
private const val KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label"
|
||||
private val DEFAULT_DOWNLOAD_LABEL: String? = null
|
||||
private const val KEY_MULTI_THREAD_DOWNLOAD = "download_thread"
|
||||
private const val DEFAULT_MULTI_THREAD_DOWNLOAD = 3
|
||||
private const val KEY_PRELOAD_IMAGE = "preload_image"
|
||||
private const val DEFAULT_PRELOAD_IMAGE = 5
|
||||
private const val KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image"
|
||||
private const val DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false
|
||||
|
||||
/********************
|
||||
* Favorites
|
||||
*/
|
||||
private const val KEY_FAV_CAT_0 = "fav_cat_0"
|
||||
private const val KEY_FAV_CAT_1 = "fav_cat_1"
|
||||
private const val KEY_FAV_CAT_2 = "fav_cat_2"
|
||||
private const val KEY_FAV_CAT_3 = "fav_cat_3"
|
||||
private const val KEY_FAV_CAT_4 = "fav_cat_4"
|
||||
private const val KEY_FAV_CAT_5 = "fav_cat_5"
|
||||
private const val KEY_FAV_CAT_6 = "fav_cat_6"
|
||||
private const val KEY_FAV_CAT_7 = "fav_cat_7"
|
||||
private const val KEY_FAV_CAT_8 = "fav_cat_8"
|
||||
private const val KEY_FAV_CAT_9 = "fav_cat_9"
|
||||
private const val DEFAULT_FAV_CAT_0 = "Favorites 0"
|
||||
private const val DEFAULT_FAV_CAT_1 = "Favorites 1"
|
||||
private const val DEFAULT_FAV_CAT_2 = "Favorites 2"
|
||||
private const val DEFAULT_FAV_CAT_3 = "Favorites 3"
|
||||
private const val DEFAULT_FAV_CAT_4 = "Favorites 4"
|
||||
private const val DEFAULT_FAV_CAT_5 = "Favorites 5"
|
||||
private const val DEFAULT_FAV_CAT_6 = "Favorites 6"
|
||||
private const val DEFAULT_FAV_CAT_7 = "Favorites 7"
|
||||
private const val DEFAULT_FAV_CAT_8 = "Favorites 8"
|
||||
private const val DEFAULT_FAV_CAT_9 = "Favorites 9"
|
||||
private const val KEY_FAV_COUNT_0 = "fav_count_0"
|
||||
private const val KEY_FAV_COUNT_1 = "fav_count_1"
|
||||
private const val KEY_FAV_COUNT_2 = "fav_count_2"
|
||||
private const val KEY_FAV_COUNT_3 = "fav_count_3"
|
||||
private const val KEY_FAV_COUNT_4 = "fav_count_4"
|
||||
private const val KEY_FAV_COUNT_5 = "fav_count_5"
|
||||
private const val KEY_FAV_COUNT_6 = "fav_count_6"
|
||||
private const val KEY_FAV_COUNT_7 = "fav_count_7"
|
||||
private const val KEY_FAV_COUNT_8 = "fav_count_8"
|
||||
private const val KEY_FAV_COUNT_9 = "fav_count_9"
|
||||
private const val KEY_FAV_LOCAL = "fav_local"
|
||||
private const val KEY_FAV_CLOUD = "fav_cloud"
|
||||
private const val DEFAULT_FAV_COUNT = 0
|
||||
private const val KEY_RECENT_FAV_CAT = "recent_fav_cat"
|
||||
private const val DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL
|
||||
|
||||
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
|
||||
private const val KEY_DEFAULT_FAV_SLOT = "default_favorite_2"
|
||||
private const val DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT
|
||||
private const val DEFAULT_SAVE_PARSE_ERROR_BODY = true
|
||||
private const val KEY_SAVE_CRASH_LOG = "save_crash_log"
|
||||
private const val DEFAULT_SAVE_CRASH_LOG = true
|
||||
private const val DEFAULT_BUILT_IN_HOSTS = false
|
||||
private const val DEFAULT_FRONTING = false
|
||||
private const val DEFAULT_BYPASS_VPN = true
|
||||
private const val KEY_PROXY_TYPE = "proxy_type"
|
||||
private const val DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM
|
||||
private const val KEY_PROXY_IP = "proxy_ip"
|
||||
private val DEFAULT_PROXY_IP: String? = null
|
||||
private const val KEY_PROXY_PORT = "proxy_port"
|
||||
private const val DEFAULT_PROXY_PORT = -1
|
||||
private const val KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code"
|
||||
private const val DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0
|
||||
private const val KEY_DOWNLOAD_DELAY = "download_delay"
|
||||
private const val DEFAULT_DOWNLOAD_DELAY = 0
|
||||
private const val DEFAULT_REQUEST_NEWS = false
|
||||
private const val KEY_ARCHIVE_PASSWDS = "archive_passwds"
|
||||
private const val KEY_QS_SAVE_PROGRESS = "qs_save_progress"
|
||||
private const val DEFAULT_QS_SAVE_PROGRESS = true
|
||||
private const val KEY_PRELOAD_THUMB_AGGRESIVELY = "preload_thumb_aggressively"
|
||||
private const val DEFAULT_PRELOAD_THUMB_AGGRESIVELY = false
|
||||
var LIST_THUMB_SIZE_INITED = false
|
||||
private lateinit var sSettingsPre: SharedPreferences
|
||||
private var LIST_THUMB_SIZE = 40
|
||||
fun initialize() {
|
||||
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(application)
|
||||
fixDefaultValue()
|
||||
}
|
||||
|
||||
private fun fixDefaultValue() {
|
||||
init {
|
||||
if ("CN" == Locale.getDefault().country) {
|
||||
// Enable domain fronting if the country is CN
|
||||
if (!sSettingsPre.contains(KEY_BUILT_IN_HOSTS)) {
|
||||
putBuiltInHosts(true)
|
||||
}
|
||||
if (!sSettingsPre.contains(KEY_DOMAIN_FRONTING)) {
|
||||
putDF(true)
|
||||
}
|
||||
// Enable show tag translations if the country is CN
|
||||
if (!sSettingsPre.contains(KEY_SHOW_TAG_TRANSLATIONS)) {
|
||||
putShowTagTranslations(true)
|
||||
edit {
|
||||
if (KEY_SHOW_TAG_TRANSLATIONS !in prefs) showTagTranslations = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBoolean(key: String, defValue: Boolean): Boolean {
|
||||
return try {
|
||||
sSettingsPre.getBoolean(key, defValue)
|
||||
} catch (e: ClassCastException) {
|
||||
Log.d(TAG, "Get ClassCastException when get $key value", e)
|
||||
defValue
|
||||
interface Delegate<R> {
|
||||
val flowGetter: () -> Flow<Unit>
|
||||
operator fun getValue(thisRef: Any?, prop: KProperty<*>?): R
|
||||
operator fun setValue(thisRef: Any?, prop: KProperty<*>?, value: R)
|
||||
}
|
||||
|
||||
private fun intFromStrPref(key: String, defValue: Int) = object : Delegate<Int> {
|
||||
override val flowGetter: () -> Flow<Unit>
|
||||
private var _value by stringPref(key, defValue.toString()).also { flowGetter = { it.changesFlow() } }
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?) = _value.toIntOrNull() ?: defValue
|
||||
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: Int) { _value = value.toString() }
|
||||
}
|
||||
|
||||
private fun intArrayPref(key: String, count: Int) = object : Delegate<IntArray> {
|
||||
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
|
||||
private var _value = (0 until count).map { intPref("${key}_$it", 0) }.toTypedArray()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?): IntArray = _value.map { it.value }.toIntArray()
|
||||
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: IntArray) {
|
||||
check(value.size == count)
|
||||
edit { value.zip(_value) { v, d -> d.value = v } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun putBoolean(key: String, value: Boolean) {
|
||||
sSettingsPre.edit().putBoolean(key, value).apply()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getInt(key: String, defValue: Int): Int {
|
||||
return try {
|
||||
sSettingsPre.getInt(key, defValue)
|
||||
} catch (e: ClassCastException) {
|
||||
Log.d(TAG, "Get ClassCastException when get $key value", e)
|
||||
defValue
|
||||
private fun stringArrayPref(key: String, count: Int, defMetaValue: String) = object : Delegate<Array<String>> {
|
||||
override val flowGetter: () -> Flow<Unit> = { _value.asFlow().flatMapMerge { it.changesFlow() }.conflate() }
|
||||
private var _value = (0 until count).map { stringPref("${key}_$it", "$defMetaValue $it") }.toTypedArray()
|
||||
override fun getValue(thisRef: Any?, prop: KProperty<*>?): Array<String> = _value.map { it.value }.toTypedArray()
|
||||
override fun setValue(thisRef: Any?, prop: KProperty<*>?, value: Array<String>) {
|
||||
check(value.size == count)
|
||||
edit { value.zip(_value) { v, d -> d.value = v } }
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun putInt(key: String, value: Int) {
|
||||
sSettingsPre.edit().putInt(key, value).apply()
|
||||
}
|
||||
|
||||
private fun getString(key: String, defValue: String?): String? {
|
||||
return try {
|
||||
sSettingsPre.getString(key, defValue)
|
||||
} catch (e: ClassCastException) {
|
||||
Log.d(TAG, "Get ClassCastException when get $key value", e)
|
||||
defValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun putString(key: String, value: String?) {
|
||||
sSettingsPre.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
private fun getStringSet(key: String): MutableSet<String>? {
|
||||
return sSettingsPre.getStringSet(key, null)
|
||||
}
|
||||
|
||||
private fun putStringToStringSet(key: String, value: String) {
|
||||
var set = getStringSet(key)
|
||||
if (set == null) {
|
||||
set =
|
||||
mutableSetOf(value)
|
||||
} else if (set.contains(value)) return else set.add(value)
|
||||
sSettingsPre.edit().putStringSet(key, set).apply()
|
||||
}
|
||||
|
||||
private fun getIntFromStr(key: String, defValue: Int): Int {
|
||||
return try {
|
||||
NumberUtils.parseIntSafely(
|
||||
sSettingsPre.getString(key, defValue.toString()),
|
||||
defValue,
|
||||
)
|
||||
} catch (e: ClassCastException) {
|
||||
Log.d(TAG, "Get ClassCastException when get $key value", e)
|
||||
defValue
|
||||
}
|
||||
}
|
||||
|
||||
private fun putIntToStr(key: String, value: Int) {
|
||||
sSettingsPre.edit().putString(key, value.toString()).apply()
|
||||
}
|
||||
|
||||
var lastDawnDay: Long
|
||||
get() = sSettingsPre.getLong(KEY_LAST_DAWN_DAY, 0)
|
||||
set(value) = sSettingsPre.edit().putLong(KEY_LAST_DAWN_DAY, value).apply()
|
||||
val displayName: String?
|
||||
get() = getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME)
|
||||
|
||||
fun putDisplayName(value: String?) {
|
||||
putString(KEY_DISPLAY_NAME, value)
|
||||
}
|
||||
|
||||
fun putAvatar(value: String?) {
|
||||
putString(KEY_AVATAR, value)
|
||||
}
|
||||
|
||||
val removeImageFiles: Boolean
|
||||
get() = getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES)
|
||||
|
||||
fun putRemoveImageFiles(value: Boolean) {
|
||||
putBoolean(KEY_REMOVE_IMAGE_FILES, value)
|
||||
}
|
||||
|
||||
val needSignIn: Boolean
|
||||
get() = getBoolean(KEY_NEED_SIGN_IN, DEFAULT_NEED_SIGN_IN)
|
||||
|
||||
fun putNeedSignIn(value: Boolean) {
|
||||
putBoolean(KEY_NEED_SIGN_IN, value)
|
||||
}
|
||||
|
||||
val selectSite: Boolean
|
||||
get() = getBoolean(KEY_SELECT_SITE, DEFAULT_SELECT_SITE)
|
||||
|
||||
fun putSelectSite(value: Boolean) {
|
||||
putBoolean(KEY_SELECT_SITE, value)
|
||||
}
|
||||
|
||||
val theme: Int
|
||||
get() = getIntFromStr(KEY_THEME, DEFAULT_THEME)
|
||||
val blackDarkTheme
|
||||
get() = getBoolean(KEY_BLACK_DARK_THEME, DEFAULT_BLACK_DARK_THEME)
|
||||
val gallerySite: Int
|
||||
get() = getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE)
|
||||
|
||||
fun putGallerySite(value: Int) {
|
||||
putIntToStr(KEY_GALLERY_SITE, value)
|
||||
}
|
||||
|
||||
val launchPageGalleryListSceneAction: String
|
||||
get() {
|
||||
return when (val value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE)) {
|
||||
0 -> GalleryListScene.ACTION_HOMEPAGE
|
||||
1 -> GalleryListScene.ACTION_SUBSCRIPTION
|
||||
2 -> GalleryListScene.ACTION_WHATS_HOT
|
||||
3 -> GalleryListScene.ACTION_TOP_LIST
|
||||
else -> throw IllegalStateException("Unexpected value: $value")
|
||||
}
|
||||
}
|
||||
val listMode: Int
|
||||
get() = getIntFromStr(KEY_LIST_MODE, DEFAULT_LIST_MODE)
|
||||
private val detailSize: Int
|
||||
get() = getIntFromStr(KEY_DETAIL_SIZE, DEFAULT_DETAIL_SIZE)
|
||||
|
||||
@get:DimenRes
|
||||
val detailSizeResId: Int
|
||||
get() = when (detailSize) {
|
||||
0 -> R.dimen.gallery_list_column_width_long
|
||||
1 -> R.dimen.gallery_list_column_width_short
|
||||
else -> throw IllegalStateException("Unexpected value: $detailSize")
|
||||
}
|
||||
val thumbSize: Int
|
||||
get() = dp2pix(application, thumbSizeDp.toFloat())
|
||||
|
||||
val thumbSizeDp: Int
|
||||
get() = getInt(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE)
|
||||
|
||||
val thumbResolution: Int
|
||||
get() = getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION)
|
||||
val showComments: Boolean
|
||||
get() = getBoolean(KEY_SHOW_COMMENTS, DEFAULT_SHOW_COMMENTS)
|
||||
val requestNews: Boolean
|
||||
get() = getBoolean(KEY_REQUEST_NEWS, DEFAULT_REQUEST_NEWS)
|
||||
val hideHvEvents: Boolean
|
||||
get() = getBoolean(KEY_HIDE_HV_EVENTS, DEFAULT_HIDE_HV_EVENTS)
|
||||
val showJpnTitle: Boolean
|
||||
get() = getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE)
|
||||
val showGalleryPages: Boolean
|
||||
get() = getBoolean(KEY_SHOW_GALLERY_PAGES, DEFAULT_SHOW_GALLERY_PAGES)
|
||||
val showTagTranslations: Boolean
|
||||
get() = getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS)
|
||||
|
||||
private fun putShowTagTranslations(value: Boolean) {
|
||||
putBoolean(KEY_SHOW_TAG_TRANSLATIONS, value)
|
||||
}
|
||||
|
||||
val meteredNetworkWarning: Boolean
|
||||
get() = getBoolean(KEY_METERED_NETWORK_WARNING, DEFAULT_METERED_NETWORK_WARNING)
|
||||
val appLinkVerifyTip: Boolean
|
||||
get() = getBoolean(KEY_APP_LINK_VERIFY_TIP, DEFAULT_APP_LINK_VERIFY_TIP)
|
||||
|
||||
fun putAppLinkVerifyTip(value: Boolean) {
|
||||
putBoolean(KEY_APP_LINK_VERIFY_TIP, value)
|
||||
}
|
||||
|
||||
val enabledSecurity: Boolean
|
||||
get() = getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY)
|
||||
val downloadLocation: UniFile?
|
||||
get() {
|
||||
val dir: UniFile?
|
||||
val builder = Uri.Builder()
|
||||
builder.scheme(getString(KEY_DOWNLOAD_SAVE_SCHEME, null))
|
||||
builder.encodedAuthority(getString(KEY_DOWNLOAD_SAVE_AUTHORITY, null))
|
||||
builder.encodedPath(getString(KEY_DOWNLOAD_SAVE_PATH, null))
|
||||
builder.encodedQuery(getString(KEY_DOWNLOAD_SAVE_QUERY, null))
|
||||
builder.encodedFragment(getString(KEY_DOWNLOAD_SAVE_FRAGMENT, null))
|
||||
dir = UniFile.fromUri(application, builder.build())
|
||||
return dir ?: UniFile.fromFile(AppConfig.getDefaultDownloadDir())
|
||||
}
|
||||
|
||||
fun putDownloadLocation(location: UniFile) {
|
||||
val uri = location.uri
|
||||
putString(KEY_DOWNLOAD_SAVE_SCHEME, uri.scheme)
|
||||
putString(KEY_DOWNLOAD_SAVE_AUTHORITY, uri.encodedAuthority)
|
||||
putString(KEY_DOWNLOAD_SAVE_PATH, uri.encodedPath)
|
||||
putString(KEY_DOWNLOAD_SAVE_QUERY, uri.encodedQuery)
|
||||
putString(KEY_DOWNLOAD_SAVE_FRAGMENT, uri.encodedFragment)
|
||||
}
|
||||
|
||||
val mediaScan: Boolean
|
||||
get() = getBoolean(KEY_MEDIA_SCAN, DEFAULT_MEDIA_SCAN)
|
||||
val recentDownloadLabel: String?
|
||||
get() = getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL)
|
||||
|
||||
fun putRecentDownloadLabel(value: String?) {
|
||||
putString(KEY_RECENT_DOWNLOAD_LABEL, value)
|
||||
}
|
||||
|
||||
val hasDefaultDownloadLabel: Boolean
|
||||
get() = getBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, DEFAULT_HAS_DOWNLOAD_LABEL)
|
||||
|
||||
fun putHasDefaultDownloadLabel(hasDefaultDownloadLabel: Boolean) {
|
||||
putBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, hasDefaultDownloadLabel)
|
||||
}
|
||||
|
||||
val defaultDownloadLabel: String?
|
||||
get() = getString(KEY_DEFAULT_DOWNLOAD_LABEL, DEFAULT_DOWNLOAD_LABEL)
|
||||
|
||||
fun putDefaultDownloadLabel(value: String?) {
|
||||
putString(KEY_DEFAULT_DOWNLOAD_LABEL, value)
|
||||
}
|
||||
|
||||
val multiThreadDownload: Int
|
||||
get() = getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD)
|
||||
val downloadDelay: Int
|
||||
get() = getIntFromStr(KEY_DOWNLOAD_DELAY, DEFAULT_DOWNLOAD_DELAY)
|
||||
val preloadImage: Int
|
||||
get() = getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE)
|
||||
val downloadOriginImage: Boolean
|
||||
get() = getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE)
|
||||
val favCat: Array<String>
|
||||
get() = arrayOf(
|
||||
sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_1, DEFAULT_FAV_CAT_1)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_2, DEFAULT_FAV_CAT_2)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_3, DEFAULT_FAV_CAT_3)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_4, DEFAULT_FAV_CAT_4)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_5, DEFAULT_FAV_CAT_5)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_6, DEFAULT_FAV_CAT_6)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_7, DEFAULT_FAV_CAT_7)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_8, DEFAULT_FAV_CAT_8)!!,
|
||||
sSettingsPre.getString(KEY_FAV_CAT_9, DEFAULT_FAV_CAT_9)!!,
|
||||
)
|
||||
|
||||
fun putFavCat(value: Array<String?>) {
|
||||
check(value.size == 10)
|
||||
sSettingsPre.edit()
|
||||
.putString(KEY_FAV_CAT_0, value[0])
|
||||
.putString(KEY_FAV_CAT_1, value[1])
|
||||
.putString(KEY_FAV_CAT_2, value[2])
|
||||
.putString(KEY_FAV_CAT_3, value[3])
|
||||
.putString(KEY_FAV_CAT_4, value[4])
|
||||
.putString(KEY_FAV_CAT_5, value[5])
|
||||
.putString(KEY_FAV_CAT_6, value[6])
|
||||
.putString(KEY_FAV_CAT_7, value[7])
|
||||
.putString(KEY_FAV_CAT_8, value[8])
|
||||
.putString(KEY_FAV_CAT_9, value[9])
|
||||
.apply()
|
||||
}
|
||||
|
||||
val favCount: IntArray
|
||||
get() = intArrayOf(
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_0, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_1, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_2, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_3, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_4, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_5, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_6, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_7, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_8, DEFAULT_FAV_COUNT),
|
||||
sSettingsPre.getInt(KEY_FAV_COUNT_9, DEFAULT_FAV_COUNT),
|
||||
)
|
||||
|
||||
fun putFavCount(count: IntArray) {
|
||||
check(count.size == 10)
|
||||
sSettingsPre.edit()
|
||||
.putInt(KEY_FAV_COUNT_0, count[0])
|
||||
.putInt(KEY_FAV_COUNT_1, count[1])
|
||||
.putInt(KEY_FAV_COUNT_2, count[2])
|
||||
.putInt(KEY_FAV_COUNT_3, count[3])
|
||||
.putInt(KEY_FAV_COUNT_4, count[4])
|
||||
.putInt(KEY_FAV_COUNT_5, count[5])
|
||||
.putInt(KEY_FAV_COUNT_6, count[6])
|
||||
.putInt(KEY_FAV_COUNT_7, count[7])
|
||||
.putInt(KEY_FAV_COUNT_8, count[8])
|
||||
.putInt(KEY_FAV_COUNT_9, count[9])
|
||||
.apply()
|
||||
}
|
||||
|
||||
val favLocalCount: Int
|
||||
get() = sSettingsPre.getInt(KEY_FAV_LOCAL, DEFAULT_FAV_COUNT)
|
||||
|
||||
fun putFavLocalCount(count: Int) {
|
||||
sSettingsPre.edit().putInt(KEY_FAV_LOCAL, count).apply()
|
||||
}
|
||||
|
||||
val favCloudCount: Int
|
||||
get() = sSettingsPre.getInt(KEY_FAV_CLOUD, DEFAULT_FAV_COUNT)
|
||||
|
||||
fun putFavCloudCount(count: Int) {
|
||||
sSettingsPre.edit().putInt(KEY_FAV_CLOUD, count).apply()
|
||||
}
|
||||
|
||||
val recentFavCat: Int
|
||||
get() = getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT)
|
||||
|
||||
fun putRecentFavCat(value: Int) {
|
||||
putInt(KEY_RECENT_FAV_CAT, value)
|
||||
}
|
||||
|
||||
val defaultFavSlot: Int
|
||||
get() = getInt(KEY_DEFAULT_FAV_SLOT, DEFAULT_DEFAULT_FAV_SLOT)
|
||||
|
||||
fun putDefaultFavSlot(value: Int) {
|
||||
putInt(KEY_DEFAULT_FAV_SLOT, value)
|
||||
}
|
||||
|
||||
val qSSaveProgress: Boolean
|
||||
get() = getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS)
|
||||
|
||||
fun putQSSaveProgress(value: Boolean) {
|
||||
putBoolean(KEY_QS_SAVE_PROGRESS, value)
|
||||
}
|
||||
|
||||
val saveParseErrorBody: Boolean
|
||||
get() = getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY)
|
||||
val saveCrashLog: Boolean
|
||||
get() = getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG)
|
||||
val security: Boolean
|
||||
get() = getBoolean(KEY_SECURITY, false)
|
||||
|
||||
fun putSecurity(value: Boolean) {
|
||||
putBoolean(KEY_SECURITY, value)
|
||||
}
|
||||
|
||||
val securityDelay: Int
|
||||
get() = getInt(KEY_SECURITY_DELAY, 0)
|
||||
val readCacheSize: Int
|
||||
get() = getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE)
|
||||
val builtInHosts: Boolean
|
||||
get() = getBoolean(KEY_BUILT_IN_HOSTS, DEFAULT_BUILT_IN_HOSTS)
|
||||
|
||||
private fun putBuiltInHosts(value: Boolean) {
|
||||
putBoolean(KEY_BUILT_IN_HOSTS, value)
|
||||
}
|
||||
|
||||
val dF: Boolean
|
||||
get() = getBoolean(KEY_DOMAIN_FRONTING, DEFAULT_FRONTING)
|
||||
|
||||
private fun putDF(value: Boolean) {
|
||||
putBoolean(KEY_DOMAIN_FRONTING, value)
|
||||
}
|
||||
|
||||
val bypassVpn: Boolean
|
||||
get() = getBoolean(KEY_BYPASS_VPN, DEFAULT_BYPASS_VPN)
|
||||
|
||||
@JvmStatic
|
||||
val proxyType: Int
|
||||
get() = getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE)
|
||||
|
||||
fun putProxyType(value: Int) {
|
||||
putInt(KEY_PROXY_TYPE, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val proxyIp: String?
|
||||
get() = getString(KEY_PROXY_IP, DEFAULT_PROXY_IP)
|
||||
|
||||
fun putProxyIp(value: String?) {
|
||||
putString(KEY_PROXY_IP, value)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val proxyPort: Int
|
||||
get() = getInt(KEY_PROXY_PORT, DEFAULT_PROXY_PORT)
|
||||
|
||||
fun putProxyPort(value: Int) {
|
||||
putInt(KEY_PROXY_PORT, value)
|
||||
}
|
||||
|
||||
val clipboardTextHashCode: Int
|
||||
get() = getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE)
|
||||
|
||||
fun putClipboardTextHashCode(value: Int) {
|
||||
putInt(KEY_CLIPBOARD_TEXT_HASH_CODE, value)
|
||||
}
|
||||
|
||||
val archivePasswds: Set<String>?
|
||||
get() = getStringSet(KEY_ARCHIVE_PASSWDS)
|
||||
|
||||
fun putPasswdToArchivePasswds(value: String) {
|
||||
putStringToStringSet(KEY_ARCHIVE_PASSWDS, value)
|
||||
}
|
||||
|
||||
val listThumbSize: Int
|
||||
get() {
|
||||
if (LIST_THUMB_SIZE_INITED) {
|
||||
return LIST_THUMB_SIZE
|
||||
}
|
||||
val size = 3 * getInt(KEY_LIST_THUMB_SIZE, DEFAULT_LIST_THUMB_SIZE)
|
||||
LIST_THUMB_SIZE = size
|
||||
LIST_THUMB_SIZE_INITED = true
|
||||
return size
|
||||
}
|
||||
val notificationRequired: Boolean
|
||||
get() = getBoolean(KEY_NOTIFICATION_REQUIRED, false)
|
||||
|
||||
fun putNotificationRequired() {
|
||||
putBoolean(KEY_NOTIFICATION_REQUIRED, true)
|
||||
}
|
||||
|
||||
val preloadThumbAggressively: Boolean
|
||||
get() = getBoolean(KEY_PRELOAD_THUMB_AGGRESIVELY, DEFAULT_PRELOAD_THUMB_AGGRESIVELY)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.dailycheck.updateDailyCheckWork
|
||||
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import splitties.preferences.PrefDelegate
|
||||
|
||||
private val collectScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||
fun <T, R : PrefDelegate<T>> R.observed(func: (Unit) -> Unit) = apply { collectScope.launch { changesFlow().collect(func) } }
|
||||
fun <T, R : Settings.Delegate<T>> R.observed(func: (Unit) -> Unit) = apply { collectScope.launch { flowGetter().collect(func) } }
|
||||
fun <T, R : Settings.Delegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { flowGetter().collect { flow.emit(Unit) } } }
|
||||
fun <T, R : PrefDelegate<T>> R.emitTo(flow: MutableSharedFlow<Unit>) = apply { collectScope.launch { changesFlow().collect { flow.emit(Unit) } } }
|
||||
|
||||
fun updateWhenLocaleChanges() {
|
||||
val newValue = Settings.language
|
||||
if ("system" == newValue) {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
|
||||
} else {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue))
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenKeepMediaStatusChanges() {
|
||||
collectScope.launchIO {
|
||||
runCatching {
|
||||
keepNoMediaFileStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenThemeChanges() {
|
||||
collectScope.launch {
|
||||
delay(100) // Avoid recompose being cancelled
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.theme)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenAmoledModeChanges() {
|
||||
collectScope.launch {
|
||||
delay(100) // Avoid recompose being cancelled
|
||||
AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_AUTO_BATTERY)
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.theme)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenRequestNewsChanges() {
|
||||
updateDailyCheckWork(appCtx)
|
||||
}
|
||||
|
||||
fun updateWhenGallerySiteChanges() {
|
||||
if (!Settings.needSignIn && EhCookieStore.hasSignedIn()) {
|
||||
collectScope.launchIO {
|
||||
runCatching {
|
||||
EhEngine.getUConfig()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateWhenTagTranslationChanges() {
|
||||
collectScope.launchIO {
|
||||
runCatching {
|
||||
EhTagDatabase.update()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.client.data.NormalGalleryPreview
|
||||
@@ -26,21 +27,24 @@ import com.hippo.ehviewer.client.data.NormalGalleryPreview
|
||||
|
||||
private const val URL_PREFIX_THUMB_E = "https://ehgt.org/"
|
||||
private const val URL_PREFIX_THUMB_EX = "https://s.exhentai.org/"
|
||||
private const val URL_PREFIX_LARGE_THUMB_EX = URL_PREFIX_THUMB_EX + "t/"
|
||||
|
||||
fun getImageKey(gid: Long, index: Int): String {
|
||||
return "image:$gid:$index"
|
||||
}
|
||||
|
||||
fun getThumbKey(url: String): String {
|
||||
return url.removePrefix(URL_PREFIX_THUMB_E).removePrefix(URL_PREFIX_THUMB_EX).removePrefix("t/")
|
||||
return url.removePrefix(URL_PREFIX_THUMB_E).removePrefix(URL_PREFIX_LARGE_THUMB_EX).removePrefix(URL_PREFIX_THUMB_EX)
|
||||
}
|
||||
|
||||
val GalleryPreview.url
|
||||
get() = if (this is NormalGalleryPreview) {
|
||||
if (EhUtils.isExHentai) URL_PREFIX_THUMB_EX + imageKey else URL_PREFIX_THUMB_E + imageKey
|
||||
get() = if (exThumb) {
|
||||
if (this is NormalGalleryPreview) URL_PREFIX_THUMB_EX else URL_PREFIX_LARGE_THUMB_EX
|
||||
} else {
|
||||
if (EhUtils.isExHentai) URL_PREFIX_THUMB_EX + "t/" + imageKey else URL_PREFIX_THUMB_E + imageKey
|
||||
}
|
||||
URL_PREFIX_THUMB_E
|
||||
} + imageKey
|
||||
|
||||
val GalleryInfo.thumbUrl
|
||||
get() = if (EhUtils.isExHentai) URL_PREFIX_THUMB_EX + "t/" + thumbKey!! else URL_PREFIX_THUMB_E + thumbKey!!
|
||||
get() = (if (exThumb) URL_PREFIX_LARGE_THUMB_EX else URL_PREFIX_THUMB_E) + thumbKey!!
|
||||
|
||||
val exThumb get() = EhUtils.isExHentai && !Settings.forceEhThumb
|
||||
@@ -15,136 +15,65 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.network.CookieDatabase
|
||||
import com.hippo.ehviewer.network.CookieSet
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import android.webkit.CookieManager
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.util.Collections
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object EhCookieStore : CookieJar {
|
||||
private val db: CookieDatabase = CookieDatabase(EhApplication.application)
|
||||
private val map: MutableMap<String, CookieSet> = db.allCookies
|
||||
private val manager = CookieManager.getInstance()
|
||||
fun signOut() = manager.removeAllCookies(null)
|
||||
fun contains(url: HttpUrl, name: String) = get(url).any { it.name == name }
|
||||
|
||||
fun signOut() {
|
||||
clear()
|
||||
fun get(url: HttpUrl): List<Cookie> {
|
||||
val cookies = manager.getCookie(url.toString())
|
||||
|
||||
return if (cookies != null && cookies.isNotEmpty()) {
|
||||
cookies.split(";").mapNotNull { Cookie.parse(url, it) }.filterNot { it.name == KEY_UTMP_NAME }
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasSignedIn(): Boolean {
|
||||
val url = EhUrl.HOST_E.toHttpUrl()
|
||||
return contains(url, KEY_IPB_MEMBER_ID) &&
|
||||
contains(url, KEY_IPB_PASS_HASH)
|
||||
return contains(url, KEY_IPB_MEMBER_ID) && contains(url, KEY_IPB_PASS_HASH)
|
||||
}
|
||||
|
||||
const val KEY_IPB_MEMBER_ID = "ipb_member_id"
|
||||
const val KEY_IPB_PASS_HASH = "ipb_pass_hash"
|
||||
const val KEY_IGNEOUS = "igneous"
|
||||
const val KEY_SETTINGS_PROFILE = "sp"
|
||||
const val KEY_STAR = "star"
|
||||
private const val KEY_STAR = "star"
|
||||
private const val KEY_CONTENT_WARNING = "nw"
|
||||
private const val CONTENT_WARNING_NOT_SHOW = "1"
|
||||
private val sTipsCookie: Cookie = Cookie.Builder()
|
||||
.name(KEY_CONTENT_WARNING)
|
||||
.value(CONTENT_WARNING_NOT_SHOW)
|
||||
.domain(EhUrl.DOMAIN_E)
|
||||
.path("/")
|
||||
.expiresAt(Long.MAX_VALUE)
|
||||
.build()
|
||||
private const val KEY_UTMP_NAME = "__utmp"
|
||||
private val sTipsCookie = Cookie.Builder().apply {
|
||||
name(KEY_CONTENT_WARNING)
|
||||
value(CONTENT_WARNING_NOT_SHOW)
|
||||
domain(EhUrl.DOMAIN_E)
|
||||
path("/")
|
||||
expiresAt(Long.MAX_VALUE)
|
||||
}.build()
|
||||
|
||||
fun newCookie(
|
||||
cookie: Cookie,
|
||||
newDomain: String,
|
||||
forcePersistent: Boolean = false,
|
||||
forceLongLive: Boolean = false,
|
||||
forceNotHostOnly: Boolean = false,
|
||||
): Cookie {
|
||||
val builder = Cookie.Builder()
|
||||
builder.name(cookie.name)
|
||||
builder.value(cookie.value)
|
||||
if (forceLongLive) {
|
||||
builder.expiresAt(Long.MAX_VALUE)
|
||||
} else if (cookie.persistent) {
|
||||
builder.expiresAt(cookie.expiresAt)
|
||||
} else if (forcePersistent) {
|
||||
builder.expiresAt(Long.MAX_VALUE)
|
||||
}
|
||||
if (cookie.hostOnly && !forceNotHostOnly) {
|
||||
builder.hostOnlyDomain(newDomain)
|
||||
} else {
|
||||
builder.domain(newDomain)
|
||||
}
|
||||
builder.path(cookie.path)
|
||||
if (cookie.secure) {
|
||||
builder.secure()
|
||||
}
|
||||
if (cookie.httpOnly) {
|
||||
builder.httpOnly()
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
fun copyCookie(domain: String, newDomain: String, name: String, path: String = "/") {
|
||||
val cookie = map[domain]?.get(name, domain, path)
|
||||
cookie?.let { addCookie(newCookie(it, newDomain)) }
|
||||
fun copyNecessaryCookies() {
|
||||
val cookie = get(EhUrl.HOST_E.toHttpUrl()).filter { it.name == KEY_STAR || it.name == KEY_IPB_MEMBER_ID || it.name == KEY_IPB_PASS_HASH || it.name == KEY_IGNEOUS }
|
||||
cookie.forEach { manager.setCookie(EhUrl.HOST_EX, it.toString()) }
|
||||
flush()
|
||||
}
|
||||
|
||||
fun deleteCookie(url: HttpUrl, name: String) {
|
||||
val deletedCookie = Cookie.Builder()
|
||||
.name(name)
|
||||
.value("deleted")
|
||||
.domain(url.host)
|
||||
.expiresAt(0)
|
||||
.build()
|
||||
addCookie(deletedCookie)
|
||||
manager.setCookie(url.toString(), "$name=;Max-Age=0")
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun addCookie(cookie: Cookie) {
|
||||
// For cookie database
|
||||
var toAdd: Cookie? = null
|
||||
var toUpdate: Cookie? = null
|
||||
var toRemove: Cookie? = null
|
||||
var set = map[cookie.domain]
|
||||
if (set == null) {
|
||||
set = CookieSet()
|
||||
map[cookie.domain] = set
|
||||
}
|
||||
if (cookie.expiresAt <= System.currentTimeMillis()) {
|
||||
toRemove = set.remove(cookie)
|
||||
// If the cookie is not persistent, it's not in database
|
||||
if (toRemove != null && !toRemove.persistent) {
|
||||
toRemove = null
|
||||
}
|
||||
} else {
|
||||
toAdd = cookie
|
||||
toUpdate = set.add(cookie)
|
||||
// If the cookie is not persistent, it's not in database
|
||||
if (!toAdd.persistent) toAdd = null
|
||||
if (toUpdate != null && !toUpdate.persistent) toUpdate = null
|
||||
// Remove the cookie if it updates to null
|
||||
if (toAdd == null && toUpdate != null) {
|
||||
toRemove = toUpdate
|
||||
toUpdate = null
|
||||
}
|
||||
}
|
||||
if (toRemove != null) {
|
||||
db.remove(toRemove)
|
||||
}
|
||||
if (toAdd != null) {
|
||||
if (toUpdate != null) {
|
||||
db.update(toUpdate, toAdd)
|
||||
} else {
|
||||
db.add(toAdd)
|
||||
}
|
||||
}
|
||||
if (EhUrl.DOMAIN_E in cookie.domain) manager.setCookie(EhUrl.HOST_E, cookie.toString()) else manager.setCookie(EhUrl.DOMAIN_EX, cookie.toString())
|
||||
}
|
||||
|
||||
fun flush() = manager.flush()
|
||||
|
||||
fun getCookieHeader(url: HttpUrl): String {
|
||||
val cookies = getCookies(url)
|
||||
val cookies = loadForRequest(url)
|
||||
val cookieHeader = StringBuilder()
|
||||
var i = 0
|
||||
val size = cookies.size
|
||||
@@ -159,111 +88,17 @@ object EhCookieStore : CookieJar {
|
||||
return cookieHeader.toString()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getCookies(url: HttpUrl): List<Cookie> {
|
||||
val accepted: MutableList<Cookie> = ArrayList()
|
||||
val expired: MutableList<Cookie> = ArrayList()
|
||||
for ((domain, cookieSet) in map) {
|
||||
if (domainMatch(url, domain)) {
|
||||
cookieSet[url, accepted, expired]
|
||||
}
|
||||
}
|
||||
for (cookie in expired) {
|
||||
if (cookie.persistent) {
|
||||
db.remove(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 6265 Section-5.4 step 2, sort the cookie-list
|
||||
// Cookies with longer paths are listed before cookies with shorter paths.
|
||||
// Ignore creation-time, we don't store them.
|
||||
accepted.sortWith { o1: Cookie, o2: Cookie -> o2.path.length - o1.path.length }
|
||||
return accepted
|
||||
}
|
||||
|
||||
fun contains(url: HttpUrl, name: String?): Boolean {
|
||||
for (cookie in getCookies(url)) {
|
||||
if (cookie.name == name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all cookies in this `CookieRepository`.
|
||||
*/
|
||||
@Synchronized
|
||||
fun clear() {
|
||||
map.clear()
|
||||
launchIO {
|
||||
db.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
for (cookie in cookies) {
|
||||
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
||||
if (cookie.name != "__utmp") {
|
||||
addCookie(cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation
|
||||
* of Android's private InetAddress#isNumeric API.
|
||||
*
|
||||
*
|
||||
* This matches IPv6 addresses as a hex string containing at least one colon, and possibly
|
||||
* including dots after the first colon. It matches IPv4 addresses as strings containing only
|
||||
* decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP
|
||||
* addresses nor hostnames; they will be verified as IP addresses (which is a more strict
|
||||
* verification).
|
||||
*/
|
||||
private val VERIFY_AS_IP_ADDRESS =
|
||||
Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)")
|
||||
|
||||
/**
|
||||
* Returns true if `host` is not a host name and might be an IP address.
|
||||
*/
|
||||
private fun verifyAsIpAddress(host: String): Boolean {
|
||||
return VERIFY_AS_IP_ADDRESS.matcher(host).matches()
|
||||
}
|
||||
|
||||
// okhttp3.Cookie.domainMatch(HttpUrl, String)
|
||||
@JvmStatic
|
||||
fun domainMatch(url: HttpUrl, domain: String?): Boolean {
|
||||
val urlHost = url.host
|
||||
return if (urlHost == domain) {
|
||||
true // As in 'example.com' matching 'example.com'.
|
||||
} else {
|
||||
urlHost.endsWith(domain!!) &&
|
||||
urlHost[urlHost.length - domain.length - 1] == '.' && !verifyAsIpAddress(
|
||||
urlHost,
|
||||
)
|
||||
}
|
||||
// As in 'example.com' matching 'www.example.com'.
|
||||
}
|
||||
// See https://github.com/Ehviewer-Overhauled/Ehviewer/issues/873
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) = cookies.filterNot { it.name == KEY_UTMP_NAME }.forEach { manager.setCookie(url.toString(), it.toString()) }.also { flush() }
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
val cookies = getCookies(url)
|
||||
val checkTips = domainMatch(url, EhUrl.DOMAIN_E)
|
||||
return if (checkTips) {
|
||||
val result: MutableList<Cookie> = ArrayList(cookies.size + 1)
|
||||
// Add all but skip some
|
||||
for (cookie in cookies) {
|
||||
val name = cookie.name
|
||||
if (KEY_CONTENT_WARNING == name) {
|
||||
continue
|
||||
}
|
||||
result.add(cookie)
|
||||
val checkTips = EhUrl.DOMAIN_E in url.host
|
||||
return get(url).run {
|
||||
if (checkTips) {
|
||||
filterNot { it.name == KEY_CONTENT_WARNING }.toMutableList().apply { add(sTipsCookie) }
|
||||
} else {
|
||||
this
|
||||
}
|
||||
// Add some
|
||||
result.add(sTipsCookie)
|
||||
Collections.unmodifiableList(result)
|
||||
} else {
|
||||
cookies
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.Hosts
|
||||
import com.hippo.ehviewer.Settings
|
||||
import okhttp3.Dns
|
||||
import java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
|
||||
object EhDns : Dns {
|
||||
private val hosts = EhApplication.hosts
|
||||
private val builtInHosts: MutableMap<String, List<InetAddress>> = mutableMapOf()
|
||||
|
||||
init {
|
||||
// Pair(ip: String!, blockedByCCP: Boolean!)
|
||||
val ehgtHosts = arrayOf(
|
||||
Pair("37.48.89.44", false),
|
||||
Pair("81.171.10.48", false),
|
||||
Pair("178.162.139.24", false),
|
||||
Pair("178.162.140.212", false),
|
||||
Pair("2001:1af8:4700:a062:8::47de", false),
|
||||
Pair("2001:1af8:4700:a062:9::47de", true),
|
||||
Pair("2001:1af8:4700:a0c9:4::47de", false),
|
||||
Pair("2001:1af8:4700:a0c9:3::47de", true),
|
||||
)
|
||||
|
||||
put(
|
||||
builtInHosts,
|
||||
"e-hentai.org",
|
||||
Pair("104.20.134.21", false),
|
||||
Pair("104.20.135.21", false),
|
||||
Pair("172.67.0.127", false),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"exhentai.org",
|
||||
Pair("178.175.128.252", false),
|
||||
Pair("178.175.129.252", false),
|
||||
Pair("178.175.129.254", false),
|
||||
Pair("178.175.128.254", false),
|
||||
Pair("178.175.132.20", false),
|
||||
Pair("178.175.132.22", false),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"s.exhentai.org",
|
||||
Pair("178.175.129.254", false),
|
||||
Pair("178.175.128.254", false),
|
||||
Pair("178.175.132.22", false),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"repo.e-hentai.org",
|
||||
Pair("94.100.28.57", true),
|
||||
Pair("94.100.29.73", true),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"forums.e-hentai.org",
|
||||
Pair("94.100.18.243", false),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"ehgt.org",
|
||||
*ehgtHosts,
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"gt0.ehgt.org",
|
||||
*ehgtHosts,
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"gt1.ehgt.org",
|
||||
*ehgtHosts,
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"gt2.ehgt.org",
|
||||
*ehgtHosts,
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"gt3.ehgt.org",
|
||||
*ehgtHosts,
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"ul.ehgt.org",
|
||||
Pair("94.100.24.82", true),
|
||||
Pair("94.100.24.72", true),
|
||||
)
|
||||
put(
|
||||
builtInHosts,
|
||||
"raw.githubusercontent.com",
|
||||
Pair("151.101.0.133", false),
|
||||
Pair("151.101.64.133", false),
|
||||
Pair("151.101.128.133", false),
|
||||
Pair("151.101.192.133", false),
|
||||
)
|
||||
}
|
||||
|
||||
private fun put(
|
||||
map: MutableMap<String, List<InetAddress>>,
|
||||
host: String,
|
||||
vararg ips: Pair<String, Boolean>,
|
||||
) {
|
||||
map[host] = ips.mapNotNull { pair ->
|
||||
Hosts.toInetAddress(host, pair.first).takeUnless { Settings.dF && pair.second }
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(UnknownHostException::class)
|
||||
override fun lookup(hostname: String): List<InetAddress> {
|
||||
return hosts[hostname] ?: builtInHosts[hostname].takeIf { Settings.builtInHosts }
|
||||
?: Dns.SYSTEM.lookup(hostname)
|
||||
}
|
||||
|
||||
fun isInHosts(hostname: String): Boolean {
|
||||
return hosts.contains(hostname) || (builtInHosts.contains(hostname) && Settings.builtInHosts)
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,13 @@ package com.hippo.ehviewer.client
|
||||
import android.util.Log
|
||||
import arrow.fx.coroutines.parMap
|
||||
import arrow.fx.coroutines.parZip
|
||||
import com.hippo.ehviewer.AppConfig
|
||||
import com.hippo.ehviewer.GetText
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.InsufficientFundsException
|
||||
import com.hippo.ehviewer.client.exception.NotLoggedInException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.parser.ArchiveParser
|
||||
import com.hippo.ehviewer.client.parser.EventPaneParser
|
||||
@@ -38,16 +39,27 @@ import com.hippo.ehviewer.client.parser.GalleryPageParser
|
||||
import com.hippo.ehviewer.client.parser.GalleryTokenApiParser
|
||||
import com.hippo.ehviewer.client.parser.HomeParser
|
||||
import com.hippo.ehviewer.client.parser.ProfileParser
|
||||
import com.hippo.ehviewer.client.parser.RateGalleryParser
|
||||
import com.hippo.ehviewer.client.parser.RateGalleryResult
|
||||
import com.hippo.ehviewer.client.parser.SignInParser
|
||||
import com.hippo.ehviewer.client.parser.TorrentParser
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentParser
|
||||
import com.hippo.ehviewer.client.parser.TorrentResult
|
||||
import com.hippo.ehviewer.client.parser.VoteCommentResult
|
||||
import com.hippo.ehviewer.client.parser.VoteTagParser
|
||||
import com.hippo.ehviewer.cronet.awaitBodyFully
|
||||
import com.hippo.ehviewer.cronet.cronetRequest
|
||||
import com.hippo.ehviewer.cronet.execute
|
||||
import com.hippo.ehviewer.dailycheck.showEventNotification
|
||||
import com.hippo.ehviewer.dailycheck.today
|
||||
import com.hippo.ehviewer.network.StatusCodeException
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.StatusCodeException
|
||||
import com.hippo.ehviewer.util.isCronetSupported
|
||||
import io.ktor.utils.io.pool.DirectByteBufferPool
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.addJsonArray
|
||||
import kotlinx.serialization.json.put
|
||||
import moe.tarsin.coroutines.removeAllSuspend
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import okhttp3.Headers
|
||||
import okhttp3.MediaType
|
||||
@@ -55,9 +67,10 @@ import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import org.json.JSONArray
|
||||
import org.jsoup.Jsoup
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.math.ceil
|
||||
|
||||
private const val TAG = "EhEngine"
|
||||
@@ -85,7 +98,7 @@ private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwab
|
||||
|
||||
// Check kokomade
|
||||
if (body.contains(KOKOMADE_URL)) {
|
||||
throw EhException("今回はここまで ${GetText.getString(R.string.kokomade_tip)}".trimIndent())
|
||||
throw EhException("今回はここまで ${appCtx.getString(R.string.kokomade_tip)}".trimIndent())
|
||||
}
|
||||
|
||||
// Check Gallery Not Available
|
||||
@@ -103,12 +116,12 @@ private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwab
|
||||
|
||||
if (e is ParseException) {
|
||||
if (body.isEmpty()) {
|
||||
throw EhException(GetText.getString(R.string.error_empty_html))
|
||||
throw EhException(appCtx.getString(R.string.error_empty_html))
|
||||
} else if ("<" !in body) {
|
||||
throw EhException(body)
|
||||
} else {
|
||||
if (Settings.saveParseErrorBody) AppConfig.saveParseErrorBody(e)
|
||||
throw EhException(GetText.getString(R.string.error_parse_error))
|
||||
throw EhException(appCtx.getString(R.string.error_parse_error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +129,40 @@ private fun rethrowExactly(code: Int, headers: Headers, body: String, e: Throwab
|
||||
throw e
|
||||
}
|
||||
|
||||
private suspend inline fun <T> Request.executeAndParsingWith(block: String.() -> T): T {
|
||||
val httpContentPool = DirectByteBufferPool(16, 16384)
|
||||
|
||||
suspend inline fun <T> fetchCompat(url: String, referer: String? = null, crossinline parser: (ByteBuffer) -> T): T {
|
||||
return if (isCronetSupported) {
|
||||
cronetRequest(url, referer).execute {
|
||||
val buffer = httpContentPool.borrow()
|
||||
try {
|
||||
awaitBodyFully {
|
||||
buffer.put(it)
|
||||
}
|
||||
buffer.flip()
|
||||
parser(buffer)
|
||||
} finally {
|
||||
httpContentPool.recycle(buffer)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ehRequest(url, referer).execute {
|
||||
val buffer = httpContentPool.borrow()
|
||||
try {
|
||||
while (true) {
|
||||
if (body.source().read(buffer) == -1) break
|
||||
}
|
||||
buffer.flip()
|
||||
parser(buffer)
|
||||
} finally {
|
||||
httpContentPool.recycle(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
||||
private suspend inline fun <T> Request.executeAndParsingWith(block: suspend String.() -> T): T {
|
||||
Log.d(TAG, url.toString())
|
||||
return execute {
|
||||
val body = body.string()
|
||||
@@ -129,9 +175,20 @@ private suspend inline fun <T> Request.executeAndParsingWith(block: String.() ->
|
||||
}
|
||||
|
||||
object EhEngine {
|
||||
suspend fun getTorrentList(url: String, gid: Long, token: String?): List<TorrentParser.Result> {
|
||||
suspend fun getOriginalImageUrl(url: String, referer: String?): String {
|
||||
Log.d(TAG, url)
|
||||
return ehRequest(url, referer).executeNoRedirect {
|
||||
header("Location")?.apply {
|
||||
if (contains("bounce_login")) {
|
||||
throw NotLoggedInException()
|
||||
}
|
||||
} ?: throw InsufficientFundsException()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTorrentList(url: String, gid: Long, token: String?): TorrentResult {
|
||||
val referer = EhUrl.getGalleryDetailUrl(gid, token)
|
||||
return ehRequest(url, referer).executeAndParsingWith(TorrentParser::parse)
|
||||
return fetchCompat(url, referer, TorrentParser::parse)
|
||||
}
|
||||
|
||||
suspend fun getArchiveList(url: String, gid: Long, token: String?) = ehRequest(url, EhUrl.getGalleryDetailUrl(gid, token))
|
||||
@@ -139,7 +196,7 @@ object EhEngine {
|
||||
.apply { funds = funds ?: ehRequest(EhUrl.URL_FUNDS).executeAndParsingWith(HomeParser::parseFunds) }
|
||||
|
||||
suspend fun getImageLimits() = parZip(
|
||||
{ ehRequest(EhUrl.URL_HOME).executeAndParsingWith(HomeParser::parse) },
|
||||
{ fetchCompat(EhUrl.URL_HOME, parser = HomeParser::parse) },
|
||||
{ ehRequest(EhUrl.URL_FUNDS).executeAndParsingWith(HomeParser::parseFunds) },
|
||||
{ limits, funds -> HomeParser.Result(limits, funds) },
|
||||
)
|
||||
@@ -154,12 +211,12 @@ object EhEngine {
|
||||
|
||||
suspend fun getUConfig(url: String = EhUrl.uConfigUrl) {
|
||||
runSuspendCatching {
|
||||
ehRequest(url).executeAndParsingWith { check(contains(U_CONFIG_TEXT)) { "U_CONFIG_TEXT not found!" } }
|
||||
ehRequest(url).executeAndParsingWith { check(contains(U_CONFIG_TEXT)) { "Unable to load config from $url!" } }
|
||||
}.onFailure {
|
||||
// It may get redirected when accessing ex for the first time
|
||||
if (EhUtils.isExHentai) {
|
||||
if (url == EhUrl.URL_UCONFIG_EX) {
|
||||
it.printStackTrace()
|
||||
ehRequest(url).executeAndParsingWith { check(contains(U_CONFIG_TEXT)) { "U_CONFIG_TEXT not found!" } }
|
||||
ehRequest(url).executeAndParsingWith { check(contains(U_CONFIG_TEXT)) { "Unable to load config from $url!" } }
|
||||
} else {
|
||||
throw it
|
||||
}
|
||||
@@ -233,11 +290,11 @@ object EhEngine {
|
||||
* @param dstCat -1 for delete, 0 - 9 for cloud favorite, others throw Exception
|
||||
* @param note max 250 characters
|
||||
*/
|
||||
suspend fun addFavorites(
|
||||
suspend fun modifyFavorites(
|
||||
gid: Long,
|
||||
token: String?,
|
||||
dstCat: Int,
|
||||
note: String?,
|
||||
dstCat: Int = -1,
|
||||
note: String = "",
|
||||
) {
|
||||
val catStr: String = when (dstCat) {
|
||||
-1 -> "favdel"
|
||||
@@ -248,9 +305,9 @@ object EhEngine {
|
||||
return ehRequest(url, url, EhUrl.origin) {
|
||||
formBody {
|
||||
add("favcat", catStr)
|
||||
add("favnote", note ?: "")
|
||||
// submit=Add+to+Favorites is not necessary, just use submit=Apply+Changes all the time
|
||||
add("submit", "Apply Changes")
|
||||
add("favnote", note)
|
||||
// apply=Add+to+Favorites is not necessary, just use apply=Apply+Changes all the time
|
||||
add("apply", "Apply Changes")
|
||||
add("update", "1")
|
||||
}
|
||||
}.executeAndParsingWith { }
|
||||
@@ -300,14 +357,11 @@ object EhEngine {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun resetImageLimits(): HomeParser.Limits? {
|
||||
return ehRequest(EhUrl.URL_HOME) {
|
||||
formBody {
|
||||
add("act", "limits")
|
||||
add("reset", "Reset Limit")
|
||||
}
|
||||
}.executeAndParsingWith(HomeParser::parseResetLimits)
|
||||
}
|
||||
suspend fun resetImageLimits() = ehRequest(EhUrl.URL_HOME) {
|
||||
formBody {
|
||||
add("reset_imagelimit", "Reset Limit")
|
||||
}
|
||||
}.executeAndParsingWith(HomeParser::parseResetLimits)
|
||||
|
||||
suspend fun modifyFavorites(url: String, gidArray: LongArray, dstCat: Int): FavoritesParser.Result {
|
||||
val catStr: String = when (dstCat) {
|
||||
@@ -319,7 +373,6 @@ object EhEngine {
|
||||
formBody {
|
||||
add("ddact", catStr)
|
||||
gidArray.forEach { add("modifygids[]", it.toString()) }
|
||||
add("apply", "Apply")
|
||||
}
|
||||
}.executeAndParsingWith(FavoritesParser::parse).apply { fillGalleryList(galleryInfoList, url, false) }
|
||||
}
|
||||
@@ -343,7 +396,13 @@ object EhEngine {
|
||||
}.executeAndParsingWith(GalleryPageApiParser::parse)
|
||||
}
|
||||
|
||||
suspend fun rateGallery(apiUid: Long, apiKey: String?, gid: Long, token: String?, rating: Float) = ehRequest(EhUrl.apiUrl, EhUrl.getGalleryDetailUrl(gid, token), EhUrl.origin) {
|
||||
suspend fun rateGallery(
|
||||
apiUid: Long,
|
||||
apiKey: String?,
|
||||
gid: Long,
|
||||
token: String?,
|
||||
rating: Float,
|
||||
): RateGalleryResult = ehRequest(EhUrl.apiUrl, EhUrl.getGalleryDetailUrl(gid, token), EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "rategallery")
|
||||
put("apiuid", apiUid)
|
||||
@@ -352,13 +411,20 @@ object EhEngine {
|
||||
put("token", token)
|
||||
put("rating", ceil((rating * 2).toDouble()).toInt())
|
||||
}
|
||||
}.executeAndParsingWith(RateGalleryParser::parse)
|
||||
}.executeAndParsingWith(String::parseAs)
|
||||
|
||||
suspend fun fillGalleryListByApi(galleryInfoList: List<GalleryInfo>, referer: String) = galleryInfoList.chunked(MAX_REQUEST_SIZE).parMap {
|
||||
ehRequest(EhUrl.apiUrl, referer, EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "gdata")
|
||||
array("gidlist") { it.forEach { put(jsonArrayOf(it.gid, it.token)) } }
|
||||
array("gidlist") {
|
||||
it.forEach {
|
||||
addJsonArray {
|
||||
add(it.gid)
|
||||
add(it.token)
|
||||
}
|
||||
}
|
||||
}
|
||||
put("namespace", 1)
|
||||
}
|
||||
}.executeAndParsingWith { GalleryApiParser.parse(this, it) }
|
||||
@@ -371,7 +437,7 @@ object EhEngine {
|
||||
token: String?,
|
||||
commentId: Long,
|
||||
commentVote: Int,
|
||||
) = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
|
||||
): VoteCommentResult = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "votecomment")
|
||||
put("apiuid", apiUid)
|
||||
@@ -381,7 +447,7 @@ object EhEngine {
|
||||
put("comment_id", commentId)
|
||||
put("comment_vote", commentVote)
|
||||
}
|
||||
}.executeAndParsingWith { VoteCommentParser.parse(this, commentVote) }
|
||||
}.executeAndParsingWith(String::parseAs)
|
||||
|
||||
suspend fun voteTag(
|
||||
apiUid: Long,
|
||||
@@ -405,7 +471,13 @@ object EhEngine {
|
||||
suspend fun getGalleryToken(gid: Long, gtoken: String?, page: Int) = ehRequest(EhUrl.apiUrl, EhUrl.referer, EhUrl.origin) {
|
||||
jsonBody {
|
||||
put("method", "gtoken")
|
||||
put("pagelist", JSONArray().put(JSONArray().put(gid).put(gtoken).put(page + 1)))
|
||||
array("pagelist") {
|
||||
addJsonArray {
|
||||
add(gid)
|
||||
add(gtoken)
|
||||
add(page + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.executeAndParsingWith(GalleryTokenApiParser::parse)
|
||||
|
||||
@@ -422,9 +494,9 @@ object EhEngine {
|
||||
}
|
||||
}.executeAndParsingWith(GalleryListParser::parse).apply { fillGalleryList(galleryInfoList, EhUrl.imageSearchUrl, true) }
|
||||
|
||||
private suspend fun fillGalleryList(list: MutableList<GalleryInfo>, url: String, filter: Boolean) {
|
||||
private suspend fun fillGalleryList(list: MutableList<BaseGalleryInfo>, url: String, filter: Boolean) {
|
||||
// Filter title and uploader
|
||||
if (filter) list.removeAll { EhFilter.filterTitle(it) || EhFilter.filterUploader(it) }
|
||||
if (filter) list.removeAllSuspend { EhFilter.filterTitle(it) || EhFilter.filterUploader(it) }
|
||||
|
||||
var hasTags = false
|
||||
var hasPages = false
|
||||
@@ -444,11 +516,10 @@ object EhEngine {
|
||||
if (needApi) fillGalleryListByApi(list, url)
|
||||
|
||||
// Filter tag, thumbnail mode need filter uploader again
|
||||
if (filter) list.removeAll { EhFilter.filterUploader(it) || EhFilter.filterTag(it) || EhFilter.filterTagNamespace(it) }
|
||||
if (filter) list.removeAllSuspend { EhFilter.filterUploader(it) || EhFilter.filterTag(it) || EhFilter.filterTagNamespace(it) }
|
||||
}
|
||||
|
||||
suspend fun addFavoritesRange(gidArray: LongArray, tokenArray: Array<String?>, dstCat: Int) {
|
||||
require(gidArray.size == tokenArray.size)
|
||||
gidArray.forEachIndexed { index, gid -> addFavorites(gid, tokenArray[index], dstCat, null) }
|
||||
suspend fun modifyFavoritesRange(galleryList: List<Pair<Long, String>>, dstCat: Int) {
|
||||
galleryList.forEach { (gid, token) -> modifyFavorites(gid, token, dstCat) }
|
||||
}
|
||||
}
|
||||
@@ -1,112 +1,35 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.util.Log
|
||||
import arrow.core.memoize
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.FilterMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val regex = { p: Filter -> Regex(p.text!!) }.memoize()
|
||||
private val regex = { p: Filter -> Regex(p.text) }.memoize()
|
||||
|
||||
object EhFilter {
|
||||
val titleFilterList = mutableListOf<Filter>()
|
||||
val uploaderFilterList = mutableListOf<Filter>()
|
||||
val tagFilterList = mutableListOf<Filter>()
|
||||
val tagNamespaceFilterList = mutableListOf<Filter>()
|
||||
val commenterFilterList = mutableListOf<Filter>()
|
||||
val commentFilterList = mutableListOf<Filter>()
|
||||
|
||||
const val MODE_TITLE = 0
|
||||
const val MODE_UPLOADER = 1
|
||||
const val MODE_TAG = 2
|
||||
const val MODE_TAG_NAMESPACE = 3
|
||||
const val MODE_COMMENTER = 4
|
||||
const val MODE_COMMENT = 5
|
||||
private const val TAG = "EhFilter"
|
||||
|
||||
init { EhDB.allFilter.forEach(::memorizeFilter) }
|
||||
|
||||
private fun memorizeFilter(filter: Filter) {
|
||||
when (filter.mode) {
|
||||
MODE_TITLE -> {
|
||||
filter.text = filter.text!!.lowercase()
|
||||
titleFilterList.add(filter)
|
||||
}
|
||||
|
||||
MODE_TAG -> {
|
||||
filter.text = filter.text!!.lowercase()
|
||||
tagFilterList.add(filter)
|
||||
}
|
||||
|
||||
MODE_TAG_NAMESPACE -> {
|
||||
filter.text = filter.text!!.lowercase()
|
||||
tagNamespaceFilterList.add(filter)
|
||||
}
|
||||
|
||||
MODE_UPLOADER -> uploaderFilterList.add(filter)
|
||||
MODE_COMMENTER -> commenterFilterList.add(filter)
|
||||
MODE_COMMENT -> commentFilterList.add(filter)
|
||||
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
|
||||
}
|
||||
object EhFilter : CoroutineScope {
|
||||
override val coroutineContext = Dispatchers.IO.limitedParallelism(1)
|
||||
val filters = async { EhDB.getAllFilter() as MutableList }
|
||||
private suspend inline fun anyActive(mode: FilterMode, predicate: (Filter) -> Boolean) = filters.await().any { it.mode == mode && it.enable && predicate(it) }
|
||||
private fun <R> Filter.launchOps(
|
||||
callback: ((R) -> Unit)? = null,
|
||||
ops: suspend Filter.() -> R,
|
||||
) = launch { ops().let { callback?.invoke(it) } }
|
||||
fun Filter.remember(callback: ((Boolean) -> Unit)? = null) = launchOps(callback) {
|
||||
EhDB.addFilter(this).also { if (it) filters.await().add(this) }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun addFilter(filter: Filter): Boolean {
|
||||
filter.enable = true
|
||||
if (!EhDB.addFilter(filter)) return false
|
||||
memorizeFilter(filter)
|
||||
return true
|
||||
fun Filter.trigger(callback: ((Unit) -> Unit)? = null) = launchOps(callback) {
|
||||
enable = !enable
|
||||
EhDB.updateFilter(this)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun triggerFilter(filter: Filter) {
|
||||
EhDB.triggerFilter(filter)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun deleteFilter(filter: Filter) {
|
||||
EhDB.deleteFilter(filter)
|
||||
when (filter.mode) {
|
||||
MODE_TITLE -> titleFilterList.remove(filter)
|
||||
MODE_TAG -> tagFilterList.remove(filter)
|
||||
MODE_TAG_NAMESPACE -> tagNamespaceFilterList.remove(filter)
|
||||
MODE_UPLOADER -> uploaderFilterList.remove(filter)
|
||||
MODE_COMMENTER -> commenterFilterList.remove(filter)
|
||||
MODE_COMMENT -> commentFilterList.remove(filter)
|
||||
else -> Log.d(TAG, "Unknown mode: " + filter.mode)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun needTags(): Boolean {
|
||||
return 0 != tagFilterList.size || 0 != tagNamespaceFilterList.size
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterTitle(info: GalleryInfo): Boolean {
|
||||
val title = info.title ?: return false
|
||||
return titleFilterList.any { it.enable!! && it.text!! in title.lowercase() }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterUploader(info: GalleryInfo): Boolean {
|
||||
val uploader = info.uploader ?: return false
|
||||
return uploaderFilterList.any { it.enable!! && it.text == uploader }
|
||||
fun Filter.forget(callback: ((Unit) -> Unit)? = null) = launchOps(callback) {
|
||||
EhDB.deleteFilter(this)
|
||||
filters.await().remove(this)
|
||||
}
|
||||
|
||||
private fun spiltTag(tag: String) = tag.run {
|
||||
@@ -124,30 +47,16 @@ object EhFilter {
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterTag(info: GalleryInfo): Boolean {
|
||||
val tags = info.simpleTags ?: return false
|
||||
return tags.any { tag -> tagFilterList.any { it.enable!! && matchTag(tag, it.text!!) } }
|
||||
}
|
||||
|
||||
private fun matchTagNamespace(tag: String, filter: String): Boolean {
|
||||
val (nameSpace, _) = spiltTag(tag)
|
||||
return nameSpace == filter
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterTagNamespace(info: GalleryInfo): Boolean {
|
||||
val tags = info.simpleTags ?: return false
|
||||
return tags.any { tag -> tagNamespaceFilterList.any { it.enable!! && matchTagNamespace(tag, it.text!!) } }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterCommenter(commenter: String): Boolean {
|
||||
return commenterFilterList.any { it.enable!! && it.text == commenter }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun filterComment(comment: String): Boolean {
|
||||
return commentFilterList.any { it.enable!! && regex(it).containsMatchIn(comment) }
|
||||
}
|
||||
suspend fun needTags() = filters.await().any { it.enable && (it.mode == FilterMode.TAG || it.mode == FilterMode.TAG_NAMESPACE) }
|
||||
suspend fun filterTitle(info: GalleryInfo) = anyActive(FilterMode.TITLE) { info.title.orEmpty().contains(it.text, true) }
|
||||
suspend fun filterUploader(info: GalleryInfo) = anyActive(FilterMode.UPLOADER) { it.text == info.uploader }
|
||||
suspend fun filterTag(info: GalleryInfo) = info.simpleTags?.any { tag -> anyActive(FilterMode.TAG) { matchTag(tag, it.text.lowercase()) } } ?: false
|
||||
suspend fun filterTagNamespace(info: GalleryInfo) = info.simpleTags?.any { tag -> anyActive(FilterMode.TAG_NAMESPACE) { matchTagNamespace(tag, it.text.lowercase()) } } ?: false
|
||||
suspend fun filterCommenter(commenter: String) = anyActive(FilterMode.COMMENTER) { it.text == commenter }
|
||||
suspend fun filterComment(comment: String) = anyActive(FilterMode.COMMENT) { regex(it).containsMatchIn(comment) }
|
||||
}
|
||||
@@ -15,8 +15,20 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import com.hippo.ehviewer.EhApplication.Companion.nonCacheOkHttpClient
|
||||
import android.util.Log
|
||||
import com.hippo.ehviewer.EhApplication.Companion.baseOkHttpClient
|
||||
import com.hippo.ehviewer.EhApplication.Companion.noRedirectOkHttpClient
|
||||
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArrayBuilder
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.okio.decodeFromBufferedSource
|
||||
import okhttp3.Call
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
@@ -25,10 +37,11 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.executeAsync
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import okio.BufferedSource
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
inline fun ehRequest(url: String, referer: String? = null, origin: String? = null, builder: Request.Builder.() -> Unit = {}) = Request.Builder().url(url).apply {
|
||||
addHeader("User-Agent", CHROME_USER_AGENT)
|
||||
@@ -44,11 +57,54 @@ inline fun Request.Builder.multipartBody(builder: MultipartBody.Builder.() -> Un
|
||||
|
||||
val MEDIA_TYPE_JSON: MediaType = "application/json; charset=utf-8".toMediaType()
|
||||
|
||||
inline fun JSONObject.array(name: String, builder: JSONArray.() -> Unit): JSONObject = put(name, JSONArray().apply(builder))
|
||||
inline fun JsonObjectBuilder.array(name: String, builder: JsonArrayBuilder.() -> Unit) = put(name, buildJsonArray(builder))
|
||||
|
||||
fun jsonArrayOf(vararg element: Any?) = JSONArray().apply { element.forEach { put(it) } } // Should ensure it is inlined
|
||||
inline fun Request.Builder.jsonBody(builder: JsonObjectBuilder.() -> Unit) = post(buildJsonObject(builder).toString().toRequestBody(MEDIA_TYPE_JSON))
|
||||
|
||||
inline fun Request.Builder.jsonBody(builder: JSONObject.() -> Unit) = post(JSONObject().apply(builder).toString().toRequestBody(MEDIA_TYPE_JSON))
|
||||
const val TAG_CALL = "CancellableCallReadingScope"
|
||||
|
||||
// This scoped response reading scope suspend function aimed at cancelling the call immediately once coroutine is cancelled.
|
||||
// i.e., bind a call with a suspended coroutine, and launch another coroutine to execute reading
|
||||
// Once origin coroutine is cancelled, then call cancelled, socket is closed, the reading coroutine will encounter [IOException]
|
||||
// Since Okio is not suspendable/cancellable, also okhttp, [executeAsync] only make [Request -> Response] cancellable, reading a Response is still not cancellable
|
||||
// Considering interrupting is not safe and performant, See https://github.com/Kotlin/kotlinx.coroutines/issues/3551#issuecomment-1353245978, we have such [usingCancellable]
|
||||
// TODO: Remove logging after this function stable
|
||||
suspend inline fun <R> Call.usingCancellable(crossinline block: suspend Response.() -> R): R = executeAsync().use {
|
||||
val call = this
|
||||
Log.d(TAG_CALL, "Got response of call$call")
|
||||
coroutineScope {
|
||||
suspendCancellableCoroutine<R> { cont ->
|
||||
launch {
|
||||
val r = runCatching {
|
||||
Log.d(TAG_CALL, "Reading response of call$call")
|
||||
block(it)
|
||||
}
|
||||
if (!isCanceled() && cont.isActive) {
|
||||
r.exceptionOrNull()?.let {
|
||||
Log.e(TAG_CALL, "Reading response of call$call failed!")
|
||||
cont.resumeWithException(it)
|
||||
}
|
||||
r.getOrNull()?.let {
|
||||
Log.d(TAG_CALL, "Reading response of call$call succeed!")
|
||||
cont.resume(it)
|
||||
}
|
||||
} else if (isCanceled() && cont.isActive) {
|
||||
Log.e(TAG_CALL, "call$call cancelled but coroutine is Active!")
|
||||
} else if (!isCanceled() && !cont.isActive) {
|
||||
Log.e(TAG_CALL, "call$call not cancelled but coroutine is Dead!")
|
||||
} else {
|
||||
Log.d(TAG_CALL, "Reading response of call$call cancelled")
|
||||
}
|
||||
}
|
||||
cont.invokeOnCancellation {
|
||||
Log.d(TAG_CALL, "Cancelling call$call")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
Log.d(TAG_CALL, "Reading response of call$call finished!")
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <R> Request.execute(block: Response.() -> R): R {
|
||||
contract {
|
||||
@@ -56,14 +112,17 @@ suspend inline fun <R> Request.execute(block: Response.() -> R): R {
|
||||
}
|
||||
return okHttpClient.newCall(this).executeAsync().use(block)
|
||||
}
|
||||
suspend inline fun <R> Request.executeNonCache(crossinline block: suspend Response.() -> R) = baseOkHttpClient.newCall(this).usingCancellable(block)
|
||||
suspend inline fun <R> Request.executeNoRedirect(block: Response.() -> R) = noRedirectOkHttpClient.newCall(this).executeAsync().use(block)
|
||||
|
||||
suspend inline fun <R> Request.executeNonCache(block: Response.() -> R): R {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return nonCacheOkHttpClient.newCall(this).executeAsync().use(block)
|
||||
}
|
||||
suspend inline fun <reified T> Request.executeAndParseAs() = execute { parseAs<T>() }
|
||||
|
||||
const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36"
|
||||
inline fun <reified T> Response.parseAs(): T = body.source().parseAs()
|
||||
inline fun <reified T> BufferedSource.parseAs(): T = json.decodeFromBufferedSource(this)
|
||||
inline fun <reified T> String.parseAs(): T = json.decodeFromString(this)
|
||||
|
||||
val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
const val CHROME_USER_AGENT = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36"
|
||||
const val CHROME_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
const val CHROME_ACCEPT_LANGUAGE = "en-US,en;q=0.5"
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.util.Log
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
|
||||
private const val EXCEPTIONAL_DOMAIN = "hath.network"
|
||||
|
||||
object EhSSLSocketFactory : SSLSocketFactory() {
|
||||
private val sslSocketFactory = getDefault() as SSLSocketFactory
|
||||
override fun getDefaultCipherSuites(): Array<String> {
|
||||
return sslSocketFactory.defaultCipherSuites
|
||||
}
|
||||
|
||||
override fun getSupportedCipherSuites(): Array<String> {
|
||||
return sslSocketFactory.supportedCipherSuites
|
||||
}
|
||||
|
||||
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
|
||||
val address = s.inetAddress.hostAddress.takeIf { EhDns.isInHosts(host) || EXCEPTIONAL_DOMAIN in host }
|
||||
val socket = sslSocketFactory.createSocket(s, address ?: host, port, autoClose) as SSLSocket
|
||||
val sslSession = socket.session
|
||||
Log.d(
|
||||
"EhSSLSocketFactory",
|
||||
"Host: " + host + " Address: " + address + " Protocol:" + sslSession.protocol + " CipherSuite:" + sslSession.cipherSuite,
|
||||
)
|
||||
return socket
|
||||
}
|
||||
|
||||
override fun createSocket(host: String, port: Int): Socket {
|
||||
return sslSocketFactory.createSocket(host, port)
|
||||
}
|
||||
|
||||
override fun createSocket(
|
||||
host: String,
|
||||
port: Int,
|
||||
localHost: InetAddress,
|
||||
localPort: Int,
|
||||
): Socket {
|
||||
return sslSocketFactory.createSocket(host, port, localHost, localPort)
|
||||
}
|
||||
|
||||
override fun createSocket(host: InetAddress, port: Int): Socket {
|
||||
return sslSocketFactory.createSocket(host, port)
|
||||
}
|
||||
|
||||
override fun createSocket(
|
||||
address: InetAddress,
|
||||
port: Int,
|
||||
localAddress: InetAddress,
|
||||
localPort: Int,
|
||||
): Socket {
|
||||
return sslSocketFactory.createSocket(address, port, localAddress, localPort)
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.content.Context
|
||||
import com.hippo.ehviewer.AppConfig
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.EhApplication.Companion.nonCacheOkHttpClient
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.hippo.ehviewer.EhApplication.Companion.baseOkHttpClient
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.yorozuya.FileUtils
|
||||
import com.hippo.ehviewer.yorozuya.copyToFile
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.FileUtils
|
||||
import com.hippo.ehviewer.util.copyToFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -34,33 +36,29 @@ import moe.tarsin.coroutines.runSuspendCatching
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.executeAsync
|
||||
import okio.BufferedSource
|
||||
import okio.HashingSink.Companion.sha1
|
||||
import okio.blackholeSink
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import org.json.JSONObject
|
||||
import splitties.init.appCtx
|
||||
import java.io.File
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
private typealias TagGroup = Map<String, String>
|
||||
private typealias TagGroups = Map<String, TagGroup>
|
||||
|
||||
object EhTagDatabase : CoroutineScope {
|
||||
private const val NAMESPACE_PREFIX = "n"
|
||||
private lateinit var tagGroups: TagGroups
|
||||
private val PREFIXES = arrayOf(
|
||||
"-",
|
||||
"~",
|
||||
"tag:",
|
||||
"weak:",
|
||||
)
|
||||
private lateinit var tagGroups: Map<String, Map<String, String>>
|
||||
private val updateLock = Mutex()
|
||||
private val dbLock = Mutex()
|
||||
override val coroutineContext = Dispatchers.IO + Job()
|
||||
|
||||
fun isInitialized(): Boolean {
|
||||
return this::tagGroups.isInitialized
|
||||
}
|
||||
|
||||
private fun JSONObject.toTagGroups(): TagGroups =
|
||||
keys().asSequence().associateWith { getJSONObject(it).toTagGroup() }
|
||||
|
||||
private fun JSONObject.toTagGroup(): TagGroup =
|
||||
keys().asSequence().associateWith { getString(it) }
|
||||
var initialized by mutableStateOf(false)
|
||||
|
||||
fun getTranslation(prefix: String? = NAMESPACE_PREFIX, tag: String?): String? {
|
||||
return tagGroups[prefix]?.get(tag)?.trim()?.takeIf { it.isNotEmpty() }
|
||||
@@ -101,22 +99,25 @@ object EhTagDatabase : CoroutineScope {
|
||||
translate: Boolean,
|
||||
exactly: Boolean = false,
|
||||
): Flow<Pair<String?, String>> = flow {
|
||||
val keywordPrefix = keyword.substringBefore(':')
|
||||
val keywordTag = keyword.drop(keywordPrefix.length + 1)
|
||||
val prefix = namespaceToPrefix(keywordPrefix) ?: keywordPrefix
|
||||
val tags = tagGroups[prefix.takeIf { keywordTag.isNotEmpty() && it != NAMESPACE_PREFIX }]
|
||||
var mKeyword = keyword
|
||||
PREFIXES.forEach { mKeyword = mKeyword.removePrefix(it) }
|
||||
val prefix = keyword.dropLast(mKeyword.length)
|
||||
val namespace = mKeyword.substringBefore(':')
|
||||
val tagKeyword = mKeyword.drop(namespace.length + 1)
|
||||
val namespacePrefix = namespaceToPrefix(namespace) ?: namespace
|
||||
val tags = tagGroups[namespacePrefix.takeIf { tagKeyword.isNotEmpty() && it != NAMESPACE_PREFIX }]
|
||||
tags?.let {
|
||||
internalSuggestFlow(it, keywordTag, translate, exactly).collect { (hint, tag) ->
|
||||
emit(Pair(hint, "$prefix:$tag"))
|
||||
internalSuggestFlow(it, tagKeyword, translate, exactly).collect { (hint, tag) ->
|
||||
emit(Pair(hint, "$prefix$namespacePrefix:$tag"))
|
||||
}
|
||||
} ?: tagGroups.forEach { (prefix, tags) ->
|
||||
if (prefix != NAMESPACE_PREFIX) {
|
||||
internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) ->
|
||||
emit(Pair(hint, "$prefix:$tag"))
|
||||
} ?: tagGroups.forEach { (namespacePrefix, tags) ->
|
||||
if (namespacePrefix != NAMESPACE_PREFIX) {
|
||||
internalSuggestFlow(tags, mKeyword, translate, exactly).collect { (hint, tag) ->
|
||||
emit(Pair(hint, "$prefix$namespacePrefix:$tag"))
|
||||
}
|
||||
} else {
|
||||
internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) ->
|
||||
emit(Pair(hint, "$tag:"))
|
||||
internalSuggestFlow(tags, mKeyword, translate, exactly).collect { (hint, namespacePrefix) ->
|
||||
emit(Pair(hint, "$prefix$namespacePrefix:"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +142,6 @@ object EhTagDatabase : CoroutineScope {
|
||||
it["reclass"] = "r"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun namespaceToPrefix(namespace: String): String? {
|
||||
return NAMESPACE_TO_PREFIX[namespace]
|
||||
}
|
||||
@@ -204,12 +204,13 @@ object EhTagDatabase : CoroutineScope {
|
||||
|
||||
private suspend fun issueUpdateInMemoryData() {
|
||||
dbLock.withLock {
|
||||
getMetadata(EhApplication.application)?.let { urls ->
|
||||
getMetadata(appCtx)?.let { urls ->
|
||||
val dataName = urls[2]
|
||||
val dir = AppConfig.getFilesDir("tag-translations")
|
||||
val dataFile = File(dir, dataName).takeIf { it.exists() } ?: return
|
||||
runSuspendCatching {
|
||||
tagGroups = JSONObject(dataFile.source().buffer().readString(StandardCharsets.UTF_8)).toTagGroups()
|
||||
tagGroups = dataFile.source().buffer().use(BufferedSource::parseAs)
|
||||
initialized = true
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
@@ -224,7 +225,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
}
|
||||
|
||||
private suspend fun updateInternal() {
|
||||
val urls = getMetadata(EhApplication.application)
|
||||
val urls = getMetadata(appCtx)
|
||||
urls?.let {
|
||||
val sha1Name = urls[0]
|
||||
val sha1Url = urls[1]
|
||||
@@ -244,11 +245,9 @@ object EhTagDatabase : CoroutineScope {
|
||||
FileUtils.delete(dataFile)
|
||||
}
|
||||
|
||||
val client = nonCacheOkHttpClient
|
||||
|
||||
// Save new sha1
|
||||
val tempSha1File = File(dir, "$sha1Name.tmp")
|
||||
check(save(client, sha1Url, tempSha1File))
|
||||
check(save(baseOkHttpClient, sha1Url, tempSha1File))
|
||||
val tempSha1 = getFileContent(tempSha1File)
|
||||
|
||||
// Check new sha1 and current sha1
|
||||
@@ -260,7 +259,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
|
||||
// Save new data
|
||||
val tempDataFile = File(dir, "$dataName.tmp")
|
||||
check(save(client, dataUrl, tempDataFile))
|
||||
check(save(baseOkHttpClient, dataUrl, tempDataFile))
|
||||
|
||||
// Check new sha1 and new data
|
||||
if (!checkData(tempSha1, tempDataFile)) {
|
||||
|
||||
@@ -16,26 +16,23 @@
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.network.UrlBuilder
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
object EhUrl {
|
||||
const val SITE_E = 0
|
||||
const val SITE_EX = 1
|
||||
const val DOMAIN_EX = "exhentai.org"
|
||||
const val DOMAIN_E = "e-hentai.org"
|
||||
const val DOMAIN_LOFI = "lofi.e-hentai.org"
|
||||
const val HOST_EX = "https://$DOMAIN_EX/"
|
||||
const val API_EX = HOST_EX + "api.php"
|
||||
const val URL_FAVORITES_EX = HOST_EX + "favorites.php"
|
||||
const val FAV_PATH = "favorites.php"
|
||||
const val WATCHED_PATH = "watched"
|
||||
const val URL_UCONFIG_EX = HOST_EX + "uconfig.php"
|
||||
const val URL_MY_TAGS_EX = HOST_EX + "mytags"
|
||||
const val URL_WATCHED_EX = HOST_EX + "watched"
|
||||
const val HOST_E = "https://$DOMAIN_E/"
|
||||
const val API_E = HOST_E + "api.php"
|
||||
const val URL_FAVORITES_E = HOST_E + "favorites.php"
|
||||
const val URL_UCONFIG_E = HOST_E + "uconfig.php"
|
||||
const val URL_MY_TAGS_E = HOST_E + "mytags"
|
||||
const val URL_WATCHED_E = HOST_E + "watched"
|
||||
const val API_SIGN_IN = "https://forums.e-hentai.org/index.php?act=Login&CODE=01"
|
||||
const val URL_POPULAR_E = "https://e-hentai.org/popular"
|
||||
const val URL_POPULAR_EX = "https://exhentai.org/popular"
|
||||
@@ -59,11 +56,11 @@ object EhUrl {
|
||||
else -> HOST_E
|
||||
}
|
||||
|
||||
val favoritesUrl: String
|
||||
val domain: String
|
||||
get() = when (Settings.gallerySite) {
|
||||
SITE_E -> URL_FAVORITES_E
|
||||
SITE_EX -> URL_FAVORITES_EX
|
||||
else -> URL_FAVORITES_E
|
||||
SITE_E -> DOMAIN_E
|
||||
SITE_EX -> DOMAIN_EX
|
||||
else -> HOST_E
|
||||
}
|
||||
|
||||
val apiUrl: String
|
||||
@@ -73,7 +70,6 @@ object EhUrl {
|
||||
else -> API_E
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val referer: String
|
||||
get() = when (Settings.gallerySite) {
|
||||
SITE_E -> REFERER_E
|
||||
@@ -88,7 +84,6 @@ object EhUrl {
|
||||
else -> ORIGIN_E
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val uConfigUrl: String
|
||||
get() = when (Settings.gallerySite) {
|
||||
SITE_E -> URL_UCONFIG_E
|
||||
@@ -96,7 +91,6 @@ object EhUrl {
|
||||
else -> URL_UCONFIG_E
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val myTagsUrl: String
|
||||
get() = when (Settings.gallerySite) {
|
||||
SITE_E -> URL_MY_TAGS_E
|
||||
@@ -118,36 +112,20 @@ object EhUrl {
|
||||
else -> URL_IMAGE_SEARCH_E
|
||||
}
|
||||
|
||||
val watchedUrl: String
|
||||
get() = when (Settings.gallerySite) {
|
||||
SITE_E -> URL_WATCHED_E
|
||||
SITE_EX -> URL_WATCHED_EX
|
||||
else -> URL_WATCHED_E
|
||||
}
|
||||
|
||||
fun getGalleryDetailUrl(gid: Long, token: String?): String {
|
||||
return getGalleryDetailUrl(gid, token, 0, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getGalleryDetailUrl(gid: Long, token: String?, index: Int, allComment: Boolean): String {
|
||||
val builder = UrlBuilder(host + "g/" + gid + '/' + token + '/')
|
||||
if (index != 0) {
|
||||
builder.addQuery("p", index)
|
||||
}
|
||||
if (allComment) {
|
||||
builder.addQuery("hc", 1)
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
fun getGalleryDetailUrl(gid: Long, token: String?, index: Int, allComment: Boolean) = ehUrl {
|
||||
addPathSegments("g/$gid/$token/")
|
||||
if (index != 0) addQueryParameter("p", "$index")
|
||||
if (allComment) addQueryParameter("hc", "1")
|
||||
}.toString()
|
||||
|
||||
@JvmStatic
|
||||
fun getGalleryMultiPageViewerUrl(gid: Long, token: String): String {
|
||||
val builder = UrlBuilder(host + "mpv/" + gid + '/' + token + '/')
|
||||
return builder.build()
|
||||
}
|
||||
fun getGalleryMultiPageViewerUrl(gid: Long, token: String) = ehUrl {
|
||||
addPathSegments("mpv/$gid/$token/")
|
||||
}.toString()
|
||||
|
||||
@JvmStatic
|
||||
fun getPageUrl(gid: Long, index: Int, pToken: String): String {
|
||||
return host + "s/" + pToken + '/' + gid + '-' + (index + 1)
|
||||
}
|
||||
@@ -164,3 +142,9 @@ object EhUrl {
|
||||
return "https://ehwiki.org/wiki/" + tag.replace(' ', '_')
|
||||
}
|
||||
}
|
||||
|
||||
inline fun httpsUrl(builder: HttpUrl.Builder.() -> Unit) = HttpUrl.Builder().apply(builder).scheme("https").build()
|
||||
inline fun ehUrl(builder: HttpUrl.Builder.() -> Unit) = httpsUrl { apply(builder).host(EhUrl.domain) }
|
||||
fun HttpUrl.Builder.addQueryParameterIfNotBlank(name: String, value: String?) = apply {
|
||||
if (!value.isNullOrBlank()) addQueryParameter(name, value)
|
||||
}
|
||||
@@ -15,7 +15,15 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.google.android.material.R
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.color.utilities.Hct
|
||||
import com.google.android.material.color.utilities.MathUtils
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import java.util.regex.Pattern
|
||||
@@ -79,7 +87,6 @@ object EhUtils {
|
||||
val isExHentai: Boolean
|
||||
get() = Settings.gallerySite == EhUrl.SITE_EX
|
||||
|
||||
@JvmStatic
|
||||
fun getCategory(type: String?): Int {
|
||||
for (entry in CATEGORY_STRINGS) {
|
||||
for (str in entry.first)
|
||||
@@ -94,8 +101,19 @@ object EhUtils {
|
||||
return CATEGORY_VALUES.getOrDefault(type, CATEGORY_VALUES[UNKNOWN])!![0]
|
||||
}
|
||||
|
||||
fun getCategoryColor(category: Int): Int {
|
||||
return when (category) {
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun harmonizeWithRole(context: Context, src: Int): Int {
|
||||
val primary = MaterialColors.getColor(context, R.attr.colorPrimaryContainer, "EhUtils")
|
||||
val fromHct = Hct.fromInt(src)
|
||||
val toHct = Hct.fromInt(primary)
|
||||
val differenceDegrees = MathUtils.differenceDegrees(fromHct.hue, toHct.hue)
|
||||
val rotationDegrees = minOf(differenceDegrees * 0.5, 15.0)
|
||||
val outputHue = MathUtils.sanitizeDegreesDouble(fromHct.hue + rotationDegrees * MathUtils.rotationDirection(fromHct.hue, toHct.hue))
|
||||
return Hct.from(outputHue, toHct.chroma, toHct.tone).toInt()
|
||||
}
|
||||
|
||||
private fun getCategoryColor(context: Context, category: Int): Int {
|
||||
val primary = when (category) {
|
||||
DOUJINSHI -> BG_COLOR_DOUJINSHI
|
||||
MANGA -> BG_COLOR_MANGA
|
||||
ARTIST_CG -> BG_COLOR_ARTIST_CG
|
||||
@@ -108,27 +126,29 @@ object EhUtils {
|
||||
MISC -> BG_COLOR_MISC
|
||||
else -> BG_COLOR_UNKNOWN
|
||||
}.toInt()
|
||||
return harmonizeWithRole(context, primary)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getCategoryColor(category: Int) = Color(getCategoryColor(LocalContext.current, category))
|
||||
|
||||
fun signOut() {
|
||||
EhCookieStore.signOut()
|
||||
Settings.putAvatar(null)
|
||||
Settings.putDisplayName(null)
|
||||
Settings.putGallerySite(EhUrl.SITE_E)
|
||||
Settings.putNeedSignIn(true)
|
||||
Settings.putSelectSite(true)
|
||||
Settings.avatar = null
|
||||
Settings.displayName = null
|
||||
Settings.gallerySite = EhUrl.SITE_E
|
||||
Settings.needSignIn = true
|
||||
}
|
||||
|
||||
fun needSignedIn(): Boolean {
|
||||
return Settings.needSignIn
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getSuitableTitle(gi: GalleryInfo): String {
|
||||
return if (Settings.showJpnTitle) {
|
||||
if (TextUtils.isEmpty(gi.titleJpn)) gi.title else gi.titleJpn
|
||||
if (gi.titleJpn.isNullOrEmpty()) gi.title else gi.titleJpn
|
||||
} else {
|
||||
if (TextUtils.isEmpty(gi.title)) gi.titleJpn else gi.title
|
||||
if (gi.title.isNullOrEmpty()) gi.titleJpn else gi.title
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
@@ -146,7 +166,6 @@ object EhUtils {
|
||||
return title.ifEmpty { null }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun handleThumbUrlResolution(url: String?): String? {
|
||||
if (null == url) {
|
||||
return null
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
interface AbstractDownloadInfo {
|
||||
var state: Int
|
||||
var legacy: Int
|
||||
var time: Long
|
||||
var label: String?
|
||||
var position: Int
|
||||
var speed: Long
|
||||
var remaining: Long
|
||||
var finished: Int
|
||||
var downloaded: Int
|
||||
var total: Int
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
interface AbstractGalleryInfo {
|
||||
var gid: Long
|
||||
var token: String?
|
||||
var title: String?
|
||||
var titleJpn: String?
|
||||
var thumbKey: String?
|
||||
var category: Int
|
||||
var posted: String?
|
||||
var uploader: String?
|
||||
var disowned: Boolean
|
||||
var rating: Float
|
||||
var rated: Boolean
|
||||
var simpleTags: Array<String>?
|
||||
var pages: Int
|
||||
var thumbWidth: Int
|
||||
var thumbHeight: Int
|
||||
var spanSize: Int
|
||||
var spanIndex: Int
|
||||
var spanGroupIndex: Int
|
||||
var simpleLanguage: String?
|
||||
var favoriteSlot: Int
|
||||
var favoriteName: String?
|
||||
}
|
||||
@@ -15,12 +15,16 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "GALLERIES")
|
||||
open class BaseGalleryInfo(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "GID")
|
||||
@@ -57,7 +61,7 @@ open class BaseGalleryInfo(
|
||||
override var rated: Boolean = false,
|
||||
|
||||
@Ignore
|
||||
override var simpleTags: Array<String>? = null,
|
||||
override var simpleTags: ArrayList<String>? = null,
|
||||
|
||||
@Ignore
|
||||
override var pages: Int = 0,
|
||||
@@ -68,22 +72,17 @@ open class BaseGalleryInfo(
|
||||
@Ignore
|
||||
override var thumbHeight: Int = 0,
|
||||
|
||||
@Ignore
|
||||
override var spanSize: Int = 0,
|
||||
|
||||
@Ignore
|
||||
override var spanIndex: Int = 0,
|
||||
|
||||
@Ignore
|
||||
override var spanGroupIndex: Int = 0,
|
||||
|
||||
@ColumnInfo(name = "SIMPLE_LANGUAGE")
|
||||
override var simpleLanguage: String? = null,
|
||||
|
||||
@Ignore
|
||||
override var favoriteSlot: Int = -2,
|
||||
@ColumnInfo(name = "FAVORITE_SLOT")
|
||||
override var favoriteSlot: Int = NOT_FAVORITED,
|
||||
|
||||
@Ignore
|
||||
override var favoriteName: String? = null,
|
||||
|
||||
) : GalleryInfo
|
||||
@Ignore
|
||||
override var favoriteNote: String? = null,
|
||||
) : GalleryInfo, Parcelable {
|
||||
constructor() : this(0)
|
||||
}
|
||||
@@ -17,14 +17,14 @@ package com.hippo.ehviewer.client.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.network.UrlBuilder
|
||||
import com.hippo.ehviewer.util.encodeUTF8
|
||||
import com.hippo.ehviewer.client.addQueryParameterIfNotBlank
|
||||
import com.hippo.ehviewer.client.ehUrl
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class FavListUrlBuilder(
|
||||
private var mPrev: String? = null,
|
||||
private var mNext: String? = null,
|
||||
var mPrev: String? = null,
|
||||
var mNext: String? = null,
|
||||
var jumpTo: String? = null,
|
||||
var keyword: String? = null,
|
||||
var favCat: Int = FAV_CAT_ALL,
|
||||
@@ -34,19 +34,18 @@ class FavListUrlBuilder(
|
||||
mPrev = index.takeUnless { isNext }
|
||||
}
|
||||
|
||||
fun build(): String {
|
||||
val ub = UrlBuilder(EhUrl.favoritesUrl)
|
||||
fun build() = ehUrl {
|
||||
addPathSegment(EhUrl.FAV_PATH)
|
||||
if (isValidFavCat(favCat)) {
|
||||
ub.addQuery("favcat", favCat.toString())
|
||||
addQueryParameter("favcat", "$favCat")
|
||||
} else if (favCat == FAV_CAT_ALL) {
|
||||
ub.addQuery("favcat", "all")
|
||||
addQueryParameter("favcat", "all")
|
||||
}
|
||||
keyword?.takeIf { it.isNotBlank() }?.let { ub.addQuery("f_search", encodeUTF8(it)) }
|
||||
mPrev?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("prev", it) }
|
||||
mNext?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("next", it) }
|
||||
jumpTo?.takeIf { it.isNotEmpty() }?.let { ub.addQuery("seek", it) }
|
||||
return ub.build()
|
||||
}
|
||||
addQueryParameterIfNotBlank("f_search", keyword)
|
||||
addQueryParameterIfNotBlank("prev", mPrev)
|
||||
addQueryParameterIfNotBlank("next", mNext)
|
||||
addQueryParameterIfNotBlank("seek", jumpTo)
|
||||
}.toString()
|
||||
|
||||
companion object {
|
||||
const val FAV_CAT_ALL = -1
|
||||
|
||||
@@ -21,30 +21,17 @@ import kotlinx.parcelize.Parcelize
|
||||
@Parcelize
|
||||
class GalleryComment(
|
||||
// 0 for uploader comment. can't vote
|
||||
@JvmField
|
||||
var id: Long = 0,
|
||||
@JvmField
|
||||
var score: Int = 0,
|
||||
@JvmField
|
||||
var editable: Boolean = false,
|
||||
@JvmField
|
||||
var voteUpAble: Boolean = false,
|
||||
@JvmField
|
||||
var voteUpEd: Boolean = false,
|
||||
@JvmField
|
||||
var voteDownAble: Boolean = false,
|
||||
@JvmField
|
||||
var voteDownEd: Boolean = false,
|
||||
@JvmField
|
||||
var uploader: Boolean = false,
|
||||
@JvmField
|
||||
var voteState: String? = null,
|
||||
@JvmField
|
||||
var time: Long = 0,
|
||||
@JvmField
|
||||
var user: String? = null,
|
||||
@JvmField
|
||||
var comment: String? = null,
|
||||
@JvmField
|
||||
var lastEdited: Long = 0,
|
||||
) : Parcelable
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.S_LANGS
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -37,25 +38,24 @@ private val LANGUAGES = arrayOf(
|
||||
|
||||
@Parcelize
|
||||
class GalleryDetail(
|
||||
val galleryInfo: GalleryInfo = BaseGalleryInfo(),
|
||||
val galleryInfo: BaseGalleryInfo = BaseGalleryInfo(),
|
||||
var apiUid: Long = -1L,
|
||||
var apiKey: String? = null,
|
||||
var torrentCount: Int = 0,
|
||||
var torrentUrl: String? = null,
|
||||
var archiveUrl: String? = null,
|
||||
var parent: String? = null,
|
||||
var newerVersions: ArrayList<GalleryInfo> = ArrayList(),
|
||||
var newerVersions: ArrayList<BaseGalleryInfo> = ArrayList(),
|
||||
var visible: String? = null,
|
||||
var language: String? = null,
|
||||
var size: String? = null,
|
||||
var favoriteCount: Int = 0,
|
||||
var isFavorited: Boolean = false,
|
||||
var ratingCount: Int = 0,
|
||||
val tags: Array<GalleryTagGroup>,
|
||||
var comments: GalleryCommentList,
|
||||
val previewPages: Int,
|
||||
val previewList: List<GalleryPreview>,
|
||||
) : AbstractGalleryInfo by galleryInfo, GalleryInfo {
|
||||
) : GalleryInfo by galleryInfo, Parcelable {
|
||||
override fun generateSLang() {
|
||||
val index = LANGUAGES.indexOf(language)
|
||||
if (index != -1) simpleLanguage = S_LANGS[index]
|
||||
|
||||
@@ -15,16 +15,35 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import java.util.regex.Pattern
|
||||
|
||||
interface GalleryInfo : AbstractGalleryInfo, Parcelable {
|
||||
interface GalleryInfo {
|
||||
var gid: Long
|
||||
var token: String?
|
||||
var title: String?
|
||||
var titleJpn: String?
|
||||
var thumbKey: String?
|
||||
var category: Int
|
||||
var posted: String?
|
||||
var uploader: String?
|
||||
var disowned: Boolean
|
||||
var rating: Float
|
||||
var rated: Boolean
|
||||
var simpleTags: ArrayList<String>?
|
||||
var pages: Int
|
||||
var thumbWidth: Int
|
||||
var thumbHeight: Int
|
||||
var simpleLanguage: String?
|
||||
var favoriteSlot: Int
|
||||
var favoriteName: String?
|
||||
var favoriteNote: String?
|
||||
|
||||
fun generateSLang() {
|
||||
simpleLanguage = simpleTags?.let { generateSLangFromTags(it) }
|
||||
?: title?.let { generateSLangFromTitle(it) }
|
||||
}
|
||||
|
||||
private fun generateSLangFromTags(simpleTags: Array<String>): String? {
|
||||
private fun generateSLangFromTags(simpleTags: List<String>): String? {
|
||||
for (tag in simpleTags) {
|
||||
for (i in S_LANGS.indices) {
|
||||
if (S_LANG_TAGS[i] == tag) {
|
||||
@@ -127,5 +146,8 @@ interface GalleryInfo : AbstractGalleryInfo, Parcelable {
|
||||
"language:hungarian",
|
||||
"language:dutch",
|
||||
)
|
||||
|
||||
const val NOT_FAVORITED = -2
|
||||
const val LOCAL_FAVORITED = -1
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class GalleryTagGroup(
|
||||
private val mTagList: ArrayList<String> = arrayListOf(),
|
||||
@JvmField
|
||||
var groupName: String? = null,
|
||||
val groupName: String,
|
||||
private val mTagList: MutableList<String> = mutableListOf(),
|
||||
) : Parcelable, MutableList<String> by mTagList
|
||||
@@ -16,16 +16,15 @@
|
||||
package com.hippo.ehviewer.client.data
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.IntDef
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.addQueryParameterIfNotBlank
|
||||
import com.hippo.ehviewer.client.ehUrl
|
||||
import com.hippo.ehviewer.dao.QuickSearch
|
||||
import com.hippo.ehviewer.network.UrlBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.AdvanceSearchTable
|
||||
import com.hippo.ehviewer.ui.main.AdvanceTable
|
||||
import com.hippo.ehviewer.util.encodeUTF8
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.ehviewer.yorozuya.StringUtils
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
@@ -35,11 +34,11 @@ data class ListUrlBuilder(
|
||||
@get:Mode @Mode
|
||||
var mode: Int = MODE_NORMAL,
|
||||
private var mPrev: String? = null,
|
||||
private var mNext: String? = null,
|
||||
private var mJumpTo: String? = null,
|
||||
var mNext: String? = null,
|
||||
var mJumpTo: String? = null,
|
||||
var category: Int = EhUtils.NONE,
|
||||
private var mKeyword: String? = null,
|
||||
private var mSHash: String? = null,
|
||||
var hash: String? = null,
|
||||
var advanceSearch: Int = -1,
|
||||
var minRating: Int = -1,
|
||||
var pageFrom: Int = -1,
|
||||
@@ -63,7 +62,7 @@ data class ListUrlBuilder(
|
||||
imagePath = null
|
||||
isUseSimilarityScan = false
|
||||
isOnlySearchCovers = false
|
||||
mSHash = null
|
||||
hash = null
|
||||
}
|
||||
|
||||
fun setIndex(index: String?, isNext: Boolean = true) {
|
||||
@@ -71,8 +70,8 @@ data class ListUrlBuilder(
|
||||
mPrev = index?.takeUnless { isNext }
|
||||
}
|
||||
|
||||
fun setJumpTo(jumpTo: String?) {
|
||||
mJumpTo = jumpTo
|
||||
fun setJumpTo(jumpTo: Int) {
|
||||
mJumpTo = jumpTo.takeUnless { it == 0 }?.toString()
|
||||
}
|
||||
|
||||
var keyword: String?
|
||||
@@ -82,6 +81,7 @@ data class ListUrlBuilder(
|
||||
}
|
||||
|
||||
fun set(q: QuickSearch) {
|
||||
reset()
|
||||
mode = q.mode
|
||||
this.category = q.category
|
||||
mKeyword = q.keyword
|
||||
@@ -89,21 +89,20 @@ data class ListUrlBuilder(
|
||||
minRating = q.minRating
|
||||
pageFrom = q.pageFrom
|
||||
pageTo = q.pageTo
|
||||
imagePath = null
|
||||
isUseSimilarityScan = false
|
||||
isOnlySearchCovers = false
|
||||
mNext = q.name.substringAfterLast('@', "").takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun toQuickSearch(): QuickSearch {
|
||||
val q = QuickSearch()
|
||||
q.mode = mode
|
||||
q.category = this.category
|
||||
q.keyword = mKeyword
|
||||
q.advanceSearch = advanceSearch
|
||||
q.minRating = minRating
|
||||
q.pageFrom = pageFrom
|
||||
q.pageTo = pageTo
|
||||
return q
|
||||
fun toQuickSearch(name: String): QuickSearch {
|
||||
return QuickSearch(
|
||||
name = name,
|
||||
mode = mode,
|
||||
category = category,
|
||||
keyword = mKeyword,
|
||||
advanceSearch = advanceSearch,
|
||||
minRating = minRating,
|
||||
pageFrom = pageFrom,
|
||||
pageTo = pageTo,
|
||||
)
|
||||
}
|
||||
|
||||
fun equalsQuickSearch(q: QuickSearch?): Boolean {
|
||||
@@ -116,7 +115,7 @@ data class ListUrlBuilder(
|
||||
if (q.category != this.category) {
|
||||
return false
|
||||
}
|
||||
if (!StringUtils.equals(q.keyword, mKeyword)) {
|
||||
if (q.keyword != mKeyword) {
|
||||
return false
|
||||
}
|
||||
if (q.advanceSearch != advanceSearch) {
|
||||
@@ -138,10 +137,10 @@ data class ListUrlBuilder(
|
||||
// TODO page
|
||||
fun setQuery(query: String?) {
|
||||
reset()
|
||||
if (TextUtils.isEmpty(query)) {
|
||||
if (query.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
val queries = StringUtils.split(query, '&')
|
||||
val queries = query.split('&')
|
||||
var category = 0
|
||||
var keyword: String? = null
|
||||
var enableAdvanceSearch = false
|
||||
@@ -160,7 +159,7 @@ data class ListUrlBuilder(
|
||||
val value = str.substring(index + 1)
|
||||
when (key) {
|
||||
"f_cats" -> {
|
||||
val cats = NumberUtils.parseIntSafely(value, EhUtils.ALL_CATEGORY)
|
||||
val cats = value.toIntOrDefault(EhUtils.ALL_CATEGORY)
|
||||
category = category or (cats.inv() and EhUtils.ALL_CATEGORY)
|
||||
}
|
||||
|
||||
@@ -216,37 +215,37 @@ data class ListUrlBuilder(
|
||||
}
|
||||
|
||||
"f_sh" -> if ("on" == value) {
|
||||
advanceSearch = advanceSearch or AdvanceSearchTable.SH
|
||||
advanceSearch = advanceSearch or AdvanceTable.SH
|
||||
}
|
||||
|
||||
"f_sto" -> if ("on" == value) {
|
||||
advanceSearch = advanceSearch or AdvanceSearchTable.STO
|
||||
advanceSearch = advanceSearch or AdvanceTable.STO
|
||||
}
|
||||
|
||||
"f_sfl" -> if ("on" == value) {
|
||||
advanceSearch = advanceSearch or AdvanceSearchTable.SFL
|
||||
advanceSearch = advanceSearch or AdvanceTable.SFL
|
||||
}
|
||||
|
||||
"f_sfu" -> if ("on" == value) {
|
||||
advanceSearch = advanceSearch or AdvanceSearchTable.SFU
|
||||
advanceSearch = advanceSearch or AdvanceTable.SFU
|
||||
}
|
||||
|
||||
"f_sft" -> if ("on" == value) {
|
||||
advanceSearch = advanceSearch or AdvanceSearchTable.SFT
|
||||
advanceSearch = advanceSearch or AdvanceTable.SFT
|
||||
}
|
||||
|
||||
"f_sr" -> if ("on" == value) {
|
||||
enableMinRating = true
|
||||
}
|
||||
|
||||
"f_srdd" -> minRating = NumberUtils.parseIntSafely(value, -1)
|
||||
"f_srdd" -> minRating = value.toIntOrDefault(-1)
|
||||
"f_sp" -> if ("on" == value) {
|
||||
enablePage = true
|
||||
}
|
||||
|
||||
"f_spf" -> pageFrom = NumberUtils.parseIntSafely(value, -1)
|
||||
"f_spt" -> pageTo = NumberUtils.parseIntSafely(value, -1)
|
||||
"f_shash" -> mSHash = value
|
||||
"f_spf" -> pageFrom = value.toIntOrDefault(-1)
|
||||
"f_spt" -> pageTo = value.toIntOrDefault(-1)
|
||||
"f_shash" -> hash = value
|
||||
}
|
||||
}
|
||||
this.category = category
|
||||
@@ -272,63 +271,66 @@ data class ListUrlBuilder(
|
||||
|
||||
fun build(): String {
|
||||
return when (mode) {
|
||||
MODE_NORMAL, MODE_SUBSCRIPTION -> {
|
||||
val url: String = if (mode == MODE_NORMAL) {
|
||||
EhUrl.host
|
||||
} else {
|
||||
EhUrl.watchedUrl
|
||||
}
|
||||
val ub = UrlBuilder(url)
|
||||
if (this.category != EhUtils.NONE) {
|
||||
ub.addQuery("f_cats", category.inv() and EhUtils.ALL_CATEGORY)
|
||||
}
|
||||
mSHash?.let {
|
||||
ub.addQuery("f_shash", it)
|
||||
}
|
||||
mJumpTo?.let {
|
||||
ub.addQuery("seek", it)
|
||||
}
|
||||
mPrev?.let {
|
||||
ub.addQuery("prev", it)
|
||||
}
|
||||
mNext?.let {
|
||||
ub.addQuery("next", it)
|
||||
MODE_NORMAL, MODE_SUBSCRIPTION -> ehUrl {
|
||||
if (mode == MODE_SUBSCRIPTION) addPathSegment(EhUrl.WATCHED_PATH)
|
||||
if (category != EhUtils.NONE) {
|
||||
addEncodedQueryParameter("f_cats", (category.inv() and EhUtils.ALL_CATEGORY).toString())
|
||||
}
|
||||
addQueryParameterIfNotBlank("f_shash", hash)
|
||||
addQueryParameterIfNotBlank("prev", mPrev)
|
||||
addQueryParameterIfNotBlank("next", mNext)
|
||||
addQueryParameterIfNotBlank("seek", mJumpTo)
|
||||
// Search key
|
||||
// the settings of ub:UrlBuilder may be overwritten by following Advance search
|
||||
StringUtils.split(mKeyword, '|')?.forEachIndexed { idx, keyword ->
|
||||
val keyword = keyword.trim { it <= ' ' }
|
||||
mKeyword?.split('|')?.forEachIndexed { idx, kwd ->
|
||||
val keyword = kwd.trim { it <= ' ' }
|
||||
when (idx) {
|
||||
0 -> keyword.takeIf { it.isNotEmpty() }?.let { ub.addQuery("f_search", encodeUTF8(it)) }
|
||||
0 -> addQueryParameterIfNotBlank("f_search", keyword)
|
||||
|
||||
else -> keyword.indexOf(':').takeIf { it >= 0 }?.run {
|
||||
val key = keyword.substring(0, this).trim { it <= ' ' }
|
||||
val value = keyword.substring(this + 1).trim { it <= ' ' }
|
||||
ub.addQuery(key, encodeUTF8(value))
|
||||
addQueryParameterIfNotBlank(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Advance search
|
||||
if (advanceSearch != -1) {
|
||||
ub.addQuery("advsearch", "1")
|
||||
if (advanceSearch and AdvanceSearchTable.SH != 0) ub.addQuery("f_sh", "on")
|
||||
if (advanceSearch and AdvanceSearchTable.STO != 0) ub.addQuery("f_sto", "on")
|
||||
if (advanceSearch and AdvanceSearchTable.SFL != 0) ub.addQuery("f_sfl", "on")
|
||||
if (advanceSearch and AdvanceSearchTable.SFU != 0) ub.addQuery("f_sfu", "on")
|
||||
if (advanceSearch and AdvanceSearchTable.SFT != 0) ub.addQuery("f_sft", "on")
|
||||
addQueryParameter("advsearch", "1")
|
||||
if (advanceSearch and AdvanceTable.SH != 0) {
|
||||
addQueryParameter("f_sh", "on")
|
||||
}
|
||||
if (advanceSearch and AdvanceTable.STO != 0) {
|
||||
addQueryParameter("f_sto", "on")
|
||||
}
|
||||
if (advanceSearch and AdvanceTable.SFL != 0) {
|
||||
addQueryParameter("f_sfl", "on")
|
||||
}
|
||||
if (advanceSearch and AdvanceTable.SFU != 0) {
|
||||
addQueryParameter("f_sfu", "on")
|
||||
}
|
||||
if (advanceSearch and AdvanceTable.SFT != 0) {
|
||||
addQueryParameter("f_sft", "on")
|
||||
}
|
||||
// Set min star
|
||||
if (minRating != -1) {
|
||||
ub.addQuery("f_sr", "on")
|
||||
ub.addQuery("f_srdd", minRating)
|
||||
addQueryParameter("f_sr", "on")
|
||||
addQueryParameter("f_srdd", "$minRating")
|
||||
}
|
||||
// Pages
|
||||
if (pageFrom != -1 || pageTo != -1) {
|
||||
ub.addQuery("f_sp", "on")
|
||||
ub.addQuery("f_spf", if (pageFrom != -1) pageFrom.toString() else "")
|
||||
ub.addQuery("f_spt", if (pageTo != -1) pageTo.toString() else "")
|
||||
addQueryParameter("f_sp", "on")
|
||||
addQueryParameter(
|
||||
"f_spf",
|
||||
(if (pageFrom != -1) pageFrom.toString() else "").toString(),
|
||||
)
|
||||
addQueryParameter(
|
||||
"f_spt",
|
||||
(if (pageTo != -1) pageTo.toString() else "").toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
ub.build()
|
||||
}
|
||||
}.toString()
|
||||
|
||||
MODE_UPLOADER -> {
|
||||
val sb = StringBuilder(EhUrl.host)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class CloudflareBypassException : EhException(R.string.cloudflare_bypass_failed)
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import splitties.init.appCtx
|
||||
|
||||
open class EhException : Exception {
|
||||
constructor(detailMessage: String?) : super(detailMessage)
|
||||
constructor(detailMessage: String?, cause: Throwable?) : super(detailMessage, cause)
|
||||
constructor(@StringRes message: Int) : super(appCtx.getString(message))
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class InsufficientFundsException : EhException(R.string.insufficient_funds)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class NotLoggedInException : EhException(R.string.sign_in_first)
|
||||
@@ -16,6 +16,6 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
/**
|
||||
* It is an exception for get offensive tip for g.e-hentai.org
|
||||
* It is an exception for get offensive tip for e-hentai.org
|
||||
*/
|
||||
class OffensiveException : EhException("OFFENSIVE")
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.InsufficientFundsException
|
||||
import com.hippo.ehviewer.client.exception.NoHAtHClientException
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
@@ -27,8 +28,10 @@ object ArchiveParser {
|
||||
Regex("<form id=\"hathdl_form\" action=\"[^\"]*?or=([^=\"]*?)\" method=\"post\">")
|
||||
private val PATTERN_HATH_ARCHIVE =
|
||||
Regex("<p><a href=\"[^\"]*\" onclick=\"return do_hathdl\\('([0-9]+|org)'\\)\">([^<]+)</a></p>\\s*<p>([\\w. ]+)</p>\\s*<p>([\\w. ]+)</p>")
|
||||
private val PATTERN_NEED_HATH_CLIENT =
|
||||
Regex("You must have a H@H client assigned to your account to use this feature\\.")
|
||||
private const val ERROR_NEED_HATH_CLIENT =
|
||||
"You must have a H@H client assigned to your account to use this feature."
|
||||
private const val ERROR_INSUFFICIENT_FUNDS =
|
||||
"You do not have enough funds to download this archive."
|
||||
|
||||
fun parse(body: String): Result {
|
||||
val m = PATTERN_HATH_FORM.find(body)!!
|
||||
@@ -65,9 +68,12 @@ object ArchiveParser {
|
||||
return result
|
||||
}
|
||||
|
||||
@Throws(NoHAtHClientException::class)
|
||||
fun parseArchiveUrl(body: String): String? {
|
||||
if (PATTERN_NEED_HATH_CLIENT.containsMatchIn(body)) throw NoHAtHClientException("No H@H client")
|
||||
if (body.contains(ERROR_NEED_HATH_CLIENT)) {
|
||||
throw NoHAtHClientException("No H@H client")
|
||||
} else if (body.contains(ERROR_INSUFFICIENT_FUNDS)) {
|
||||
throw InsufficientFundsException()
|
||||
}
|
||||
return Jsoup.parse(body).selectFirst("#continue>a[href]")
|
||||
?.let { it.attr("href") + "?start=1" }
|
||||
// TODO: Check more errors
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Error(val error: String)
|
||||
@@ -1,62 +1,31 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.GetText.getString
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.JsoupUtils
|
||||
import org.jsoup.Jsoup
|
||||
import splitties.init.appCtx
|
||||
|
||||
object FavoritesParser {
|
||||
fun parse(body: String): Result {
|
||||
suspend fun parse(body: String): Result {
|
||||
if (body.contains("This page requires you to log on.</p>")) {
|
||||
throw EhException(getString(R.string.need_sign_in))
|
||||
throw EhException(appCtx.getString(R.string.need_sign_in))
|
||||
}
|
||||
val catArray = arrayOfNulls<String>(10)
|
||||
val countArray = IntArray(10)
|
||||
val d = Jsoup.parse(body)
|
||||
runCatching {
|
||||
val ido = JsoupUtils.getElementByClass(d, "ido")
|
||||
val fps = ido!!.getElementsByClass("fp")
|
||||
// Last one is "fp fps"
|
||||
check(fps.size == 11)
|
||||
for (i in 0..9) {
|
||||
val fp = fps[i]
|
||||
countArray[i] = ParserUtils.parseInt(fp.child(0).text(), 0)
|
||||
catArray[i] = ParserUtils.trim(fp.child(2).text())
|
||||
}
|
||||
}.onFailure {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
it.printStackTrace()
|
||||
throw ParseException("Parse favorites error", body)
|
||||
}
|
||||
val result = GalleryListParser.parse(d, body)
|
||||
return Result(catArray, countArray, result)
|
||||
val countArray = parseFav(body, catArray)
|
||||
if (countArray.isEmpty()) throw ParseException("Parse favorites error", body)
|
||||
val result = GalleryListParser.parse(body)
|
||||
return Result(catArray.requireNoNulls(), countArray, result)
|
||||
}
|
||||
|
||||
class Result(
|
||||
val catArray: Array<String?>,
|
||||
val catArray: Array<String>,
|
||||
val countArray: IntArray,
|
||||
galleryListResult: GalleryListParser.Result,
|
||||
galleryListResult: GalleryListResult,
|
||||
) {
|
||||
val prev = galleryListResult.prev
|
||||
val next = galleryListResult.next
|
||||
val galleryInfoList = galleryListResult.galleryInfoList
|
||||
}
|
||||
}
|
||||
|
||||
private external fun parseFav(body: String, favCat: Array<String?>): IntArray
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.NotLoggedInException
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
@@ -29,7 +29,7 @@ object ForumsParser {
|
||||
child.attr("href")
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
throw EhException("Not logged in")
|
||||
throw NotLoggedInException()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,30 +19,46 @@ import com.hippo.ehviewer.client.EhUtils.getCategory
|
||||
import com.hippo.ehviewer.client.EhUtils.handleThumbUrlResolution
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.getThumbKey
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import org.json.JSONObject
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.unescapeXml
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryApiParser {
|
||||
fun parse(body: String, galleryInfoList: List<GalleryInfo>) {
|
||||
val jo = JSONObject(body)
|
||||
val ja = jo.getJSONArray("gmetadata")
|
||||
for (i in 0 until ja.length()) {
|
||||
val g = ja.getJSONObject(i)
|
||||
val gid = g.getLong("gid")
|
||||
val gi = galleryInfoList.find { it.gid == gid } ?: continue
|
||||
gi.title = ParserUtils.trim(g.getString("title"))
|
||||
gi.titleJpn = ParserUtils.trim(g.getString("title_jpn"))
|
||||
gi.category = getCategory(g.getString("category"))
|
||||
gi.thumbKey = getThumbKey(handleThumbUrlResolution(g.getString("thumb"))!!)
|
||||
gi.uploader = g.getString("uploader")
|
||||
gi.posted =
|
||||
ParserUtils.formatDate(ParserUtils.parseLong(g.getString("posted"), 0) * 1000)
|
||||
gi.rating = NumberUtils.parseFloatSafely(g.getString("rating"), 0.0f)
|
||||
// tags
|
||||
val tagJa = g.getJSONArray("tags")
|
||||
gi.simpleTags = (0 until tagJa.length()).map { tagJa.getString(it) }.toTypedArray()
|
||||
gi.pages = NumberUtils.parseIntSafely(g.getString("filecount"), 0)
|
||||
gi.generateSLang()
|
||||
body.parseAs<Result>().items.forEach { item ->
|
||||
val gi = galleryInfoList.find { it.gid == item.gid } ?: return@forEach
|
||||
gi.apply {
|
||||
title = item.title.unescapeXml()
|
||||
titleJpn = item.titleJpn.unescapeXml()
|
||||
category = getCategory(item.category)
|
||||
thumbKey = getThumbKey(handleThumbUrlResolution(item.thumb)!!)
|
||||
uploader = item.uploader.unescapeXml()
|
||||
posted = ParserUtils.formatDate(item.posted * 1000)
|
||||
rating = item.rating
|
||||
simpleTags = item.tags
|
||||
pages = item.pages
|
||||
generateSLang()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Result(@SerialName("gmetadata") val items: List<Item>)
|
||||
|
||||
@Serializable
|
||||
data class Item(
|
||||
val gid: Long,
|
||||
val title: String,
|
||||
@SerialName("title_jpn")
|
||||
val titleJpn: String,
|
||||
val category: String,
|
||||
val thumb: String,
|
||||
val uploader: String,
|
||||
val posted: Long,
|
||||
@SerialName("filecount")
|
||||
val pages: Int,
|
||||
val rating: Float,
|
||||
val tags: ArrayList<String>,
|
||||
)
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryComment
|
||||
import com.hippo.ehviewer.client.data.GalleryCommentList
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.LOCAL_FAVORITED
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import com.hippo.ehviewer.client.data.GalleryPreview
|
||||
import com.hippo.ehviewer.client.data.GalleryTagGroup
|
||||
import com.hippo.ehviewer.client.data.LargeGalleryPreview
|
||||
@@ -35,11 +37,10 @@ import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.exception.PiningException
|
||||
import com.hippo.ehviewer.client.getThumbKey
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.JsoupUtils
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.ehviewer.yorozuya.StringUtils
|
||||
import com.hippo.ehviewer.yorozuya.trimAnd
|
||||
import com.hippo.ehviewer.yorozuya.unescapeXml
|
||||
import com.hippo.ehviewer.util.toFloatOrDefault
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import com.hippo.ehviewer.util.trimAnd
|
||||
import com.hippo.ehviewer.util.unescapeXml
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
@@ -82,8 +83,7 @@ object GalleryDetailParser {
|
||||
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>"
|
||||
private const val PINING_STRING = "<p>This gallery is pining for the fjords.</p>"
|
||||
|
||||
@Throws(EhException::class)
|
||||
fun parse(body: String): GalleryDetail {
|
||||
suspend fun parse(body: String): GalleryDetail {
|
||||
if (body.contains(OFFENSIVE_STRING)) {
|
||||
throw OffensiveException()
|
||||
}
|
||||
@@ -108,8 +108,7 @@ object GalleryDetailParser {
|
||||
return galleryDetail
|
||||
}
|
||||
|
||||
@Throws(ParseException::class)
|
||||
private fun parseDetail(gd: GalleryDetail, d: Document, body: String) {
|
||||
private suspend fun parseDetail(gd: GalleryDetail, d: Document, body: String) {
|
||||
PATTERN_DETAIL.find(body)?.apply {
|
||||
gd.gid = groupValues[1].toLongOrNull() ?: -1L
|
||||
gd.token = groupValues[2]
|
||||
@@ -141,11 +140,11 @@ object GalleryDetailParser {
|
||||
gd.titleJpn = gm.getElementById("gj")?.text()?.trim()
|
||||
|
||||
// Category
|
||||
val gdc = gm.getElementById("gdc")
|
||||
try {
|
||||
var ce = JsoupUtils.getElementByClass(gdc, "cn")
|
||||
val gdc = gm.getElementById("gdc")!!
|
||||
var ce = gdc.getElementsByClass("cn").first()
|
||||
if (ce == null) {
|
||||
ce = JsoupUtils.getElementByClass(gdc, "cs")
|
||||
ce = gdc.getElementsByClass("cs").first()
|
||||
}
|
||||
gd.category = getCategory(ce!!.text())
|
||||
} catch (e: Throwable) {
|
||||
@@ -157,7 +156,7 @@ object GalleryDetailParser {
|
||||
val gdn = gm.getElementById("gdn")
|
||||
if (null != gdn) {
|
||||
gd.disowned = gdn.attr("style").contains("opacity:0.5")
|
||||
gd.uploader = StringUtils.trim(gdn.text())
|
||||
gd.uploader = gdn.text().trim()
|
||||
} else {
|
||||
gd.uploader = ""
|
||||
}
|
||||
@@ -176,10 +175,7 @@ object GalleryDetailParser {
|
||||
// Rating count
|
||||
val ratingCount = gm.getElementById("rating_count")
|
||||
if (null != ratingCount) {
|
||||
gd.ratingCount = NumberUtils.parseIntSafely(
|
||||
StringUtils.trim(ratingCount.text()),
|
||||
0,
|
||||
)
|
||||
gd.ratingCount = ratingCount.text().trim().toIntOrDefault(0)
|
||||
} else {
|
||||
gd.ratingCount = 0
|
||||
}
|
||||
@@ -187,7 +183,7 @@ object GalleryDetailParser {
|
||||
// Rating
|
||||
val ratingLabel = gm.getElementById("rating_label")
|
||||
if (null != ratingLabel) {
|
||||
val ratingStr = StringUtils.trim(ratingLabel.text())
|
||||
val ratingStr = ratingLabel.text().trim()
|
||||
if ("Not Yet Rated" == ratingStr) {
|
||||
gd.rating = -1.0f
|
||||
} else {
|
||||
@@ -195,7 +191,7 @@ object GalleryDetailParser {
|
||||
if (index == -1 || index >= ratingStr.length) {
|
||||
gd.rating = 0f
|
||||
} else {
|
||||
gd.rating = NumberUtils.parseFloatSafely(ratingStr.substring(index + 1), 0f)
|
||||
gd.rating = ratingStr.substring(index + 1).toFloatOrDefault(0f)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -204,21 +200,17 @@ object GalleryDetailParser {
|
||||
|
||||
// isFavorited
|
||||
val gdf = gm.getElementById("gdf")
|
||||
gd.isFavorited = false
|
||||
if (gdf != null) {
|
||||
val favoriteName = StringUtils.trim(gdf.text())
|
||||
if (favoriteName == "Add to Favorites") {
|
||||
gd.favoriteName = null
|
||||
} else {
|
||||
gd.isFavorited = true
|
||||
gd.favoriteName = StringUtils.trim(gdf.text())
|
||||
val favoriteName = gdf.text().trim()
|
||||
if (favoriteName != "Add to Favorites") {
|
||||
gd.favoriteName = gdf.text().trim()
|
||||
PATTERN_FAVORITE_SLOT.find(body)?.run {
|
||||
gd.favoriteSlot = ((groupValues[1].toIntOrNull() ?: 2) - 2) / 19
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gd.favoriteSlot == -2 && EhDB.containLocalFavorites(gd.gid)) {
|
||||
gd.favoriteSlot = -1
|
||||
if (gd.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(gd.gid)) {
|
||||
gd.favoriteSlot = LOCAL_FAVORITED
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
@@ -234,7 +226,7 @@ object GalleryDetailParser {
|
||||
if (result != null) {
|
||||
gi.gid = result.gid
|
||||
gi.token = result.token
|
||||
gi.title = StringUtils.trim(element.text())
|
||||
gi.title = element.text().trim()
|
||||
gi.posted = dates[index]
|
||||
gd.newerVersions.add(gi)
|
||||
}
|
||||
@@ -247,8 +239,8 @@ object GalleryDetailParser {
|
||||
if (es.size < 2) {
|
||||
return
|
||||
}
|
||||
val key = StringUtils.trim(es[0].text())
|
||||
val value = StringUtils.trim(es[1].ownText())
|
||||
val key = es[0].text().trim()
|
||||
val value = es[1].ownText().trim()
|
||||
if (key.startsWith("Posted")) {
|
||||
gd.posted = value
|
||||
} else if (key.startsWith("Parent")) {
|
||||
@@ -265,7 +257,7 @@ object GalleryDetailParser {
|
||||
} else if (key.startsWith("Length")) {
|
||||
val index = value.indexOf(' ')
|
||||
if (index >= 0) {
|
||||
gd.pages = NumberUtils.parseIntSafely(value.substring(0, index), 1)
|
||||
gd.pages = value.substring(0, index).toIntOrDefault(1)
|
||||
} else {
|
||||
gd.pages = 1
|
||||
}
|
||||
@@ -278,7 +270,7 @@ object GalleryDetailParser {
|
||||
if (index == -1) {
|
||||
gd.favoriteCount = 0
|
||||
} else {
|
||||
gd.favoriteCount = NumberUtils.parseIntSafely(value.substring(0, index), 0)
|
||||
gd.favoriteCount = value.substring(0, index).toIntOrDefault(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,11 +279,10 @@ object GalleryDetailParser {
|
||||
|
||||
private fun parseTagGroup(element: Element): GalleryTagGroup? {
|
||||
return try {
|
||||
val group = GalleryTagGroup()
|
||||
var nameSpace = element.child(0).text()
|
||||
// Remove last ':'
|
||||
nameSpace = nameSpace.substring(0, nameSpace.length - 1)
|
||||
group.groupName = nameSpace
|
||||
val group = GalleryTagGroup(nameSpace)
|
||||
val tags = element.child(1).children()
|
||||
tags.forEach {
|
||||
var tag = it.text()
|
||||
@@ -338,7 +329,7 @@ object GalleryDetailParser {
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseComment(element: Element): GalleryComment? {
|
||||
private suspend fun parseComment(element: Element): GalleryComment? {
|
||||
return try {
|
||||
val comment = GalleryComment()
|
||||
// Id
|
||||
@@ -346,7 +337,7 @@ object GalleryDetailParser {
|
||||
val name = a!!.attr("name")
|
||||
comment.id = name trimAnd { substring(1).toInt().toLong() }
|
||||
// Editable, vote up and vote down
|
||||
val c4 = JsoupUtils.getElementByClass(element, "c4")
|
||||
val c4 = element.getElementsByClass("c4").first()
|
||||
if (null != c4) {
|
||||
if ("Uploader Comment" == c4.text()) {
|
||||
comment.uploader = true
|
||||
@@ -355,12 +346,12 @@ object GalleryDetailParser {
|
||||
when (e.text()) {
|
||||
"Vote+" -> {
|
||||
comment.voteUpAble = true
|
||||
comment.voteUpEd = StringUtils.trim(e.attr("style")).isNotEmpty()
|
||||
comment.voteUpEd = e.attr("style").trim().isNotEmpty()
|
||||
}
|
||||
|
||||
"Vote-" -> {
|
||||
comment.voteDownAble = true
|
||||
comment.voteDownEd = StringUtils.trim(e.attr("style")).isNotEmpty()
|
||||
comment.voteDownEd = e.attr("style").trim().isNotEmpty()
|
||||
}
|
||||
|
||||
"Edit" -> comment.editable = true
|
||||
@@ -368,23 +359,20 @@ object GalleryDetailParser {
|
||||
}
|
||||
}
|
||||
// Vote state
|
||||
val c7 = JsoupUtils.getElementByClass(element, "c7")
|
||||
val c7 = element.getElementsByClass("c7").first()
|
||||
if (null != c7) {
|
||||
comment.voteState = StringUtils.trim(c7.text())
|
||||
comment.voteState = c7.text().trim()
|
||||
}
|
||||
// Score
|
||||
val c5 = JsoupUtils.getElementByClass(element, "c5")
|
||||
val c5 = element.getElementsByClass("c5").first()
|
||||
if (null != c5) {
|
||||
val es = c5.children()
|
||||
if (!es.isEmpty()) {
|
||||
comment.score = NumberUtils.parseIntSafely(
|
||||
StringUtils.trim(es[0].text()),
|
||||
0,
|
||||
)
|
||||
comment.score = es[0].text().trim().toIntOrDefault(0)
|
||||
}
|
||||
}
|
||||
// time
|
||||
val c3 = JsoupUtils.getElementByClass(element, "c3")
|
||||
val c3 = element.getElementsByClass("c3").first()
|
||||
val temp = c3!!.ownText()
|
||||
val time = if (temp.endsWith(':')) {
|
||||
// user
|
||||
@@ -395,7 +383,7 @@ object GalleryDetailParser {
|
||||
}
|
||||
comment.time = Instant.from(WEB_COMMENT_DATE_FORMAT.parse(time)).toEpochMilli()
|
||||
// comment
|
||||
val c6 = JsoupUtils.getElementByClass(element, "c6")
|
||||
val c6 = element.getElementsByClass("c6").first()
|
||||
// fix underline support
|
||||
for (e in c6!!.children()) {
|
||||
if ("span" == e.tagName() && "text-decoration:underline;" == e.attr("style")) {
|
||||
@@ -411,7 +399,7 @@ object GalleryDetailParser {
|
||||
}
|
||||
}
|
||||
// last edited
|
||||
val c8 = JsoupUtils.getElementByClass(element, "c8")
|
||||
val c8 = element.getElementsByClass("c8").first()
|
||||
if (c8 != null) {
|
||||
val e = c8.children().first()
|
||||
if (e != null) {
|
||||
@@ -430,7 +418,7 @@ object GalleryDetailParser {
|
||||
/**
|
||||
* Parse comments with html parser
|
||||
*/
|
||||
fun parseComments(document: Document): GalleryCommentList {
|
||||
suspend fun parseComments(document: Document): GalleryCommentList {
|
||||
return try {
|
||||
val cdiv = document.getElementById("cdiv")!!
|
||||
val c1s = cdiv.getElementsByClass("c1")
|
||||
@@ -460,7 +448,6 @@ object GalleryDetailParser {
|
||||
/**
|
||||
* Parse preview pages with regular expressions
|
||||
*/
|
||||
@Throws(ParseException::class)
|
||||
fun parsePreviewPages(body: String): Int {
|
||||
return PATTERN_PREVIEW_PAGES.find(body)?.groupValues?.get(1)?.toIntOrNull()
|
||||
?: throw ParseException("Parse preview page count error", body)
|
||||
@@ -469,7 +456,6 @@ object GalleryDetailParser {
|
||||
/**
|
||||
* Parse pages with regular expressions
|
||||
*/
|
||||
@Throws(ParseException::class)
|
||||
fun parsePages(body: String): Int {
|
||||
return PATTERN_PAGES.find(body)?.groupValues?.get(1)?.toIntOrNull()
|
||||
?: throw ParseException("Parse pages error", body)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.ehviewer.util.toLongOrDefault
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,7 @@ import java.util.regex.Pattern
|
||||
*/
|
||||
object GalleryDetailUrlParser {
|
||||
private val URL_STRICT_PATTERN = Pattern.compile(
|
||||
"https?://(?:" + EhUrl.DOMAIN_EX + "|" + EhUrl.DOMAIN_E + "|" + EhUrl.DOMAIN_LOFI + ")/(?:g|mpv)/(\\d+)/([0-9a-f]{10})",
|
||||
"https?://(?:${EhUrl.DOMAIN_EX}|${EhUrl.DOMAIN_E}(?:/lofi)?)/(?:g|mpv)/(\\d+)/([0-9a-f]{10})",
|
||||
)
|
||||
private val URL_PATTERN = Pattern.compile("(\\d+)/([0-9a-f]{10})(?:[^0-9a-f]|$)")
|
||||
|
||||
@@ -33,7 +33,7 @@ object GalleryDetailUrlParser {
|
||||
val pattern = if (strict) URL_STRICT_PATTERN else URL_PATTERN
|
||||
val m = pattern.matcher(url)
|
||||
return if (m.find()) {
|
||||
val gid = NumberUtils.parseLongSafely(m.group(1), 0).takeIf { it > 0 }
|
||||
val gid = m.group(1)!!.toLongOrDefault(0).takeIf { it > 0 }
|
||||
gid?.let { Result(it, m.group(2)!!) }
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -1,343 +1,38 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import android.util.Log
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.LOCAL_FAVORITED
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.getThumbKey
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.JsoupUtils
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object GalleryListParser {
|
||||
private val TAG = GalleryListParser::class.java.simpleName
|
||||
private const val NO_UNFILTERED_TEXT =
|
||||
"No unfiltered results in this page range. You either requested an invalid page or used too aggressive filters."
|
||||
private val PATTERN_RATING = Pattern.compile("\\d+px")
|
||||
private val PATTERN_THUMB_SIZE = Pattern.compile("height:(\\d+)px;width:(\\d+)px")
|
||||
private val PATTERN_FAVORITE_SLOT =
|
||||
Pattern.compile("background-color:rgba\\((\\d+),(\\d+),(\\d+),")
|
||||
private val PATTERN_PAGES = Pattern.compile("(\\d+) page")
|
||||
private val PATTERN_NEXT_PAGE = Pattern.compile("p=(\\d+)")
|
||||
private val PATTERN_PREV = Pattern.compile("prev=(\\d+(-\\d+)?)")
|
||||
private val PATTERN_NEXT = Pattern.compile("next=(\\d+(-\\d+)?)")
|
||||
private val FAVORITE_SLOT_RGB = arrayOf(
|
||||
Triple("0", "0", "0"),
|
||||
Triple("240", "0", "0"),
|
||||
Triple("240", "160", "0"),
|
||||
Triple("208", "208", "0"),
|
||||
Triple("0", "128", "0"),
|
||||
Triple("144", "240", "64"),
|
||||
Triple("64", "176", "240"),
|
||||
Triple("0", "0", "240"),
|
||||
Triple("80", "0", "128"),
|
||||
Triple("224", "128", "224"),
|
||||
)
|
||||
|
||||
private fun parseRating(ratingStyle: String): String? {
|
||||
val m = PATTERN_RATING.matcher(ratingStyle)
|
||||
var num1 = Int.MIN_VALUE
|
||||
var num2 = Int.MIN_VALUE
|
||||
var rate = 5
|
||||
if (m.find()) {
|
||||
num1 = ParserUtils.parseInt(m.group().replace("px", ""), Int.MIN_VALUE)
|
||||
}
|
||||
if (m.find()) {
|
||||
num2 = ParserUtils.parseInt(m.group().replace("px", ""), Int.MIN_VALUE)
|
||||
}
|
||||
if (num1 == Int.MIN_VALUE || num2 == Int.MIN_VALUE) {
|
||||
return null
|
||||
}
|
||||
rate -= num1 / 16
|
||||
return if (num2 == 21) {
|
||||
"${rate - 1}.5"
|
||||
} else {
|
||||
rate.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseFavoriteSlot(style: String): Int {
|
||||
val m = PATTERN_FAVORITE_SLOT.matcher(style)
|
||||
if (m.find()) {
|
||||
val r = m.group(1)!!
|
||||
val g = m.group(2)!!
|
||||
val b = m.group(3)!!
|
||||
return FAVORITE_SLOT_RGB.indexOf(Triple(r, g, b))
|
||||
}
|
||||
return -2
|
||||
}
|
||||
|
||||
private fun parseGalleryInfo(e: Element): GalleryInfo? {
|
||||
val gi: GalleryInfo = BaseGalleryInfo()
|
||||
|
||||
// Title, gid, token (required), tags
|
||||
val glname = JsoupUtils.getElementByClass(e, "glname")
|
||||
if (glname != null) {
|
||||
var a = JsoupUtils.getElementByTag(glname, "a")
|
||||
if (a == null) {
|
||||
val parent = glname.parent()
|
||||
if (parent != null && "a" == parent.tagName()) {
|
||||
a = parent
|
||||
}
|
||||
}
|
||||
if (a != null) {
|
||||
val result = GalleryDetailUrlParser.parse(a.attr("href"))
|
||||
if (result != null) {
|
||||
gi.gid = result.gid
|
||||
gi.token = result.token
|
||||
}
|
||||
}
|
||||
var child: Element = glname
|
||||
var children = glname.children()
|
||||
while (children.size != 0) {
|
||||
child = children[0]
|
||||
children = child.children()
|
||||
}
|
||||
gi.title = child.text().trim { it <= ' ' }
|
||||
}
|
||||
if (gi.title == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Tags
|
||||
val gts = e.select(".gt, .gtl")
|
||||
if (gts.size != 0) {
|
||||
val tags = ArrayList<String>()
|
||||
for (gt in gts) {
|
||||
tags.add(gt.attr("title"))
|
||||
}
|
||||
gi.simpleTags = tags.toTypedArray()
|
||||
}
|
||||
|
||||
// Category
|
||||
gi.category = EhUtils.UNKNOWN
|
||||
var ce = JsoupUtils.getElementByClass(e, "cn")
|
||||
if (ce == null) {
|
||||
ce = JsoupUtils.getElementByClass(e, "cs")
|
||||
}
|
||||
if (ce != null) {
|
||||
gi.category = EhUtils.getCategory(ce.text())
|
||||
}
|
||||
|
||||
// Thumb
|
||||
var thumbUrl: String? = null
|
||||
val glthumb = JsoupUtils.getElementByClass(e, "glthumb")
|
||||
if (glthumb != null) {
|
||||
val img = glthumb.select("div:nth-child(1)>img").first()
|
||||
if (img != null) {
|
||||
// Thumb size
|
||||
val m = PATTERN_THUMB_SIZE.matcher(img.attr("style"))
|
||||
if (m.find()) {
|
||||
gi.thumbWidth = NumberUtils.parseIntSafely(m.group(2), 0)
|
||||
gi.thumbHeight = NumberUtils.parseIntSafely(m.group(1), 0)
|
||||
} else {
|
||||
Log.w(TAG, "Can't parse gallery info thumb size")
|
||||
gi.thumbWidth = 0
|
||||
gi.thumbHeight = 0
|
||||
}
|
||||
// Thumb url
|
||||
var url = img.attr("data-src")
|
||||
if (url.isEmpty()) {
|
||||
url = img.attr("src")
|
||||
}
|
||||
if (url.isNotEmpty()) {
|
||||
thumbUrl = EhUtils.handleThumbUrlResolution(url)!!
|
||||
}
|
||||
}
|
||||
|
||||
// Pages
|
||||
val div = glthumb.select("div:nth-child(2)>div:nth-child(2)>div:nth-child(2)").first()
|
||||
if (div != null) {
|
||||
val matcher = PATTERN_PAGES.matcher(div.text())
|
||||
if (matcher.find()) {
|
||||
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0)
|
||||
suspend fun parse(body: String) = runCatching {
|
||||
parseGalleryInfoList(body).apply {
|
||||
galleryInfoList.onEach {
|
||||
if (it.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(it.gid)) {
|
||||
it.favoriteSlot = LOCAL_FAVORITED
|
||||
}
|
||||
it.generateSLang()
|
||||
}
|
||||
}
|
||||
// Try extended and thumbnail version
|
||||
if (thumbUrl == null) {
|
||||
var gl = JsoupUtils.getElementByClass(e, "gl1e")
|
||||
if (gl == null) {
|
||||
gl = JsoupUtils.getElementByClass(e, "gl3t")
|
||||
}
|
||||
if (gl != null) {
|
||||
val img = JsoupUtils.getElementByTag(gl, "img")
|
||||
if (img != null) {
|
||||
// Thumb size
|
||||
val m = PATTERN_THUMB_SIZE.matcher(img.attr("style"))
|
||||
if (m.find()) {
|
||||
gi.thumbWidth = NumberUtils.parseIntSafely(m.group(2), 0)
|
||||
gi.thumbHeight = NumberUtils.parseIntSafely(m.group(1), 0)
|
||||
} else {
|
||||
Log.w(TAG, "Can't parse gallery info thumb size")
|
||||
gi.thumbWidth = 0
|
||||
gi.thumbHeight = 0
|
||||
}
|
||||
thumbUrl = EhUtils.handleThumbUrlResolution(img.attr("src"))
|
||||
}
|
||||
}
|
||||
}.getOrElse {
|
||||
if (body.contains("<p>You do not have any watched tags")) {
|
||||
throw EhException(R.string.gallery_list_empty_hit_subscription)
|
||||
}
|
||||
|
||||
gi.thumbKey = getThumbKey(thumbUrl!!)
|
||||
|
||||
// Posted
|
||||
val posted = e.getElementById("posted_" + gi.gid)
|
||||
if (posted != null) {
|
||||
gi.posted = posted.text().trim { it <= ' ' }
|
||||
gi.favoriteSlot = parseFavoriteSlot(posted.attr("style"))
|
||||
if (body.contains("No hits found</p>")) {
|
||||
throw EhException(R.string.gallery_list_empty_hit)
|
||||
}
|
||||
if (gi.favoriteSlot < 0) {
|
||||
gi.favoriteSlot = if (EhDB.containLocalFavorites(gi.gid)) -1 else -2
|
||||
}
|
||||
|
||||
// Rating
|
||||
val ir = JsoupUtils.getElementByClass(e, "ir")
|
||||
if (ir != null) {
|
||||
gi.rating = NumberUtils.parseFloatSafely(parseRating(ir.attr("style")), -1.0f)
|
||||
// TODO The gallery may be rated even if it doesn't has one of these classes
|
||||
gi.rated = ir.hasClass("irr") || ir.hasClass("irg") || ir.hasClass("irb")
|
||||
}
|
||||
|
||||
// Uploader and pages
|
||||
var gl = JsoupUtils.getElementByClass(e, "glhide")
|
||||
var uploaderIndex = 0
|
||||
var pagesIndex = 1
|
||||
if (gl == null) {
|
||||
// For extended
|
||||
gl = JsoupUtils.getElementByClass(e, "gl3e")
|
||||
uploaderIndex = 3
|
||||
pagesIndex = 4
|
||||
}
|
||||
if (gl != null) {
|
||||
val children = gl.children()
|
||||
if (children.size > uploaderIndex) {
|
||||
val div = children[uploaderIndex]
|
||||
if (div != null) {
|
||||
gi.disowned = div.attr("style").contains("opacity:0.5")
|
||||
val a = div.children().first()
|
||||
gi.uploader = a?.text()?.trim { it <= ' ' } ?: div.text().trim { it <= ' ' }
|
||||
}
|
||||
}
|
||||
if (children.size > pagesIndex) {
|
||||
val matcher = PATTERN_PAGES.matcher(children[pagesIndex].text())
|
||||
if (matcher.find()) {
|
||||
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
// For thumbnail
|
||||
val gl5t = JsoupUtils.getElementByClass(e, "gl5t")
|
||||
if (gl5t != null) {
|
||||
val div = gl5t.select("div:nth-child(2)>div:nth-child(2)").first()
|
||||
if (div != null) {
|
||||
val matcher = PATTERN_PAGES.matcher(div.text())
|
||||
if (matcher.find()) {
|
||||
gi.pages = NumberUtils.parseIntSafely(matcher.group(1), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
gi.generateSLang()
|
||||
return gi
|
||||
}
|
||||
|
||||
fun parse(body: String): Result {
|
||||
val d = Jsoup.parse(body)
|
||||
return parse(d, body)
|
||||
}
|
||||
|
||||
fun parse(d: Document, body: String): Result {
|
||||
val result = Result()
|
||||
try {
|
||||
val prev = d.getElementById("uprev")
|
||||
val next = d.getElementById("unext")
|
||||
assert(prev != null)
|
||||
assert(next != null)
|
||||
val matcherPrev = PATTERN_PREV.matcher(prev!!.attr("href"))
|
||||
val matcherNext = PATTERN_NEXT.matcher(next!!.attr("href"))
|
||||
if (matcherPrev.find()) result.prev = matcherPrev.group(1)
|
||||
if (matcherNext.find()) result.next = matcherNext.group(1)
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
result.noWatchedTags = body.contains("<p>You do not have any watched tags")
|
||||
if (body.contains("No hits found</p>")) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
try { // For toplists
|
||||
val ptt = d.getElementsByClass("ptt").first()
|
||||
if (ptt != null) {
|
||||
val es = ptt.child(0).child(0).children()
|
||||
result.pages = es[es.size - 2].text().trim { it <= ' ' }.toInt()
|
||||
var e = es[es.size - 1]
|
||||
if (e != null) {
|
||||
e = e.children().first()
|
||||
if (e != null) {
|
||||
val href = e.attr("href")
|
||||
val matcher = PATTERN_NEXT_PAGE.matcher(href)
|
||||
if (matcher.find()) {
|
||||
result.nextPage = NumberUtils.parseIntSafely(matcher.group(1), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
try {
|
||||
val itg = d.getElementsByClass("itg").first()
|
||||
val es = if ("table".equals(itg!!.tagName(), ignoreCase = true)) {
|
||||
itg.child(0).children()
|
||||
} else {
|
||||
itg.children()
|
||||
}
|
||||
val list = result.galleryInfoList
|
||||
// First one is table header, skip it
|
||||
for (i in es.indices) {
|
||||
val gi = parseGalleryInfo(es[i])
|
||||
if (null != gi) {
|
||||
list.add(gi)
|
||||
}
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
if (es.size < 2 || NO_UNFILTERED_TEXT != es[1].text()) {
|
||||
Log.d(TAG, "No gallery found")
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
e.printStackTrace()
|
||||
throw ParseException("Can't parse gallery list", body)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class Result {
|
||||
var pages = 0
|
||||
var nextPage = 0
|
||||
var prev: String? = null
|
||||
var next: String? = null
|
||||
var noWatchedTags = false
|
||||
val galleryInfoList = mutableListOf<GalleryInfo>()
|
||||
throw ParseException("Can't parse gallery list", body, it)
|
||||
}
|
||||
}
|
||||
|
||||
class GalleryListResult(
|
||||
val prev: String?,
|
||||
val next: String?,
|
||||
val galleryInfoList: ArrayList<BaseGalleryInfo>,
|
||||
)
|
||||
|
||||
private external fun parseGalleryInfoList(e: String): GalleryListResult
|
||||
@@ -17,10 +17,8 @@ package com.hippo.ehviewer.client.parser
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.yorozuya.Utilities
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
@@ -28,7 +26,7 @@ import java.net.URLDecoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
object GalleryListUrlParser {
|
||||
private val VALID_HOSTS = arrayOf(EhUrl.DOMAIN_EX, EhUrl.DOMAIN_E, EhUrl.DOMAIN_LOFI)
|
||||
private val VALID_HOSTS = arrayOf(EhUrl.DOMAIN_EX, EhUrl.DOMAIN_E)
|
||||
private const val PATH_NORMAL = "/"
|
||||
private const val PATH_UPLOADER = "/uploader/"
|
||||
private const val PATH_TAG = "/tag/"
|
||||
@@ -39,7 +37,7 @@ object GalleryListUrlParser {
|
||||
} catch (e: MalformedURLException) {
|
||||
return null
|
||||
}
|
||||
if (!Utilities.contain(VALID_HOSTS, url.host)) {
|
||||
if (url.host !in VALID_HOSTS) {
|
||||
return null
|
||||
}
|
||||
val path = url.path ?: return null
|
||||
@@ -88,7 +86,7 @@ object GalleryListUrlParser {
|
||||
return null
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(uploader)) {
|
||||
if (uploader.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
val builder = ListUrlBuilder()
|
||||
@@ -117,7 +115,7 @@ object GalleryListUrlParser {
|
||||
return null
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(tag)) {
|
||||
if (tag.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
val builder = ListUrlBuilder()
|
||||
@@ -129,7 +127,7 @@ object GalleryListUrlParser {
|
||||
// TODO get page
|
||||
private fun parseToplist(path: String): ListUrlBuilder? {
|
||||
val uri = Uri.parse(path)
|
||||
if (uri == null || TextUtils.isEmpty(uri.getQueryParameter("tl"))) {
|
||||
if (uri == null || uri.getQueryParameter("tl").isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
val tl = uri.getQueryParameter("tl")!!.toInt()
|
||||
|
||||
+13
-9
@@ -17,22 +17,26 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import org.json.JSONArray
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryMultiPageViewerPTokenParser {
|
||||
private const val IMAGE_LIST_STRING = "var imagelist = "
|
||||
|
||||
fun parse(body: String): List<String> {
|
||||
val imagelist = body.substring(
|
||||
body.indexOf("var imagelist = ") + 16,
|
||||
body.indexOf(";", body.indexOf("var imagelist = ")),
|
||||
)
|
||||
val index = body.indexOf(IMAGE_LIST_STRING)
|
||||
val imagelist = body.substring(index + IMAGE_LIST_STRING.length, body.indexOf(";", index))
|
||||
return runCatching {
|
||||
val ja = JSONArray(imagelist)
|
||||
(0 until ja.length()).map { ja.getJSONObject(it).getString("k") }
|
||||
imagelist.parseAs<List<Item>>().map(Item::token)
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
throw EhException(body)
|
||||
throw ParseException("Parse pToken from MPV error", body, it)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Item(@SerialName("k") val token: String)
|
||||
}
|
||||
@@ -18,15 +18,13 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.JsoupUtils
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
object GalleryNotAvailableParser {
|
||||
fun parse(body: String): String? {
|
||||
return runCatching {
|
||||
val document = Jsoup.parse(body)
|
||||
val d = JsoupUtils.getElementByClass(document, "d")
|
||||
d!!.child(0).html().replace("<br>", "\n")
|
||||
val e = Jsoup.parse(body).getElementsByClass("d").first()
|
||||
e!!.child(0).html().replace("<br>", "\n")
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
it.printStackTrace()
|
||||
|
||||
@@ -16,56 +16,44 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.yorozuya.StringUtils
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.unescapeXml
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryPageApiParser {
|
||||
private val PATTERN_IMAGE_URL = Pattern.compile("<img[^>]*src=\"([^\"]+)\" style")
|
||||
private val PATTERN_SKIP_HATH_KEY = Pattern.compile("onclick=\"return nl\\('([^)]+)'\\)")
|
||||
private val PATTERN_ORIGIN_IMAGE_URL =
|
||||
Pattern.compile("<a href=\"([^\"]+)fullimg.php([^\"]+)\">")
|
||||
private val PATTERN_IMAGE_URL = Regex("<img[^>]*src=\"([^\"]+)\" style")
|
||||
private val PATTERN_SKIP_HATH_KEY = Regex("onclick=\"return nl\\('([^)]+)'\\)")
|
||||
private val PATTERN_ORIGIN_IMAGE_URL = Regex("<a href=\"([^\"]+)fullimg.php([^\"]+)\">")
|
||||
|
||||
fun parse(body: String): Result {
|
||||
return try {
|
||||
var m: Matcher
|
||||
val jo = JSONObject(body)
|
||||
if (jo.has("error")) {
|
||||
throw ParseException(jo.getString("error"), body)
|
||||
}
|
||||
val i3 = jo.getString("i3")
|
||||
m = PATTERN_IMAGE_URL.matcher(i3)
|
||||
val imageUrl = if (m.find()) {
|
||||
StringUtils.unescapeXml(StringUtils.trim(m.group(1)))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val i6 = jo.getString("i6")
|
||||
m = PATTERN_SKIP_HATH_KEY.matcher(i6)
|
||||
val skipHathKey = if (m.find()) {
|
||||
StringUtils.unescapeXml(StringUtils.trim(m.group(1)))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val i7 = jo.getString("i7")
|
||||
m = PATTERN_ORIGIN_IMAGE_URL.matcher(i7)
|
||||
val originImageUrl = if (m.find()) {
|
||||
StringUtils.unescapeXml(m.group(1)) + "fullimg.php" +
|
||||
StringUtils.unescapeXml(m.group(2))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (!imageUrl.isNullOrEmpty()) {
|
||||
Result(imageUrl, skipHathKey, originImageUrl)
|
||||
} else {
|
||||
throw ParseException("Parse image url and skip hath key error", body)
|
||||
}
|
||||
} catch (e: JSONException) {
|
||||
throw ParseException("Can't parse json", body, e)
|
||||
runCatching { body.parseAs<Error>() }.onSuccess { throw ParseException(it.error, body) }
|
||||
val res = body.parseAs<JsonResult>()
|
||||
val imageUrl = PATTERN_IMAGE_URL.find(res.imageUrl)?.run {
|
||||
groupValues[1].trim().unescapeXml()
|
||||
}
|
||||
val skipHathKey = PATTERN_SKIP_HATH_KEY.find(res.skipHathKey)?.run {
|
||||
groupValues[1].trim().unescapeXml()
|
||||
}
|
||||
val originImageUrl = PATTERN_ORIGIN_IMAGE_URL.find(res.originImageUrl)?.run {
|
||||
groupValues[1].unescapeXml() + "fullimg.php" + groupValues[2].unescapeXml()
|
||||
}
|
||||
if (!imageUrl.isNullOrEmpty()) {
|
||||
return Result(imageUrl, skipHathKey, originImageUrl)
|
||||
} else {
|
||||
throw ParseException("Parse image url and skip hath key error", body)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class JsonResult(
|
||||
@SerialName("i3")
|
||||
val imageUrl: String,
|
||||
@SerialName("i6")
|
||||
val skipHathKey: String,
|
||||
@SerialName("i7")
|
||||
val originImageUrl: String,
|
||||
)
|
||||
|
||||
class Result(val imageUrl: String, val skipHathKey: String?, val originImageUrl: String?)
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.yorozuya.StringUtils
|
||||
import com.hippo.ehviewer.util.unescapeXml
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object GalleryPageParser {
|
||||
@@ -31,20 +31,19 @@ object GalleryPageParser {
|
||||
fun parse(body: String): Result {
|
||||
var m = PATTERN_IMAGE_URL.matcher(body)
|
||||
val imageUrl = if (m.find()) {
|
||||
StringUtils.unescapeXml(StringUtils.trim(m.group(1)))
|
||||
m.group(1)!!.trim().unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
m = PATTERN_SKIP_HATH_KEY.matcher(body)
|
||||
val skipHathKey = if (m.find()) {
|
||||
StringUtils.unescapeXml(StringUtils.trim(m.group(1)))
|
||||
m.group(1)!!.trim().unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
m = PATTERN_ORIGIN_IMAGE_URL.matcher(body)
|
||||
val originImageUrl = if (m.find()) {
|
||||
StringUtils.unescapeXml(m.group(1)) + "fullimg.php" +
|
||||
StringUtils.unescapeXml(m.group(2))
|
||||
m.group(1)!!.unescapeXml() + "fullimg.php" + m.group(2)!!.unescapeXml()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import com.hippo.ehviewer.util.toLongOrDefault
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
@@ -24,7 +25,7 @@ import java.util.regex.Pattern
|
||||
*/
|
||||
object GalleryPageUrlParser {
|
||||
private val URL_STRICT_PATTERN = Pattern.compile(
|
||||
"https?://(?:" + EhUrl.DOMAIN_EX + "|" + EhUrl.DOMAIN_E + "|" + EhUrl.DOMAIN_LOFI + ")/s/([0-9a-f]{10})/(\\d+)-(\\d+)",
|
||||
"https?://(?:${EhUrl.DOMAIN_EX}|${EhUrl.DOMAIN_E}(?:/lofi)?)/s/([0-9a-f]{10})/(\\d+)-(\\d+)",
|
||||
)
|
||||
private val URL_PATTERN = Pattern.compile("([0-9a-f]{10})/(\\d+)-(\\d+)")
|
||||
|
||||
@@ -33,9 +34,9 @@ object GalleryPageUrlParser {
|
||||
val pattern = if (strict) URL_STRICT_PATTERN else URL_PATTERN
|
||||
val m = pattern.matcher(url)
|
||||
return if (m.find()) {
|
||||
val gid = NumberUtils.parseLongSafely(m.group(2), -1L)
|
||||
val gid = m.group(2)!!.toLongOrDefault(-1L)
|
||||
val pToken = m.group(1)!!
|
||||
val page = NumberUtils.parseIntSafely(m.group(3), 0) - 1
|
||||
val page = m.group(3)!!.toIntOrDefault(0) - 1
|
||||
if (gid < 0 || page < 0) {
|
||||
null
|
||||
} else {
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import org.json.JSONObject
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GalleryTokenApiParser {
|
||||
/**
|
||||
@@ -31,12 +33,17 @@ object GalleryTokenApiParser {
|
||||
* }
|
||||
*/
|
||||
fun parse(body: String): String {
|
||||
val jo = JSONObject(body).getJSONArray("tokenlist").getJSONObject(0)
|
||||
return runCatching {
|
||||
jo.getString("token")
|
||||
body.parseAs<Result>().tokenList[0].token
|
||||
}.getOrElse {
|
||||
ExceptionUtils.throwIfFatal(it)
|
||||
throw EhException(jo.getString("error"))
|
||||
throw EhException(body.parseAs<Error>().error)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Result(@SerialName("tokenlist") val tokenList: List<Item>)
|
||||
|
||||
@Serializable
|
||||
data class Item(val gid: Long, val token: String)
|
||||
}
|
||||
@@ -1,31 +1,20 @@
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.hippo.ehviewer.client.exception.InsufficientFundsException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import org.jsoup.Jsoup
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
object HomeParser {
|
||||
private val PATTERN_FUNDS =
|
||||
Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
||||
private const val RESET_SUCCEED = "Image limit was successfully reset."
|
||||
private val PATTERN_FUNDS = Regex("Available: ([\\d,]+) Credits.*Available: ([\\d,]+) kGP", RegexOption.DOT_MATCHES_ALL)
|
||||
private const val INSUFFICIENT_FUNDS = "Insufficient funds."
|
||||
|
||||
fun parse(body: String): Limits {
|
||||
Jsoup.parse(body).selectFirst("div.homebox")?.let {
|
||||
val es = it.select("p > strong")
|
||||
if (es.size == 3) {
|
||||
val current = ParserUtils.parseInt(es[0].text(), 0)
|
||||
val maximum = ParserUtils.parseInt(es[1].text(), 0)
|
||||
val resetCost = ParserUtils.parseInt(es[2].text(), 0)
|
||||
return Limits(current, maximum, resetCost)
|
||||
}
|
||||
}
|
||||
throw ParseException("Parse image limits error", body)
|
||||
}
|
||||
fun parse(body: ByteBuffer) = parseLimit(body)
|
||||
|
||||
fun parseResetLimits(body: String): Limits? {
|
||||
return if (body.contains(RESET_SUCCEED)) {
|
||||
null
|
||||
} else {
|
||||
parse(body)
|
||||
fun parseResetLimits(body: String) {
|
||||
if (body.contains(INSUFFICIENT_FUNDS)) {
|
||||
throw InsufficientFundsException()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +27,12 @@ object HomeParser {
|
||||
throw ParseException("Parse funds error", body)
|
||||
}
|
||||
|
||||
data class Limits(val current: Int = 0, val maximum: Int, val resetCost: Int = 0)
|
||||
data class Funds(val fundsGP: Int, val fundsC: Int)
|
||||
@Parcelize
|
||||
data class Funds(val fundsGP: Int, val fundsC: Int) : Parcelable
|
||||
class Result(val limits: Limits, val funds: Funds)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Limits(val current: Int, val maximum: Int, val resetCost: Int) : Parcelable
|
||||
|
||||
private external fun parseLimit(body: ByteBuffer, limit: Int = body.limit()): Limits
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.yorozuya.NumberUtils
|
||||
import com.hippo.ehviewer.yorozuya.StringUtils
|
||||
import com.hippo.ehviewer.util.toIntOrDefault
|
||||
import com.hippo.ehviewer.util.toLongOrDefault
|
||||
import com.hippo.ehviewer.util.unescapeXml
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
@@ -32,14 +33,14 @@ object ParserUtils {
|
||||
}
|
||||
|
||||
fun trim(str: String?): String {
|
||||
return str?.let { StringUtils.unescapeXml(it).trim() } ?: ""
|
||||
return str?.unescapeXml()?.trim() ?: ""
|
||||
}
|
||||
|
||||
fun parseInt(str: String?, defValue: Int): Int {
|
||||
return NumberUtils.parseIntSafely(trim(str).replace(",", ""), defValue)
|
||||
return trim(str).replace(",", "").toIntOrDefault(defValue)
|
||||
}
|
||||
|
||||
fun parseLong(str: String?, defValue: Long): Long {
|
||||
return NumberUtils.parseLongSafely(trim(str).replace(",", ""), defValue)
|
||||
return trim(str).replace(",", "").toLongOrDefault(defValue)
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
object RateGalleryParser {
|
||||
fun parse(body: String): Result {
|
||||
return try {
|
||||
val jsonObject = JSONObject(body)
|
||||
val rating = jsonObject.getDouble("rating_avg").toFloat()
|
||||
val ratingCount = jsonObject.getInt("rating_cnt")
|
||||
Result(rating, ratingCount)
|
||||
} catch (e: JSONException) {
|
||||
throw ParseException("Can't parse rate gallery", body, e)
|
||||
}
|
||||
}
|
||||
|
||||
class Result(val rating: Float, val ratingCount: Int)
|
||||
}
|
||||
+10
-10
@@ -13,15 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.legacy
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Html.ImageGetter
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
class URLImageGetter(
|
||||
private val mTextView: ObservedTextView,
|
||||
) : ImageGetter {
|
||||
override fun getDrawable(source: String): Drawable {
|
||||
return UnikeryDrawable(mTextView, source)
|
||||
}
|
||||
}
|
||||
@Serializable
|
||||
data class RateGalleryResult(
|
||||
@SerialName("rating_avg")
|
||||
val rating: Float,
|
||||
@SerialName("rating_cnt")
|
||||
val ratingCount: Int,
|
||||
)
|
||||
@@ -1,56 +1,23 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import org.jsoup.Jsoup
|
||||
import java.util.regex.Pattern
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
object TorrentParser {
|
||||
private val PATTERN_TORRENT =
|
||||
Pattern.compile("</span> ([0-9-]+) [0-9:]+</td>[\\s\\S]+</span> ([0-9.]+ [KMGT]B)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span> ([0-9]+)</td>[\\s\\S]+</span>([^<]+)</td>[\\s\\S]+onclick=\"document.location='([^\"]+)'[^<]+>([^<]+)</a>")
|
||||
|
||||
fun parse(body: String): List<Result> {
|
||||
val torrentList = ArrayList<Result>()
|
||||
val d = Jsoup.parse(body)
|
||||
val es = d.select("form>div>table")
|
||||
for (e in es) {
|
||||
val m = PATTERN_TORRENT.matcher(e.html())
|
||||
if (m.find()) {
|
||||
val posted = m.group(1)!!
|
||||
val size = m.group(2)!!
|
||||
val seeds = m.group(3)!!.toInt()
|
||||
val peers = m.group(4)!!.toInt()
|
||||
val downloads = m.group(5)!!.toInt()
|
||||
val url = ParserUtils.trim(m.group(7))
|
||||
val name = ParserUtils.trim(m.group(8))
|
||||
torrentList.add(Result(posted, size, seeds, peers, downloads, url, name))
|
||||
}
|
||||
}
|
||||
return torrentList
|
||||
}
|
||||
|
||||
class Result(
|
||||
private val posted: String,
|
||||
private val size: String,
|
||||
private val seeds: Int,
|
||||
private val peers: Int,
|
||||
private val downloads: Int,
|
||||
val url: String,
|
||||
val name: String,
|
||||
) {
|
||||
fun format() = "[$posted] $name [$size] [↑$seeds ↓$peers ✓$downloads]"
|
||||
}
|
||||
fun parse(body: ByteBuffer) = parseTorrent(body)
|
||||
}
|
||||
|
||||
class Torrent(
|
||||
val posted: String,
|
||||
val size: String,
|
||||
val seeds: Int,
|
||||
val peers: Int,
|
||||
val downloads: Int,
|
||||
val url: String,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
fun Torrent.format() = "[$posted] $name [$size] [↑$seeds ↓$peers ✓$downloads]"
|
||||
|
||||
typealias TorrentResult = ArrayList<Torrent>
|
||||
|
||||
private external fun parseTorrent(body: ByteBuffer, size: Int = body.limit()): ArrayList<Torrent>
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
object VoteCommentParser {
|
||||
// {"comment_id":1253922,"comment_score":-19,"comment_vote":0}
|
||||
fun parse(body: String, expectVote: Int): Result {
|
||||
val jo = JSONObject(body)
|
||||
val id = jo.getLong("comment_id")
|
||||
val score = jo.getInt("comment_score")
|
||||
val vote = jo.getInt("comment_vote")
|
||||
return Result(id, score, vote, expectVote)
|
||||
}
|
||||
|
||||
class Result(val id: Long, val score: Int, val vote: Int, val expectVote: Int)
|
||||
}
|
||||
+12
-11
@@ -13,16 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
object GetText {
|
||||
fun getString(@StringRes id: Int): String {
|
||||
return EhApplication.application.getString(id)
|
||||
}
|
||||
|
||||
fun getString(@StringRes id: Int, vararg formatArgs: Any?): String {
|
||||
return EhApplication.application.getString(id, *formatArgs)
|
||||
}
|
||||
}
|
||||
@Serializable
|
||||
data class VoteCommentResult(
|
||||
@SerialName("comment_id")
|
||||
val id: Long,
|
||||
@SerialName("comment_score")
|
||||
val score: Int,
|
||||
@SerialName("comment_vote")
|
||||
val vote: Int,
|
||||
)
|
||||
@@ -17,11 +17,11 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.client.parser
|
||||
|
||||
import org.json.JSONObject
|
||||
import com.hippo.ehviewer.client.parseAs
|
||||
|
||||
object VoteTagParser {
|
||||
// {"error":"The tag \"neko\" is not allowed. Use character:neko or artist:neko"}
|
||||
fun parse(body: String): String {
|
||||
return JSONObject(body).optString("error")
|
||||
fun parse(body: String): String? {
|
||||
return runCatching { body.parseAs<Error>().error }.getOrNull()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
@file:Suppress("BlockingMethodInNonBlockingContext")
|
||||
|
||||
package com.hippo.ehviewer.coil
|
||||
|
||||
import android.net.Uri
|
||||
import android.webkit.MimeTypeMap
|
||||
import coil.ComponentRegistry
|
||||
import coil.ImageLoader
|
||||
import coil.decode.DataSource
|
||||
import coil.decode.ImageSource
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.fetch.SourceResult
|
||||
import coil.request.Options
|
||||
import com.hippo.ehviewer.cronet.copyToChannel
|
||||
import com.hippo.ehviewer.cronet.cronetRequest
|
||||
import com.hippo.ehviewer.cronet.execute
|
||||
import java.io.RandomAccessFile
|
||||
|
||||
/**
|
||||
* A HttpUriFetcher impl use Cronet for jvmheapless IO
|
||||
* cacheHeader is never respected | recorded since thumb is immutable
|
||||
*/
|
||||
class CronetHttpUriFetcher(private val data: String, private val options: Options, private val imageLoader: ImageLoader) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
val diskCacheKey = options.diskCacheKey ?: data
|
||||
val diskCache = requireNotNull(imageLoader.diskCache)
|
||||
val snapshot = diskCache.openSnapshot(diskCacheKey) ?: run {
|
||||
val success = cronetRequest(data) {
|
||||
disableCache()
|
||||
}.execute {
|
||||
diskCache.suspendEdit(diskCacheKey) {
|
||||
RandomAccessFile(data.toFile(), "rw").use {
|
||||
copyToChannel(it.channel)
|
||||
}
|
||||
}
|
||||
}
|
||||
check(success)
|
||||
// diskcache snapshot MUST exist here
|
||||
requireNotNull(diskCache.openSnapshot(diskCacheKey))
|
||||
}
|
||||
return SourceResult(
|
||||
source = ImageSource(snapshot.data, diskCache.fileSystem, diskCacheKey, snapshot),
|
||||
mimeType = getMimeType(data),
|
||||
dataSource = DataSource.DISK,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun ComponentRegistry.Builder.installCronetHttpUriFetcher() = add { data: Uri, options, loader ->
|
||||
if (data.scheme != "http" && data.scheme != "https") return@add null
|
||||
CronetHttpUriFetcher(data.toString(), options, loader)
|
||||
}
|
||||
|
||||
private fun getMimeType(url: String) = MimeTypeMap.getSingleton().getMimeTypeFromUrl(url)
|
||||
|
||||
private fun MimeTypeMap.getMimeTypeFromUrl(url: String?): String? {
|
||||
if (url.isNullOrBlank()) return null
|
||||
val extension = url
|
||||
.substringBeforeLast('#') // Strip the fragment.
|
||||
.substringBeforeLast('?') // Strip the query.
|
||||
.substringAfterLast('/') // Get the last path segment.
|
||||
.substringAfterLast('.', missingDelimiterValue = "") // Get the file extension.
|
||||
return getMimeTypeFromExtension(extension)
|
||||
}
|
||||
@@ -2,7 +2,20 @@ package com.hippo.ehviewer.coil
|
||||
|
||||
import coil.disk.DiskCache
|
||||
|
||||
suspend inline fun DiskCache.edit(key: String, crossinline block: suspend DiskCache.Editor.() -> Unit): Boolean {
|
||||
inline fun DiskCache.edit(key: String, crossinline block: DiskCache.Editor.() -> Unit): Boolean {
|
||||
val editor = openEditor(key) ?: return false
|
||||
editor.runCatching {
|
||||
block(this)
|
||||
}.onFailure {
|
||||
editor.abort()
|
||||
throw it
|
||||
}.onSuccess {
|
||||
editor.commit()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
suspend inline fun DiskCache.suspendEdit(key: String, crossinline block: suspend DiskCache.Editor.() -> Unit): Boolean {
|
||||
val editor = openEditor(key) ?: return false
|
||||
editor.runCatching {
|
||||
block(this)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.hippo.ehviewer.coil
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import coil.decode.DecodeResult
|
||||
import coil.decode.Decoder
|
||||
import coil.disk.DiskCache
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
@@ -36,8 +34,3 @@ fun ImageRequest.Builder.justDownload() = apply {
|
||||
memoryCachePolicy(CachePolicy.DISABLED)
|
||||
decoderFactory { _, _, _ -> stubFactory }
|
||||
}
|
||||
|
||||
inline fun Context.imageRequest(preview: GalleryPreview, builder: ImageRequest.Builder.() -> Unit = {}) = ImageRequest.Builder(this).ehPreview(preview).apply(builder).build()
|
||||
inline fun Context.imageRequest(info: GalleryInfo, builder: ImageRequest.Builder.() -> Unit = {}) = ImageRequest.Builder(this).ehUrl(info).apply(builder).build()
|
||||
inline fun Context.imageRequest(builder: ImageRequest.Builder.() -> Unit = {}) = ImageRequest.Builder(this).apply(builder).build()
|
||||
inline fun diskCache(builder: DiskCache.Builder.() -> Unit) = DiskCache.Builder().apply(builder).build()
|
||||
@@ -1,79 +1,81 @@
|
||||
package com.hippo.ehviewer.coil
|
||||
|
||||
import coil.intercept.Interceptor
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.ImageResult
|
||||
import com.hippo.ehviewer.Settings
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
object MergeInterceptor : Interceptor {
|
||||
private val pendingContinuationMap: HashMap<String, MutableList<Continuation<Unit>>> = hashMapOf()
|
||||
private val pendingContinuationMapLock = Mutex()
|
||||
private val pendingContinuationMap: HashMap<String, MutableList<CancellableContinuation<Unit>>> = hashMapOf()
|
||||
private val pendingContinuationMapLock = Any()
|
||||
private val notifyScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
private val EMPTY_LIST = mutableListOf<Continuation<Unit>>()
|
||||
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
|
||||
val originRequest = chain.request
|
||||
val originListener = originRequest.listener
|
||||
val key = originRequest.data as String
|
||||
val newRequest = ImageRequest.Builder(originRequest).listener(
|
||||
{ originListener?.onStart(it) },
|
||||
{ originListener?.onCancel(it) },
|
||||
{ request, result ->
|
||||
originListener?.onError(request, result)
|
||||
// Wake all pending continuations since this request is to be failed
|
||||
notifyScope.launch {
|
||||
pendingContinuationMapLock.withLock {
|
||||
pendingContinuationMap.remove(key)?.forEach { it.resume(Unit) }
|
||||
}
|
||||
}
|
||||
},
|
||||
{ request, result ->
|
||||
originListener?.onSuccess(request, result)
|
||||
// Wake all pending continuations shared with the same memory key since we have written it to memory cache
|
||||
notifyScope.launch {
|
||||
pendingContinuationMapLock.withLock {
|
||||
pendingContinuationMap.remove(key)?.forEach { it.resume(Unit) }
|
||||
}
|
||||
}
|
||||
},
|
||||
).build()
|
||||
private val EMPTY_LIST = mutableListOf<CancellableContinuation<Unit>>()
|
||||
|
||||
pendingContinuationMapLock.lock()
|
||||
val existPendingContinuations = pendingContinuationMap[key]
|
||||
if (existPendingContinuations == null) {
|
||||
pendingContinuationMap[key] = EMPTY_LIST
|
||||
pendingContinuationMapLock.unlock()
|
||||
} else {
|
||||
if (existPendingContinuations === EMPTY_LIST) pendingContinuationMap[key] = mutableListOf()
|
||||
pendingContinuationMap[key]!!.apply {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
add(continuation)
|
||||
pendingContinuationMapLock.unlock()
|
||||
continuation.invokeOnCancellation { remove(continuation) }
|
||||
private fun triggerSuccessor(key: String) {
|
||||
notifyScope.launch {
|
||||
synchronized(pendingContinuationMapLock) {
|
||||
// Wake up a pending continuation to continue executing task
|
||||
val successor = pendingContinuationMap[key]?.removeFirstOrNull()?.apply { resume(Unit) { triggerSuccessor(key) } }
|
||||
// If no successor, delete this entry from hashmap
|
||||
successor ?: pendingContinuationMap.remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun triggerFailure(key: String, e: Throwable) {
|
||||
notifyScope.launch {
|
||||
synchronized(pendingContinuationMapLock) {
|
||||
pendingContinuationMap.remove(key)?.forEach { it.resumeWithException(e) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
|
||||
val req = chain.request
|
||||
val key = req.memoryCacheKey?.key?.takeIf { it.startsWith("m/") || Settings.preloadThumbAggressively } ?: return withContext(req.interceptorDispatcher) { chain.proceed(req) }
|
||||
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
synchronized(pendingContinuationMapLock) {
|
||||
val existPendingContinuations = pendingContinuationMap[key]
|
||||
if (existPendingContinuations == null) {
|
||||
pendingContinuationMap[key] = EMPTY_LIST
|
||||
continuation.resume(Unit) {
|
||||
triggerSuccessor(key)
|
||||
}
|
||||
} else {
|
||||
if (existPendingContinuations === EMPTY_LIST) pendingContinuationMap[key] = mutableListOf()
|
||||
pendingContinuationMap[key]!!.apply {
|
||||
add(continuation)
|
||||
continuation.invokeOnCancellation { remove(continuation) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return withContext(originRequest.interceptorDispatcher) { chain.proceed(newRequest) }
|
||||
} catch (e: CancellationException) {
|
||||
notifyScope.launch {
|
||||
pendingContinuationMapLock.withLock {
|
||||
// Wake up a pending continuation to continue executing task
|
||||
val successor = pendingContinuationMap[key]?.removeFirstOrNull()?.apply { resume(Unit) }
|
||||
// If no successor, delete this entry from hashmap
|
||||
successor ?: pendingContinuationMap.remove(key)
|
||||
return withContext(req.interceptorDispatcher) { chain.proceed(req) }.apply {
|
||||
// Wake all pending continuations shared with the same memory key since we have written it to memory cache
|
||||
notifyScope.launch {
|
||||
synchronized(pendingContinuationMapLock) {
|
||||
pendingContinuationMap.remove(key)?.forEach { it.resume(Unit) }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
triggerSuccessor(key)
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
// Wake all pending continuations since this request is to be failed
|
||||
triggerFailure(key, e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.hippo.ehviewer.cronet
|
||||
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.client.CHROME_ACCEPT
|
||||
import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE
|
||||
import com.hippo.ehviewer.client.CHROME_USER_AGENT
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import io.ktor.utils.io.pool.DirectByteBufferPool
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import org.chromium.net.CronetEngine
|
||||
import org.chromium.net.CronetException
|
||||
import org.chromium.net.UrlRequest
|
||||
import org.chromium.net.UrlResponseInfo
|
||||
import splitties.init.appCtx
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
private const val TAG = "CronetRequest"
|
||||
val pool = DirectByteBufferPool(32)
|
||||
|
||||
val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply {
|
||||
enableBrotli(true)
|
||||
val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() }
|
||||
setStoragePath(cache.absolutePath)
|
||||
enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024)
|
||||
addQuicHint("e-hentai.org", 443, 443)
|
||||
addQuicHint("forums.e-hentai.org", 443, 443)
|
||||
addQuicHint("exhentai.org", 443, 443)
|
||||
}.build()
|
||||
|
||||
val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService
|
||||
|
||||
// TODO: Rewrite this to use android.net.http.HttpEngine and make it Android 14 only when released
|
||||
class CronetRequest {
|
||||
lateinit var consumer: (ByteBuffer) -> Unit
|
||||
lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit
|
||||
lateinit var request: UrlRequest
|
||||
lateinit var onError: (Throwable) -> Unit
|
||||
lateinit var readerCont: Continuation<Unit>
|
||||
val callback = object : UrlRequest.Callback() {
|
||||
override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) {
|
||||
logcat(tag = TAG) { "Redirected to $url" }
|
||||
req.followRedirect()
|
||||
}
|
||||
|
||||
override fun onResponseStarted(req: UrlRequest, info: UrlResponseInfo) {
|
||||
onResponse(info)
|
||||
}
|
||||
|
||||
override fun onReadCompleted(req: UrlRequest, info: UrlResponseInfo, data: ByteBuffer) {
|
||||
data.flip()
|
||||
consumer(data)
|
||||
}
|
||||
|
||||
override fun onSucceeded(req: UrlRequest, info: UrlResponseInfo) {
|
||||
readerCont.resume(Unit)
|
||||
}
|
||||
|
||||
override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: CronetException) {
|
||||
onError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply {
|
||||
request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply {
|
||||
addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl()))
|
||||
addHeader("User-Agent", CHROME_USER_AGENT)
|
||||
addHeader("Accept", CHROME_ACCEPT)
|
||||
addHeader("Accept-Language", CHROME_ACCEPT_LANGUAGE)
|
||||
referer?.let { addHeader("Referer", it) }
|
||||
}.apply(conf).build()
|
||||
}
|
||||
|
||||
suspend inline fun CronetRequest.awaitBodyFully(crossinline callback: (ByteBuffer) -> Unit) {
|
||||
val buffer = pool.borrow()
|
||||
return try {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
consumer = {
|
||||
callback(it)
|
||||
buffer.clear()
|
||||
request.read(buffer)
|
||||
}
|
||||
onError = { readerCont.resumeWithException(it) }
|
||||
readerCont = cont
|
||||
request.read(buffer)
|
||||
}
|
||||
} finally {
|
||||
pool.recycle(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun CronetRequest.copyToChannel(chan: FileChannel, crossinline listener: ((Int) -> Unit) = {}) = awaitBodyFully {
|
||||
val bytes = chan.write(it)
|
||||
listener(bytes)
|
||||
}
|
||||
|
||||
suspend inline fun <R> CronetRequest.execute(crossinline callback: suspend CronetRequest.(UrlResponseInfo) -> R): R {
|
||||
contract {
|
||||
callsInPlace(callback, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return coroutineScope {
|
||||
suspendCancellableCoroutine { cont ->
|
||||
onResponse = { launch { cont.resume(callback(it)) } }
|
||||
cont.invokeOnCancellation { request.cancel() }
|
||||
onError = { cont.resumeWithException(it) }
|
||||
request.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,15 @@ package com.hippo.ehviewer.dailycheck
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.Html
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.style.URLSpan
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
import androidx.core.text.getSpans
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
@@ -14,12 +19,12 @@ import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import splitties.init.appCtx
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
@@ -41,15 +46,15 @@ class DailyCheckWork(context: Context, workerParams: WorkerParameters) :
|
||||
}
|
||||
|
||||
val schedHour
|
||||
get() = Settings.getInt(Settings.KEY_REQUEST_NEWS_TIMER_HOUR, -1).takeUnless { it == -1 }
|
||||
get() = Settings.requestNewsTimerHour.takeUnless { it == -1 } ?: 0
|
||||
|
||||
val schedMinute
|
||||
get() = Settings.getInt(Settings.KEY_REQUEST_NEWS_TIMER_MINUTE, -1).takeUnless { it == -1 }
|
||||
get() = Settings.requestNewsTimerMinute.takeUnless { it == -1 } ?: 0
|
||||
|
||||
private val whenToWork
|
||||
get() = LocalDateTime.now()
|
||||
.run { withHour(schedHour ?: hour) }
|
||||
.run { withMinute(schedMinute ?: minute) }
|
||||
.run { withHour(schedHour) }
|
||||
.run { withMinute(schedMinute) }
|
||||
|
||||
private val initialDelay
|
||||
get() = Duration.between(LocalDateTime.now(), whenToWork)
|
||||
@@ -98,20 +103,26 @@ fun showEventNotification(html: String) {
|
||||
if (Settings.hideHvEvents && html.contains("You have encountered a monster!")) {
|
||||
return
|
||||
}
|
||||
val context = EhApplication.application
|
||||
val notificationManager = NotificationManagerCompat.from(context)
|
||||
val notificationManager = NotificationManagerCompat.from(appCtx)
|
||||
val chan = NotificationChannelCompat
|
||||
.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||
.setName(CHANNEL_ID)
|
||||
.build()
|
||||
notificationManager.createNotificationChannel(chan)
|
||||
val msg = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
val text = html.parseAsHtml()
|
||||
val msg = NotificationCompat.Builder(appCtx, CHANNEL_ID)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_launcher_monochrome)
|
||||
.setContentText(Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY))
|
||||
.setContentText(text)
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
.build()
|
||||
val urls = text.getSpans<URLSpan>(0, text.length)
|
||||
if (urls.isNotEmpty()) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(urls.first().url))
|
||||
val pi = PendingIntentCompat.getActivity(appCtx, 0, intent, 0, false)
|
||||
msg.setContentIntent(pi)
|
||||
}
|
||||
runCatching {
|
||||
notificationManager.notify(1, msg)
|
||||
notificationManager.notify(1, msg.build())
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
interface BasicDao<T> {
|
||||
fun list(): List<T>
|
||||
fun insert(t: T): Long
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
|
||||
@Entity(tableName = "BOOKMARKS")
|
||||
class BookmarkInfo : BaseGalleryInfo() {
|
||||
@JvmField
|
||||
@ColumnInfo(name = "PAGE")
|
||||
var page = 0
|
||||
|
||||
@JvmField
|
||||
@ColumnInfo(name = "TIME")
|
||||
var time: Long = 0
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
interface BookmarksDao : BasicDao<BookmarkInfo> {
|
||||
@Insert
|
||||
override fun insert(t: BookmarkInfo): Long
|
||||
|
||||
@Delete
|
||||
fun delete(bookmark: BookmarkInfo)
|
||||
|
||||
@Query("SELECT * FROM BOOKMARKS ORDER BY TIME DESC")
|
||||
override fun list(): List<BookmarkInfo>
|
||||
}
|
||||
@@ -1,22 +1,10 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
|
||||
@Dao
|
||||
interface CookiesDao {
|
||||
@Query("SELECT * FROM OK_HTTP_3_COOKIE")
|
||||
fun list(): List<Cookie>
|
||||
|
||||
@Delete
|
||||
fun delete(cookie: Cookie)
|
||||
|
||||
@Insert
|
||||
fun insert(cookie: Cookie): Long
|
||||
|
||||
@Update
|
||||
fun update(cookie: Cookie)
|
||||
suspend fun list(): List<Cookie>
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.AutoMigration
|
||||
import androidx.room.Database
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.DeleteTable
|
||||
import androidx.room.RenameColumn
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
|
||||
@DeleteTable(tableName = "BOOKMARKS")
|
||||
class DropBookMarkDao : AutoMigrationSpec
|
||||
|
||||
@RenameColumn(tableName = "HISTORY", fromColumnName = "MODE", toColumnName = "FAVORITE_SLOT")
|
||||
class HistoryMigration : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("UPDATE HISTORY SET FAVORITE_SLOT = FAVORITE_SLOT - 2")
|
||||
}
|
||||
}
|
||||
|
||||
class AddPositionColumn : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"QUICK_SEARCH",
|
||||
"DOWNLOAD_LABELS",
|
||||
"DOWNLOADS",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
// TODO: Rewrite this with row_number() when min sdk is 30 (SQLite 3.28.0)
|
||||
db.execSQL("UPDATE $table SET POSITION = (SELECT COUNT(*) FROM $table T WHERE T.TIME < $table.TIME)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "QUICK_SEARCH", columnName = "TIME")
|
||||
@DeleteColumn(tableName = "DOWNLOAD_LABELS", columnName = "TIME")
|
||||
class DropTimeColumn : AutoMigrationSpec
|
||||
|
||||
class AddGalleryTable : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"HISTORY",
|
||||
"DOWNLOADS",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
needMigrationTables.forEachIndexed { index, table ->
|
||||
db.execSQL(
|
||||
"INSERT OR IGNORE INTO GALLERIES " +
|
||||
"SELECT GID, TOKEN, TITLE, TITLE_JPN, THUMB, CATEGORY, POSTED, UPLOADER, RATING, SIMPLE_LANGUAGE, " +
|
||||
"${if (index == 0) "FAVORITE_SLOT" else NOT_FAVORITED} FROM $table",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "FAVORITE_SLOT")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "SIMPLE_LANGUAGE")
|
||||
class GalleryTableMigration : AutoMigrationSpec
|
||||
|
||||
class ThumbKeyMigration : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"DOWNLOADS",
|
||||
"HISTORY",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
val prefixToRemove = arrayOf(
|
||||
"https://ehgt.org/",
|
||||
"https://s.exhentai.org/t/",
|
||||
"https://exhentai.org/t/",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
prefixToRemove.forEach { prefix ->
|
||||
db.execSQL("UPDATE $table SET thumb = SUBSTR(thumb ,LENGTH('$prefix') + 1) WHERE thumb LIKE '$prefix%'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Database(
|
||||
entities = [BaseGalleryInfo::class, DownloadLabel::class, DownloadEntity::class, DownloadDirname::class, Filter::class, HistoryInfo::class, LocalFavoriteInfo::class, QuickSearch::class],
|
||||
version = 16,
|
||||
exportSchema = true,
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 4,
|
||||
to = 5,
|
||||
spec = ThumbKeyMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 5,
|
||||
to = 6,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 6,
|
||||
to = 7,
|
||||
spec = DropBookMarkDao::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 7,
|
||||
to = 8,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 8,
|
||||
to = 9,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 9,
|
||||
to = 10,
|
||||
spec = HistoryMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 10,
|
||||
to = 11,
|
||||
spec = AddPositionColumn::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 11,
|
||||
to = 12,
|
||||
spec = DropTimeColumn::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 12,
|
||||
to = 13,
|
||||
spec = AddGalleryTable::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 13,
|
||||
to = 14,
|
||||
spec = GalleryTableMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 14,
|
||||
to = 15,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 15,
|
||||
to = 16,
|
||||
),
|
||||
],
|
||||
)
|
||||
@TypeConverters(FilterModeConverter::class)
|
||||
abstract class EhDatabase : RoomDatabase() {
|
||||
abstract fun galleryDao(): GalleryDao
|
||||
abstract fun downloadDirnameDao(): DownloadDirnameDao
|
||||
abstract fun downloadLabelDao(): DownloadLabelDao
|
||||
abstract fun downloadsDao(): DownloadsDao
|
||||
abstract fun filterDao(): FilterDao
|
||||
abstract fun historyDao(): HistoryDao
|
||||
abstract fun localFavoritesDao(): LocalFavoritesDao
|
||||
abstract fun quickSearchDao(): QuickSearchDao
|
||||
}
|
||||
|
||||
// 1 -> 2 some nullability changes
|
||||
@Database(
|
||||
entities = [Cookie::class],
|
||||
version = 2,
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 1,
|
||||
to = 2,
|
||||
),
|
||||
],
|
||||
)
|
||||
abstract class CookiesDatabase : RoomDatabase() {
|
||||
abstract fun cookiesDao(): CookiesDao
|
||||
}
|
||||
|
||||
@Database(
|
||||
entities = [Search::class],
|
||||
version = 2,
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 1,
|
||||
to = 2,
|
||||
),
|
||||
],
|
||||
)
|
||||
abstract class SearchDatabase : RoomDatabase() {
|
||||
abstract fun searchDao(): SearchDao
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import androidx.room.PrimaryKey
|
||||
data class DownloadDirname(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "GID")
|
||||
var gid: Long = 0,
|
||||
val gid: Long,
|
||||
|
||||
@ColumnInfo(name = "DIRNAME")
|
||||
var dirname: String? = null,
|
||||
val dirname: String,
|
||||
)
|
||||
@@ -2,26 +2,24 @@ package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import androidx.room.Upsert
|
||||
|
||||
@Dao
|
||||
interface DownloadDirnameDao : BasicDao<DownloadDirname> {
|
||||
interface DownloadDirnameDao {
|
||||
@Query("SELECT * FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
|
||||
fun load(gid: Long): DownloadDirname?
|
||||
suspend fun load(gid: Long): DownloadDirname?
|
||||
|
||||
@Update
|
||||
fun update(downloadDirname: DownloadDirname)
|
||||
@Upsert
|
||||
suspend fun upsert(downloadDirname: DownloadDirname)
|
||||
|
||||
@Insert
|
||||
override fun insert(t: DownloadDirname): Long
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
suspend fun insertOrIgnore(downloadDirnameList: List<DownloadDirname>)
|
||||
|
||||
@Query("DELETE FROM DOWNLOAD_DIRNAME WHERE GID = :gid")
|
||||
fun deleteByKey(gid: Long)
|
||||
|
||||
@Query("DELETE FROM DOWNLOAD_DIRNAME")
|
||||
fun deleteAll()
|
||||
suspend fun deleteByKey(gid: Long)
|
||||
|
||||
@Query("SELECT * FROM DOWNLOAD_DIRNAME")
|
||||
override fun list(): List<DownloadDirname>
|
||||
suspend fun list(): List<DownloadDirname>
|
||||
}
|
||||
@@ -1,63 +1,76 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.Relation
|
||||
import com.hippo.ehviewer.client.data.AbstractDownloadInfo
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import java.time.Instant
|
||||
|
||||
@Entity(
|
||||
tableName = "DOWNLOADS",
|
||||
foreignKeys = [
|
||||
ForeignKey(BaseGalleryInfo::class, ["GID"], ["GID"]),
|
||||
ForeignKey(
|
||||
DownloadLabel::class,
|
||||
["LABEL"],
|
||||
["LABEL"],
|
||||
onDelete = ForeignKey.SET_NULL,
|
||||
onUpdate = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
class DownloadEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "GID")
|
||||
var gid: Long,
|
||||
|
||||
@Entity(tableName = "DOWNLOADS")
|
||||
class DownloadInfo() : BaseGalleryInfo() {
|
||||
@JvmField
|
||||
@ColumnInfo(name = "STATE")
|
||||
var state = 0
|
||||
override var state: Int = 0,
|
||||
|
||||
@JvmField
|
||||
@ColumnInfo(name = "LEGACY")
|
||||
var legacy = 0
|
||||
override var legacy: Int = 0,
|
||||
|
||||
@JvmField
|
||||
@ColumnInfo(name = "TIME")
|
||||
var time: Long = 0
|
||||
override var time: Long = Instant.now().toEpochMilli(),
|
||||
|
||||
@JvmField
|
||||
@ColumnInfo(name = "LABEL")
|
||||
var label: String? = null
|
||||
@ColumnInfo(name = "LABEL", index = true)
|
||||
override var label: String? = null,
|
||||
|
||||
@JvmField
|
||||
@ColumnInfo(name = "POSITION")
|
||||
override var position: Int = 0,
|
||||
) : AbstractDownloadInfo {
|
||||
@Ignore
|
||||
var speed: Long = 0
|
||||
override var speed: Long = 0
|
||||
|
||||
@JvmField
|
||||
@Ignore
|
||||
var remaining: Long = 0
|
||||
override var remaining: Long = 0
|
||||
|
||||
@JvmField
|
||||
@Ignore
|
||||
var finished = 0
|
||||
override var finished = 0
|
||||
|
||||
@JvmField
|
||||
@Ignore
|
||||
var downloaded = 0
|
||||
override var downloaded = 0
|
||||
|
||||
@JvmField
|
||||
@Ignore
|
||||
var total = 0
|
||||
override var total = 0
|
||||
}
|
||||
|
||||
constructor(galleryInfo: GalleryInfo) : this() {
|
||||
gid = galleryInfo.gid
|
||||
token = galleryInfo.token
|
||||
title = galleryInfo.title
|
||||
titleJpn = galleryInfo.titleJpn
|
||||
thumbKey = galleryInfo.thumbKey
|
||||
this.category = galleryInfo.category
|
||||
posted = galleryInfo.posted
|
||||
uploader = galleryInfo.uploader
|
||||
rating = galleryInfo.rating
|
||||
simpleTags = galleryInfo.simpleTags
|
||||
simpleLanguage = galleryInfo.simpleLanguage
|
||||
}
|
||||
data class DownloadInfo(
|
||||
@Relation(parentColumn = "GID", entityColumn = "GID")
|
||||
val galleryInfo: BaseGalleryInfo,
|
||||
|
||||
@ColumnInfo(name = "DIRNAME")
|
||||
val dirname: String?,
|
||||
|
||||
@Embedded
|
||||
val downloadInfo: DownloadEntity = DownloadEntity(galleryInfo.gid),
|
||||
) : GalleryInfo by galleryInfo, AbstractDownloadInfo by downloadInfo {
|
||||
companion object {
|
||||
const val STATE_INVALID = -1
|
||||
const val STATE_NONE = 0
|
||||
|
||||
@@ -2,17 +2,18 @@ package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "DOWNLOAD_LABELS")
|
||||
@Entity(tableName = "DOWNLOAD_LABELS", indices = [Index("LABEL", unique = true)])
|
||||
data class DownloadLabel(
|
||||
@ColumnInfo(name = "LABEL")
|
||||
var label: String,
|
||||
|
||||
@ColumnInfo(name = "POSITION")
|
||||
var position: Int,
|
||||
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "_id")
|
||||
var id: Long? = null,
|
||||
|
||||
@ColumnInfo(name = "LABEL")
|
||||
var label: String? = null,
|
||||
|
||||
@ColumnInfo(name = "TIME")
|
||||
var time: Long = 0,
|
||||
)
|
||||
@@ -7,22 +7,28 @@ import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
|
||||
@Dao
|
||||
interface DownloadLabelDao : BasicDao<DownloadLabel> {
|
||||
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC")
|
||||
override fun list(): List<DownloadLabel>
|
||||
interface DownloadLabelDao {
|
||||
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY POSITION ASC")
|
||||
suspend fun list(): List<DownloadLabel>
|
||||
|
||||
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY TIME ASC LIMIT :limit OFFSET :offset")
|
||||
fun list(offset: Int, limit: Int): List<DownloadLabel>
|
||||
@Query("SELECT * FROM DOWNLOAD_LABELS ORDER BY POSITION ASC LIMIT :limit OFFSET :offset")
|
||||
suspend fun list(offset: Int, limit: Int): List<DownloadLabel>
|
||||
|
||||
@Query("UPDATE DOWNLOAD_LABELS SET POSITION = POSITION - 1 WHERE POSITION > :position")
|
||||
suspend fun fill(position: Int)
|
||||
|
||||
@Update
|
||||
fun update(downloadLabels: List<DownloadLabel>)
|
||||
suspend fun update(downloadLabels: List<DownloadLabel>)
|
||||
|
||||
@Update
|
||||
fun update(downloadLabel: DownloadLabel)
|
||||
suspend fun update(downloadLabel: DownloadLabel)
|
||||
|
||||
@Insert
|
||||
override fun insert(t: DownloadLabel): Long
|
||||
suspend fun insert(downloadLabel: DownloadLabel): Long
|
||||
|
||||
@Insert
|
||||
suspend fun insert(downloadLabels: List<DownloadLabel>)
|
||||
|
||||
@Delete
|
||||
fun delete(downloadLabel: DownloadLabel)
|
||||
suspend fun delete(downloadLabel: DownloadLabel)
|
||||
}
|
||||
Loaded 100 of 541 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user