Compare commits
2137
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
019b961461 | ||
|
|
97f988991e | ||
|
|
0dbc3b7ea7 | ||
|
|
cac65d9030 | ||
|
|
4a83829dfd | ||
|
|
c0348484fe | ||
|
|
213133ab45 | ||
|
|
8469eac18c | ||
|
|
5aaa1bb5b3 | ||
|
|
9f500b85e6 | ||
|
|
0d6fd8d7ad | ||
|
|
7e4b6dbf31 | ||
|
|
8bf3f0f094 | ||
|
|
198acef666 | ||
|
|
1d0c76b2b2 | ||
|
|
567875cf00 | ||
|
|
58069e8925 | ||
|
|
a90cf3c72b | ||
|
|
2fbf226ccd | ||
|
|
d674672ce2 | ||
|
|
dab7968ce3 | ||
|
|
510719f890 | ||
|
|
96b92239b0 | ||
|
|
363029cbdd | ||
|
|
7783a8de15 | ||
|
|
26a589fc1d | ||
|
|
4c467c304f | ||
|
|
9496b9396a | ||
|
|
669a9ee1e3 | ||
|
|
d08b7c2d94 | ||
|
|
534f6aa22c | ||
|
|
8ea731159f | ||
|
|
7704fa9318 | ||
|
|
d974cb56df | ||
|
|
d0e3de7121 | ||
|
|
40af1862a0 | ||
|
|
33e24aa46c | ||
|
|
5a97561389 | ||
|
|
26701d84f7 | ||
|
|
d13b09aaf3 | ||
|
|
6a65b61785 | ||
|
|
2b8bfab44b | ||
|
|
b9e1d79be6 | ||
|
|
0f9e1819c1 | ||
|
|
eda97347fc | ||
|
|
2bd24b6a8b | ||
|
|
8306342ab6 | ||
|
|
6b6ba91721 | ||
|
|
13f255529b | ||
|
|
16e912c9ae | ||
|
|
d58e08e24c | ||
|
|
e111ec74be | ||
|
|
68155e6a6b | ||
|
|
bfbceefef2 | ||
|
|
21b39135fe | ||
|
|
01742ac6b9 | ||
|
|
6adc7f7221 | ||
|
|
e94424a83e | ||
|
|
56d30aa413 | ||
|
|
2c01acb4a6 | ||
|
|
c0f6d0ab04 | ||
|
|
6bfa8d1ea4 | ||
|
|
6f22269d93 | ||
|
|
b68dc98ae0 | ||
|
|
f9ad1f724b | ||
|
|
afd22635bd | ||
|
|
a9f9706be3 | ||
|
|
41f35498fe | ||
|
|
58628b6556 | ||
|
|
875858618e | ||
|
|
70b7bf390a | ||
|
|
de3d6de4cf | ||
|
|
e3b4caa37d | ||
|
|
f8e19f7772 | ||
|
|
ea5dd8b1bf | ||
|
|
bd7db1a016 | ||
|
|
02ac048d74 | ||
|
|
9a3c9b9cbd | ||
|
|
80c77c740e | ||
|
|
0f62662963 | ||
|
|
576d6515db | ||
|
|
30d29bfa80 | ||
|
|
c7c0446f23 | ||
|
|
134777584d | ||
|
|
0b64eadb0c | ||
|
|
ea93960bf4 | ||
|
|
71032e3010 | ||
|
|
a6d215a0bf | ||
|
|
cf9441adb5 | ||
|
|
9746f20b60 | ||
|
|
501148179b | ||
|
|
d6a3a351f5 | ||
|
|
a574bca48e | ||
|
|
785faf1bf4 | ||
|
|
fbcc32b3ee | ||
|
|
46b19a9de5 | ||
|
|
d2f47d51f9 | ||
|
|
db380507f5 | ||
|
|
84cf67d21a | ||
|
|
8f53591800 | ||
|
|
65ffd78844 | ||
|
|
c00b5e6635 | ||
|
|
8160991086 | ||
|
|
f0c933d97a | ||
|
|
3fb8d3e73b | ||
|
|
d8efff9921 | ||
|
|
32170688b6 | ||
|
|
36b16d7be8 | ||
|
|
fab5532407 | ||
|
|
8ad4e8640f | ||
|
|
d08491bf7e | ||
|
|
12f6f08d81 | ||
|
|
ca74bfe2e4 | ||
|
|
41767f0fd7 | ||
|
|
c62ca4dd23 | ||
|
|
9c5eddb903 | ||
|
|
4a0df07805 | ||
|
|
37a3edd44b | ||
|
|
f117abd483 | ||
|
|
4593714298 | ||
|
|
a148fd6ec3 | ||
|
|
23a44e2f3b | ||
|
|
01e03ec0f0 | ||
|
|
5b5a2a9a33 | ||
|
|
7719312355 | ||
|
|
e2345cc756 | ||
|
|
8d0b8534b2 | ||
|
|
4594ac592c | ||
|
|
eee0f2dded | ||
|
|
b4080302c3 | ||
|
|
337e210b5b | ||
|
|
48f1b0a958 | ||
|
|
9abeba6b15 | ||
|
|
63f9c8de79 | ||
|
|
f792f2c968 | ||
|
|
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 | ||
|
|
25d4d5e393 | ||
|
|
1743caa869 | ||
|
|
958fdb19b4 | ||
|
|
4b63648a79 | ||
|
|
498b84df37 | ||
|
|
7a4e65259d | ||
|
|
8cb8dcbe0f | ||
|
|
da37b3cdff | ||
|
|
0a1768e481 | ||
|
|
818d16a32d | ||
|
|
e9a5d1cc7c | ||
|
|
dac56f3e32 | ||
|
|
d6cbcdc6cd | ||
|
|
c5c1170383 | ||
|
|
6ed3fb29d9 | ||
|
|
74c6bfa479 | ||
|
|
d44981a70c | ||
|
|
06e411d332 | ||
|
|
f419189749 | ||
|
|
aa1e2102fc | ||
|
|
306ce9ed21 | ||
|
|
544fcb2e88 | ||
|
|
cb01df4071 | ||
|
|
38310b7c5a | ||
|
|
fcd56ff4e9 | ||
|
|
41300a9fd4 | ||
|
|
0bd5f9dc31 | ||
|
|
004ecd0dd3 | ||
|
|
fc24a18ac5 | ||
|
|
a9a8c68b94 | ||
|
|
193d2f8d9e | ||
|
|
538da9b98f | ||
|
|
071b597552 | ||
|
|
8926d74378 | ||
|
|
39a4b03bc3 | ||
|
|
9202bbc7dc | ||
|
|
f5601ebca1 | ||
|
|
2f978b554e | ||
|
|
f48a90d941 | ||
|
|
4f0d9fa83c | ||
|
|
57a747ed9c | ||
|
|
746a045328 | ||
|
|
2a4c7823c2 | ||
|
|
b995510df1 | ||
|
|
a2852d6df9 | ||
|
|
47863580a0 | ||
|
|
9af6532331 | ||
|
|
36f411a031 | ||
|
|
00b6185188 | ||
|
|
a7db6c8613 | ||
|
|
86097ace72 | ||
|
|
b62dd1fcac | ||
|
|
9a3b91c610 | ||
|
|
0f25789d74 | ||
|
|
b50185639e | ||
|
|
95478a615e | ||
|
|
91164e4dd6 | ||
|
|
ea42fa5613 | ||
|
|
c620478e38 | ||
|
|
467ab0b5a2 | ||
|
|
1d14e3ceb4 | ||
|
|
c4ff48f60f | ||
|
|
196190314b | ||
|
|
4c7b4dbc51 | ||
|
|
b820159736 | ||
|
|
41991b8f22 | ||
|
|
6246d01247 | ||
|
|
3734d0c585 | ||
|
|
d70c5be15a | ||
|
|
814230f4aa | ||
|
|
2b64491e76 | ||
|
|
f770ac9ac9 | ||
|
|
a6a3219d1e | ||
|
|
c1eddb5876 | ||
|
|
37dcad7d6f | ||
|
|
d06cd5cc2d | ||
|
|
c39e8eb43c | ||
|
|
ae3caf84b5 | ||
|
|
185d47fcdf | ||
|
|
a63b2b3283 | ||
|
|
576ff1b007 | ||
|
|
7a20d7ca49 | ||
|
|
346b614999 | ||
|
|
6b0ac4bf0a | ||
|
|
4e859eefb8 | ||
|
|
578aff7dea | ||
|
|
f81d9de2c6 | ||
|
|
255de89a43 | ||
|
|
c561c223d6 | ||
|
|
ba7d501a6e | ||
|
|
5b75afbdc8 | ||
|
|
f7b9be3bc5 | ||
|
|
743bd951c8 | ||
|
|
3d2bbe1463 | ||
|
|
4b1adab0eb | ||
|
|
e8ad2f2185 | ||
|
|
8986289702 | ||
|
|
69ef88909e | ||
|
|
443cacca00 | ||
|
|
ae5286bebb | ||
|
|
c3facb9787 | ||
|
|
22d5832ba2 | ||
|
|
68d55f416e | ||
|
|
dfda4c0bc2 | ||
|
|
c7289d07a0 | ||
|
|
df1ff07745 | ||
|
|
8ef017fb94 | ||
|
|
4e896927a9 | ||
|
|
3aa9fddfd5 | ||
|
|
d3bb169cdc | ||
|
|
d8e168ed61 | ||
|
|
b924cc5341 | ||
|
|
bb6ed820a7 | ||
|
|
1bb0c1d537 | ||
|
|
6ec69ca989 | ||
|
|
65f19f0782 | ||
|
|
cd48f0aed8 | ||
|
|
b165e96a07 | ||
|
|
d15dd900cc | ||
|
|
ce2391b6f3 | ||
|
|
c786ff0087 | ||
|
|
577286eb56 | ||
|
|
f91c69906f | ||
|
|
a3bdb70ef1 | ||
|
|
5f9bdabf7e | ||
|
|
86ecb9b931 | ||
|
|
9da687f168 | ||
|
|
8efea01d78 | ||
|
|
f824aed3be | ||
|
|
8b01906f1e | ||
|
|
61aab22c02 | ||
|
|
16e1e20812 | ||
|
|
25931f9068 | ||
|
|
65d5377683 | ||
|
|
383c37323a | ||
|
|
aa2073194d | ||
|
|
a57e91cd0a | ||
|
|
56de696330 | ||
|
|
a6002712fd | ||
|
|
f752636c25 | ||
|
|
6656399112 | ||
|
|
780bab2125 | ||
|
|
681be33fed | ||
|
|
d30837665a | ||
|
|
a6cefed13f | ||
|
|
0299ca8b1f | ||
|
|
dd95878ba7 | ||
|
|
f0ac27b159 | ||
|
|
f39ca55379 | ||
|
|
738c76ccd7 | ||
|
|
012ae19ca2 | ||
|
|
bfb3329e83 | ||
|
|
ff480291fe | ||
|
|
775441d223 | ||
|
|
7c5f1da1fc | ||
|
|
71bd11a941 | ||
|
|
ca62b9f4bf | ||
|
|
9272ff9fbf | ||
|
|
366b106234 | ||
|
|
d333524559 | ||
|
|
3f9c57ad2d | ||
|
|
64e55be02f | ||
|
|
99ed2b35d3 | ||
|
|
f94a2350ab | ||
|
|
7abeba8b87 | ||
|
|
7d16ad2119 | ||
|
|
97e5636813 | ||
|
|
3d67e886d2 | ||
|
|
919c8c7ee0 | ||
|
|
a47b2b234b | ||
|
|
2b95ebede8 | ||
|
|
87a4b6a91b | ||
|
|
bac07abf1f | ||
|
|
3f2184766c | ||
|
|
53d317cfa4 | ||
|
|
8aa4c34794 | ||
|
|
797182fbf8 | ||
|
|
dcbc79fd80 | ||
|
|
711848f027 | ||
|
|
8eeacf49cb | ||
|
|
bdf99d01a6 | ||
|
|
0ed68b55b3 | ||
|
|
83ad66d27c | ||
|
|
15d7bc5037 | ||
|
|
e83d0b6c55 | ||
|
|
004d8a4bff | ||
|
|
969c9a66af | ||
|
|
8eec363581 | ||
|
|
cec0578b77 | ||
|
|
2ce2f0ee9b | ||
|
|
f2641adfd2 | ||
|
|
af9125792c | ||
|
|
a9c37ba8fb | ||
|
|
94ebb4573a | ||
|
|
5f14919755 | ||
|
|
ed673b5bc1 | ||
|
|
78d8ed922c | ||
|
|
e0797cd519 | ||
|
|
eb5f39def7 | ||
|
|
d200dd9cbe | ||
|
|
ec4446bb9b | ||
|
|
c20bc215a8 | ||
|
|
13705f9872 | ||
|
|
f02cd58a6b | ||
|
|
f36480d5d6 | ||
|
|
821f9f8037 | ||
|
|
ba8e517e98 | ||
|
|
abf7676ae7 | ||
|
|
45cc6acd9f | ||
|
|
32038531e1 | ||
|
|
7d74bdc3bd | ||
|
|
b310e98c5a | ||
|
|
ce0f160f31 | ||
|
|
b8c9040030 | ||
|
|
da21628222 | ||
|
|
17a99abece | ||
|
|
9d76132a5d | ||
|
|
86a2d34f8e | ||
|
|
0a5b9e3178 | ||
|
|
7665e86812 | ||
|
|
29e5db87d9 | ||
|
|
ffaa7c92e3 | ||
|
|
b74a57967c | ||
|
|
dd368f4925 | ||
|
|
fa6c55636b | ||
|
|
9fa98671ac | ||
|
|
60f917a52d | ||
|
|
0f848a3f92 | ||
|
|
bf03d3f15a | ||
|
|
b864eba08f | ||
|
|
a8988ac17b | ||
|
|
d72a44b9f2 | ||
|
|
f9ed777c7f | ||
|
|
2cc6e8a1a6 | ||
|
|
9efb8cf450 | ||
|
|
2befb766ca | ||
|
|
ded8e77f5d | ||
|
|
7ec9d9e1cf | ||
|
|
be1d80de0f | ||
|
|
7dc28f36dc | ||
|
|
98de1d89da | ||
|
|
8a542aebe2 | ||
|
|
e7c077c31e | ||
|
|
3c0da086f5 | ||
|
|
06dc33b126 | ||
|
|
817ed1ce8f | ||
|
|
ec027d3d98 | ||
|
|
29214b58c0 | ||
|
|
bbc8f6b15e | ||
|
|
4f597584fb | ||
|
|
2d40cf801d | ||
|
|
3bb4772aea | ||
|
|
5b44df4476 | ||
|
|
3762b69fed | ||
|
|
18c205dde2 | ||
|
|
caa4e07f8c | ||
|
|
a3ae2d98a5 | ||
|
|
0fd4cdd498 | ||
|
|
0e271bbbc2 | ||
|
|
ff18198cd1 | ||
|
|
9354ca7c68 | ||
|
|
e8ed55bb41 | ||
|
|
6a9648e7f5 | ||
|
|
b41097790d | ||
|
|
e91a3d74b3 | ||
|
|
4d5ffe1618 | ||
|
|
3b04624b88 | ||
|
|
21b3b66bcd | ||
|
|
69826ccb1f | ||
|
|
66f9bfd871 | ||
|
|
fdc1868cd4 | ||
|
|
917cb107ec | ||
|
|
d3b3467989 | ||
|
|
3f09122e6d | ||
|
|
bfd0361fd5 | ||
|
|
1c52e246f0 | ||
|
|
09a403cb53 | ||
|
|
e9699abc81 | ||
|
|
b186c47e07 | ||
|
|
f26f11f89b | ||
|
|
f8bbc2d931 | ||
|
|
cbe0d5c3dd | ||
|
|
c6a802a286 | ||
|
|
27fce7267f | ||
|
|
5b0c302530 | ||
|
|
47c29d9eb5 | ||
|
|
228dad8b85 | ||
|
|
70ad933435 | ||
|
|
b061219bac | ||
|
|
da25290fad | ||
|
|
9f55aa657b | ||
|
|
2740dd094b | ||
|
|
0089f232c4 | ||
|
|
64ffe1ee14 | ||
|
|
b3d1cc81cd | ||
|
|
f8691e3868 | ||
|
|
1ab14afeaa | ||
|
|
27a6b1a5c4 | ||
|
|
78c8034b4b | ||
|
|
3a9c6f65de | ||
|
|
f5bedf6239 | ||
|
|
6e3dcc5192 | ||
|
|
5f6c3f1078 | ||
|
|
5f67e5ab03 | ||
|
|
e7a9837da1 | ||
|
|
c80c892faf | ||
|
|
94dce083f1 | ||
|
|
bad39c697f | ||
|
|
c2a9ea96e9 | ||
|
|
aff110df5d | ||
|
|
428bed2cde | ||
|
|
f06da4c033 | ||
|
|
fd79d5d034 | ||
|
|
c4c4fdc647 | ||
|
|
661bf3ecc2 | ||
|
|
04c99a5a36 | ||
|
|
ded4e015bd | ||
|
|
97becdb7e3 | ||
|
|
929766b177 | ||
|
|
2db6f7a9ae | ||
|
|
b6d8a340e5 | ||
|
|
175f75c3a2 | ||
|
|
0eda9b109f | ||
|
|
f07be06dfe | ||
|
|
32a770b9b5 | ||
|
|
2f2b440196 | ||
|
|
379422f457 | ||
|
|
3519756da3 | ||
|
|
8746f04030 | ||
|
|
c7d9697b92 | ||
|
|
66053895ad | ||
|
|
d7c21e06ad | ||
|
|
799589874e | ||
|
|
97733ca337 | ||
|
|
1a0474420b | ||
|
|
f1c1f2dbe8 | ||
|
|
bfb5752622 | ||
|
|
b3e9f594dc | ||
|
|
f81ca0688e | ||
|
|
095a9149b4 | ||
|
|
322a86c105 | ||
|
|
015fb299ba | ||
|
|
9e7f407d35 | ||
|
|
96b891fbd0 | ||
|
|
0cdb35dcfe | ||
|
|
56be5c84cb | ||
|
|
2cb8fc25f5 | ||
|
|
941c5efd07 | ||
|
|
bbdb335e92 | ||
|
|
cd0c5c8e2a | ||
|
|
8924d36925 | ||
|
|
8a39bf6ac8 | ||
|
|
a6b4f05f7f | ||
|
|
34c39464ae | ||
|
|
3dd45c1628 | ||
|
|
b962c168c4 | ||
|
|
c12bd32682 | ||
|
|
ef20f1578d | ||
|
|
47c9e07857 | ||
|
|
6d8208d7f4 | ||
|
|
946e76c3cc | ||
|
|
08bd8176b9 | ||
|
|
2ea06425f4 | ||
|
|
95ca7b23d3 | ||
|
|
ca6eb03f1e | ||
|
|
8831a41bff | ||
|
|
6418beb186 | ||
|
|
ebbc5cdc2c | ||
|
|
abceb71dcb | ||
|
|
7ab898ee85 | ||
|
|
904840c4cb | ||
|
|
ecd39f5435 | ||
|
|
7742b2c570 | ||
|
|
89b20b3cda | ||
|
|
4b0f62e058 | ||
|
|
f7281a7481 | ||
|
|
ae38fecae7 | ||
|
|
f5577d45c5 | ||
|
|
0b45c41a99 | ||
|
|
9b74e7fdec | ||
|
|
b522360357 | ||
|
|
109cb776a0 | ||
|
|
9fdc55814d | ||
|
|
2079a90f02 | ||
|
|
7fdf90e87d | ||
|
|
f7bc2e2cf7 | ||
|
|
9d442b9b18 | ||
|
|
3c777905f5 | ||
|
|
c91bbc94fd | ||
|
|
e51142ae32 | ||
|
|
1fd7d7f602 | ||
|
|
6ceb50c0bd | ||
|
|
b63b189fda | ||
|
|
b8ff8cb535 | ||
|
|
2e9124c52d | ||
|
|
261ccdda51 | ||
|
|
974108088a | ||
|
|
ebc9d2d1b1 | ||
|
|
3733d827af | ||
|
|
1333aa10f8 | ||
|
|
8621de6d09 | ||
|
|
b48775d222 | ||
|
|
3b918c38fd | ||
|
|
e21e7d863d | ||
|
|
3b4329fcc2 | ||
|
|
08bc5ef72f | ||
|
|
f378ee441a | ||
|
|
bbf0fda484 | ||
|
|
8cb4d8149b | ||
|
|
eceadf9f10 | ||
|
|
ae63e09a92 | ||
|
|
b778578353 | ||
|
|
a2ec9c4a41 | ||
|
|
30fc4a91d6 | ||
|
|
1d6307d142 | ||
|
|
93524c0e4a | ||
|
|
30e2d310f3 | ||
|
|
d1c87a65b2 | ||
|
|
961615f1c4 | ||
|
|
9a92132981 | ||
|
|
d78120c80d | ||
|
|
c560b7cd1f | ||
|
|
f0a8e1c0bc | ||
|
|
9e77f104f6 | ||
|
|
029d6bf7f6 | ||
|
|
3cb0e3ecd4 | ||
|
|
8708897751 | ||
|
|
d920e8705f | ||
|
|
f5e9bf2459 | ||
|
|
6a88aaf782 | ||
|
|
9d9c09743c | ||
|
|
e076f34993 | ||
|
|
923ec7ce5e | ||
|
|
15ae3cecb1 | ||
|
|
3f8e26644a | ||
|
|
a6a2a6ebba | ||
|
|
4175816577 | ||
|
|
36679ffbc7 | ||
|
|
b2c4cd721c | ||
|
|
da9aeb54f0 | ||
|
|
0a9d7c5560 | ||
|
|
14843ddda7 | ||
|
|
dada671f97 | ||
|
|
1a7214c75b | ||
|
|
23eede12fc | ||
|
|
58910be98e | ||
|
|
f27319e8cb | ||
|
|
737c190107 | ||
|
|
6c2989a4d3 | ||
|
|
a5c05bd2f3 | ||
|
|
a136c5349e | ||
|
|
8b8d845068 | ||
|
|
7c41b5d4ed | ||
|
|
39336a447b | ||
|
|
f58c33354e | ||
|
|
9a9b5037ec | ||
|
|
effe9c281a | ||
|
|
c590ff875b | ||
|
|
3771c325a2 | ||
|
|
7746e95cad | ||
|
|
767c10f886 | ||
|
|
d97a15df57 | ||
|
|
55cd89a0e5 | ||
|
|
8ef7ac5a87 | ||
|
|
5b6a06b33b | ||
|
|
cd4971962a | ||
|
|
f18e2e1e55 | ||
|
|
0d8925c787 | ||
|
|
c773cd1842 | ||
|
|
bf9d2c82ac | ||
|
|
7958785588 | ||
|
|
56ea23c4bf | ||
|
|
c382f773a7 | ||
|
|
46995511f1 | ||
|
|
7d60037998 | ||
|
|
cdf53c812a | ||
|
|
b628755464 | ||
|
|
69da2f14c7 | ||
|
|
3ea0b0847a | ||
|
|
47da1bcdb4 | ||
|
|
691dc18f3b | ||
|
|
5ddd948ace | ||
|
|
b815c924ff | ||
|
|
f46d72a9eb | ||
|
|
9b58d1161b | ||
|
|
3208a34b75 | ||
|
|
e50bb39770 | ||
|
|
bcf9c7df30 | ||
|
|
e8f97beb84 | ||
|
|
d73ff60c36 | ||
|
|
6414f14420 | ||
|
|
5787753279 | ||
|
|
4a838597f8 | ||
|
|
c3c5e2b5b0 | ||
|
|
6a184462c6 | ||
|
|
10def6e48a | ||
|
|
29c97b9d79 | ||
|
|
d5e1f8814f | ||
|
|
972cb55801 | ||
|
|
a65a1a05f0 | ||
|
|
659afd9245 | ||
|
|
73b9b353e6 | ||
|
|
34337d18fa | ||
|
|
f57917e2b6 | ||
|
|
a39bc1182a | ||
|
|
795e42f609 | ||
|
|
b39b0a5ca6 | ||
|
|
dcea49c682 | ||
|
|
c1e7d043f9 | ||
|
|
f354b86d1d | ||
|
|
9d4977e2b3 | ||
|
|
595147bacc | ||
|
|
39681dcb8a | ||
|
|
31be6a2cca | ||
|
|
0286557701 | ||
|
|
805e3c920c | ||
|
|
26f05a0800 | ||
|
|
b944d073de | ||
|
|
56fdb2d4a8 | ||
|
|
c83bd885f6 | ||
|
|
d4ec177c28 | ||
|
|
e9839335c6 | ||
|
|
562a6d6ab8 | ||
|
|
4cfeb88bb3 | ||
|
|
54858261f8 | ||
|
|
8e4d3df400 | ||
|
|
dc47f68ff3 | ||
|
|
c1852f4728 | ||
|
|
0e9c7761f5 | ||
|
|
b4acb06665 | ||
|
|
98107e40bc | ||
|
|
d1d1da7875 | ||
|
|
a800094b21 | ||
|
|
89736d1d45 | ||
|
|
06650c14ad | ||
|
|
1c1066eff2 | ||
|
|
5bb63cf3e5 | ||
|
|
2ba39a9b80 | ||
|
|
7fef84a006 | ||
|
|
f6458cd8ee | ||
|
|
5c18dad1d8 | ||
|
|
3f4efc51c9 | ||
|
|
841ac0cb74 | ||
|
|
e2be9580c6 | ||
|
|
154cf18b34 | ||
|
|
0a93763c48 | ||
|
|
c4c654079d | ||
|
|
92f8e67a60 | ||
|
|
1814f06311 | ||
|
|
2dbc704448 | ||
|
|
e2478b4eb8 | ||
|
|
b7b3e8d4cb | ||
|
|
0a089351e9 | ||
|
|
386a991ad7 | ||
|
|
e028834ac3 | ||
|
|
695a4966b5 | ||
|
|
3973f5ef70 | ||
|
|
24ccf60870 | ||
|
|
c4a3f06b51 | ||
|
|
5dff1f7678 | ||
|
|
d3d07f9e4e | ||
|
|
e5500b978c | ||
|
|
2fea94ce9b | ||
|
|
90b6106fb4 | ||
|
|
6e15d1e480 | ||
|
|
d35588b993 | ||
|
|
957ecf0fe1 | ||
|
|
83dc47c3b5 | ||
|
|
fa4d35487c | ||
|
|
624a20c9bf | ||
|
|
58cd5c2896 | ||
|
|
bb8a802d65 | ||
|
|
f757e98da0 | ||
|
|
49661c0bf0 | ||
|
|
42f36b8e74 | ||
|
|
9bc3c9a902 | ||
|
|
1dd04740bb | ||
|
|
35201e6615 | ||
|
|
86d416bccf | ||
|
|
979841665a | ||
|
|
c29b8c114b | ||
|
|
a03eae3c87 | ||
|
|
704c2a261e | ||
|
|
8fabdd1926 | ||
|
|
f9df373a16 | ||
|
|
f959f98012 | ||
|
|
e12a93f36b | ||
|
|
c3aca0a88d | ||
|
|
add7819945 | ||
|
|
3c6d3bbd80 | ||
|
|
66e1315b08 | ||
|
|
9bbe010fc9 | ||
|
|
4f81d164c8 | ||
|
|
c808e9135d | ||
|
|
1e7861ef15 | ||
|
|
a70b8c6dff | ||
|
|
ce86638e8e | ||
|
|
06ab978f53 | ||
|
|
9c2026982c | ||
|
|
863b910311 | ||
|
|
68da3b7580 | ||
|
|
43556f219b | ||
|
|
6022144dce | ||
|
|
18a0e3fd9e | ||
|
|
6c7c6e4b22 | ||
|
|
ffa7a74575 | ||
|
|
37f9c28133 | ||
|
|
ea7a012e0a | ||
|
|
0d594da6c9 | ||
|
|
ed588bf26c | ||
|
|
b6673fd828 | ||
|
|
060bcc76ef | ||
|
|
8fc8dd2960 | ||
|
|
1dc36275aa | ||
|
|
f305e68202 | ||
|
|
5c481df41f | ||
|
|
e86c626536 | ||
|
|
44559bab1c | ||
|
|
2ae28f1c63 | ||
|
|
b3e16d2b2a | ||
|
|
6f186080df | ||
|
|
c9d9335b71 | ||
|
|
22c8a36224 | ||
|
|
37f705d517 | ||
|
|
8b9985a7dc | ||
|
|
f51e585d8d | ||
|
|
a6c204e444 | ||
|
|
a0a215400e | ||
|
|
37e3166f98 | ||
|
|
ddccdc2ade | ||
|
|
755a15d5e5 | ||
|
|
5c6716bd98 | ||
|
|
7d87417095 | ||
|
|
77bb6e9a82 | ||
|
|
019174d7e2 | ||
|
|
cac88cbb81 | ||
|
|
25500da486 | ||
|
|
53f2c7126b | ||
|
|
eb95026a47 | ||
|
|
6b0f8bc325 | ||
|
|
a80f41a89a | ||
|
|
8690f8aa8d | ||
|
|
d9c55a284b | ||
|
|
c8a10a8b75 | ||
|
|
8e8dc76c3d | ||
|
|
725679f5d4 | ||
|
|
dfce07d7eb | ||
|
|
73c9e37eba | ||
|
|
ccd5bd8394 | ||
|
|
fd9b7db999 | ||
|
|
f3fe115733 | ||
|
|
16f3f74a90 | ||
|
|
4a6b85003a | ||
|
|
bf651c7e40 | ||
|
|
473ff4d308 | ||
|
|
5d64e8c61b | ||
|
|
b559bb3904 | ||
|
|
359f250b3b | ||
|
|
a20336a11a | ||
|
|
0313c5705f | ||
|
|
14469638b3 | ||
|
|
d15c5fc9d1 | ||
|
|
16e7d266fe | ||
|
|
b716a98548 | ||
|
|
39f40ed906 | ||
|
|
a644a1cee1 | ||
|
|
61fd8dc778 | ||
|
|
145d5e6302 | ||
|
|
ad071e48df | ||
|
|
28b5717f5b | ||
|
|
41b818caa6 | ||
|
|
cf87a654fb | ||
|
|
9ffab097bb | ||
|
|
bdc3c2b6b5 | ||
|
|
3f39662ba1 | ||
|
|
aa485547a6 | ||
|
|
747f23bfe1 | ||
|
|
d885841826 | ||
|
|
0c457ef61d | ||
|
|
9462f1455b | ||
|
|
c07325e71e | ||
|
|
5eda7b3fc3 | ||
|
|
6b0c208b75 | ||
|
|
b0c6a26bea | ||
|
|
67a0893a97 | ||
|
|
d7438a26fb | ||
|
|
ee7ab38028 | ||
|
|
8d999ea278 | ||
|
|
0851cf50a0 | ||
|
|
0b6676aaa3 | ||
|
|
c64167c76a | ||
|
|
72a6419aff | ||
|
|
64e26a6b8b | ||
|
|
23184df1b9 | ||
|
|
dd123d7b87 | ||
|
|
0e5d1e2325 | ||
|
|
4939ce37c3 | ||
|
|
6e7e668d39 | ||
|
|
2d086a5c6e | ||
|
|
f7e592eac0 | ||
|
|
c13abafb09 | ||
|
|
eeed0720f7 | ||
|
|
f099b74aa6 | ||
|
|
f7f54692a7 | ||
|
|
b97eff4284 | ||
|
|
f234dc61bc | ||
|
|
83b40d2ccb | ||
|
|
a8d3d71ea9 | ||
|
|
621772f359 | ||
|
|
63cd60b237 | ||
|
|
0df8e8522f | ||
|
|
1ebbf6e349 | ||
|
|
1b71f6a60a | ||
|
|
a3241e48df | ||
|
|
69396f3d59 | ||
|
|
9042d065ae | ||
|
|
300505e127 | ||
|
|
f7bf1d624b | ||
|
|
d7b08a0181 | ||
|
|
3941f59bbc | ||
|
|
802a30e16c | ||
|
|
032563d7a4 | ||
|
|
fe7ad2032b | ||
|
|
fcde4b3fc2 | ||
|
|
1073ed6c24 | ||
|
|
e5ce3450e8 | ||
|
|
a6f1e91747 | ||
|
|
8dcf5a3654 | ||
|
|
22f9cf7c4c | ||
|
|
38f2879d82 | ||
|
|
4060695db6 | ||
|
|
81fab695ff | ||
|
|
5259428106 | ||
|
|
9774bd858f | ||
|
|
d30379251d | ||
|
|
0e6156e51c | ||
|
|
d610c9727d | ||
|
|
adba04e2dd | ||
|
|
b2aa6552b1 | ||
|
|
8b4b060425 | ||
|
|
2f11853f0d | ||
|
|
388685b784 | ||
|
|
d6939b2913 | ||
|
|
90ee3b749f | ||
|
|
e51c297a7d | ||
|
|
283e8f20b3 | ||
|
|
850c3fb8cd | ||
|
|
7046e994d7 | ||
|
|
099d802a03 | ||
|
|
453df64f69 | ||
|
|
8a2cb42546 | ||
|
|
6413ed67c6 | ||
|
|
4dc7a97322 | ||
|
|
a2d8189c74 | ||
|
|
f806c82c33 | ||
|
|
f46598cd74 | ||
|
|
919d34c90d | ||
|
|
e230af8991 | ||
|
|
09842f5d72 | ||
|
|
911ff31f69 | ||
|
|
f1c9c4e231 | ||
|
|
fa04ce5465 | ||
|
|
45ff03abfd | ||
|
|
20cebb8a05 | ||
|
|
f79e3ff51f | ||
|
|
45a67a7f3d | ||
|
|
281469fe5c | ||
|
|
461ad7fa42 | ||
|
|
899d221b67 | ||
|
|
b246e163d2 | ||
|
|
d83bb0f964 | ||
|
|
a9626475e6 | ||
|
|
d1374eca15 | ||
|
|
6289162684 | ||
|
|
25b3187094 | ||
|
|
bd2c9b172a | ||
|
|
246f953b4d | ||
|
|
433098a388 | ||
|
|
f442d9d030 | ||
|
|
61d7ae03f6 | ||
|
|
22e144cb3b | ||
|
|
a3f0d7092d | ||
|
|
27efdba185 | ||
|
|
59a1fa9992 | ||
|
|
a5140ca293 | ||
|
|
a639b02890 | ||
|
|
ead64d3c73 | ||
|
|
be761071f8 | ||
|
|
e48f19269f | ||
|
|
82ebfb99c5 | ||
|
|
4d69bf2aba | ||
|
|
4e4f77de90 | ||
|
|
fe0c3176d8 | ||
|
|
8cfbdf46e0 | ||
|
|
2234d056fc | ||
|
|
4a4c447bd2 | ||
|
|
933a95023c | ||
|
|
1d8b5f81a1 | ||
|
|
81df632e48 | ||
|
|
b0df7cbc68 | ||
|
|
25d90a3c78 | ||
|
|
384637efdf | ||
|
|
ef3d3e6443 | ||
|
|
675e98fa1a | ||
|
|
0b059c71c1 | ||
|
|
8988f08243 | ||
|
|
adb54d9d7c | ||
|
|
fdf58388a3 | ||
|
|
a5244cdb41 | ||
|
|
6caec5dbce | ||
|
|
4419ab154b | ||
|
|
67c529ffe9 | ||
|
|
057ba0c6e1 | ||
|
|
6117ea755f | ||
|
|
a56c923f0e | ||
|
|
3686241bf5 | ||
|
|
0311c6f52b | ||
|
|
f59dccd5fd | ||
|
|
01d8dabef4 | ||
|
|
773993c6f2 | ||
|
|
2c5646b1c2 | ||
|
|
b8d71e24e7 | ||
|
|
4db3f7a742 | ||
|
|
298203f909 | ||
|
|
a1cafe62cf | ||
|
|
cab75ef0d6 | ||
|
|
33a8d86684 | ||
|
|
b4d329725b | ||
|
|
6efe8922b3 | ||
|
|
f5bd097584 | ||
|
|
7492841b5f | ||
|
|
1430bd9e88 | ||
|
|
e0400305ab | ||
|
|
65d5120272 | ||
|
|
cbb398e58c | ||
|
|
57d8735624 | ||
|
|
fd99bd2ed1 | ||
|
|
46c89534fd | ||
|
|
5650ee580f | ||
|
|
62f126b5d9 | ||
|
|
51cb354f35 | ||
|
|
d4f591354d | ||
|
|
0895af0ecc | ||
|
|
fafe25fee4 | ||
|
|
3db6cc1a1c | ||
|
|
408f5a05fc | ||
|
|
28ee23c5f3 | ||
|
|
37804e67c1 | ||
|
|
a7bce9c8e9 | ||
|
|
bcf2d16a10 | ||
|
|
28d2b92abb | ||
|
|
2f3b6df529 | ||
|
|
fa2cbbb0cc | ||
|
|
d1ab9e77de | ||
|
|
3484261b50 | ||
|
|
7a03fb53ea | ||
|
|
d7f050efc4 | ||
|
|
2d11a29a79 | ||
|
|
0b5aecfdb5 | ||
|
|
487925b1f2 | ||
|
|
54c8183666 | ||
|
|
c878952237 | ||
|
|
26d6428836 | ||
|
|
cf31a31430 | ||
|
|
e10e1c665f | ||
|
|
0834ff26be | ||
|
|
014b8db20d | ||
|
|
34445e6f92 | ||
|
|
7d00e01e00 | ||
|
|
300fabca21 | ||
|
|
e17dc78aeb | ||
|
|
1b9e00efe1 | ||
|
|
152b4d3765 | ||
|
|
e0b95ab2ae | ||
|
|
e0520edccc | ||
|
|
581b32e8a2 | ||
|
|
e71d61ef37 | ||
|
|
0a51d48703 | ||
|
|
30c7f9e020 | ||
|
|
71f7771a97 | ||
|
|
b36e5b7906 | ||
|
|
bb25449b35 | ||
|
|
dc8053dfcd | ||
|
|
20be8e23ba | ||
|
|
d83f476439 | ||
|
|
29352b8838 | ||
|
|
c0390c3c5d | ||
|
|
81dd2eeb47 | ||
|
|
3beedf4dfe | ||
|
|
f5be71ecbd | ||
|
|
58ba5dfedc | ||
|
|
040d265f3c | ||
|
|
fccf4db163 | ||
|
|
1122498b54 | ||
|
|
6a98aea209 | ||
|
|
ae4d589c8e | ||
|
|
6ccb59957d | ||
|
|
edd4e865b3 | ||
|
|
6ff7614e94 | ||
|
|
065a14952a | ||
|
|
1e620931a2 | ||
|
|
a46a531a89 | ||
|
|
06c95b8d36 | ||
|
|
39967a9211 | ||
|
|
a18e6f4b63 | ||
|
|
ef475000c3 | ||
|
|
f08a2444be | ||
|
|
c746d58210 | ||
|
|
ccb48505bf | ||
|
|
c9ddd25e99 | ||
|
|
42234d9163 | ||
|
|
e058885c7c | ||
|
|
1e3556cc8e | ||
|
|
b1cda242df | ||
|
|
b593fce745 | ||
|
|
282cdeaad0 | ||
|
|
6e54c7d672 | ||
|
|
51a12ee5cb | ||
|
|
fcea40685c | ||
|
|
07450a7a51 | ||
|
|
19fa8d15a6 | ||
|
|
db2980f598 | ||
|
|
b3e4fd65aa | ||
|
|
d46eca750c | ||
|
|
d441e92adc | ||
|
|
5f70d71203 | ||
|
|
59ffb2ea8e | ||
|
|
ce59b8037f | ||
|
|
56e67ba70a | ||
|
|
108e9e216e | ||
|
|
b219b4e832 | ||
|
|
ef4e0d76ec | ||
|
|
61c75c06a9 | ||
|
|
091705b3f2 | ||
|
|
6aa5b06619 | ||
|
|
df8fc0b229 | ||
|
|
d7fcda1f83 | ||
|
|
b832c24185 | ||
|
|
3b04f8c695 | ||
|
|
0ef11b58d2 | ||
|
|
bf13bbf506 | ||
|
|
92c94aa22a | ||
|
|
1c0785432b | ||
|
|
dcb9a50061 | ||
|
|
de7f4bdb6e | ||
|
|
73d636dba6 | ||
|
|
3f1c453ff6 | ||
|
|
ecb6baaebb | ||
|
|
e1cba45486 | ||
|
|
ea4181b243 | ||
|
|
6c5b9f8851 | ||
|
|
c1fccaf240 | ||
|
|
e7c223da7d | ||
|
|
6bfe1ba876 | ||
|
|
574e5dab18 | ||
|
|
5dc9b83831 | ||
|
|
57a214a9a4 | ||
|
|
cf48eb94af | ||
|
|
683f1e488a | ||
|
|
e888ae4da7 | ||
|
|
4512f2a375 | ||
|
|
68442afa0f | ||
|
|
ddf4d0cbc3 | ||
|
|
4be185e428 | ||
|
|
f75e66ac19 | ||
|
|
abda781f02 | ||
|
|
c1533ec66d | ||
|
|
c8319446b0 | ||
|
|
7ae452cf13 | ||
|
|
4c024c5faa | ||
|
|
9d16e9d98c | ||
|
|
48357d08f7 | ||
|
|
8e0bcf40db | ||
|
|
85ef5f04dd | ||
|
|
18e0748d14 | ||
|
|
cdde5b098e | ||
|
|
f83964752d | ||
|
|
0c3e34bbf9 | ||
|
|
6dea7faac8 | ||
|
|
726f482c33 | ||
|
|
cb242f664a | ||
|
|
edc0345970 | ||
|
|
a50535dd76 | ||
|
|
505942aa25 | ||
|
|
38a593a9de | ||
|
|
da87fb2551 | ||
|
|
d4dc65a4ba | ||
|
|
53463caedd | ||
|
|
0707a0fad8 | ||
|
|
dd1024af27 | ||
|
|
7edc58a6da | ||
|
|
5a5da0cace | ||
|
|
53f02ed366 | ||
|
|
27cfa885e7 | ||
|
|
beb132d604 | ||
|
|
b1b10298b4 | ||
|
|
b1993ff7d2 | ||
|
|
2afe5fe5b2 | ||
|
|
52fa8f64cc | ||
|
|
4f7978070f | ||
|
|
a909983822 | ||
|
|
5267cad896 | ||
|
|
ce274cf65b | ||
|
|
55b6b2e34d | ||
|
|
5e4e2971b5 | ||
|
|
a933bcfb1e | ||
|
|
b4b38e27f3 | ||
|
|
bc70824b57 | ||
|
|
87ecaaa6a4 | ||
|
|
c4cd9cd802 | ||
|
|
bbe0e8c5a9 | ||
|
|
3f8984d2dc | ||
|
|
f695c4598a | ||
|
|
eada1bc023 | ||
|
|
0ebb55551c | ||
|
|
20bce419c9 | ||
|
|
ca3fc370c3 | ||
|
|
94a8b78d1a | ||
|
|
06e9ff6fcf | ||
|
|
699e74656d | ||
|
|
b3ad6ef8b8 | ||
|
|
673c8e25f5 | ||
|
|
333c8e812d | ||
|
|
f0f7a88cfb | ||
|
|
0661d78bce | ||
|
|
dc4aece272 | ||
|
|
57a312b0b4 | ||
|
|
b9897ed6ac | ||
|
|
733230f037 | ||
|
|
fc6ba3e6ad | ||
|
|
2d7c539c5f | ||
|
|
7f34965026 | ||
|
|
de8b915cff | ||
|
|
d56040581d | ||
|
|
9869b7bbb5 | ||
|
|
7df97c28c0 | ||
|
|
018a559f78 | ||
|
|
3b32ad5fed | ||
|
|
f796d2a842 | ||
|
|
2f7e84e126 | ||
|
|
996ddbf3db | ||
|
|
7779ba2ac5 | ||
|
|
4dadeab762 | ||
|
|
f4d3131ef3 | ||
|
|
efa2ac803e | ||
|
|
e0be9d46bd | ||
|
|
106b27dbec | ||
|
|
f764567523 | ||
|
|
245f8669bc | ||
|
|
b265d9bba6 | ||
|
|
72228574f9 | ||
|
|
fba1ac3d90 | ||
|
|
4a73fb565e | ||
|
|
13011ed4af | ||
|
|
b2eccdefd3 | ||
|
|
dd2d6a977b | ||
|
|
38cd1b6040 | ||
|
|
1a28bbcde8 | ||
|
|
91013daadc | ||
|
|
1ad435e79d | ||
|
|
fd5c300c53 | ||
|
|
aa7d9cf29c | ||
|
|
8db1c3400e | ||
|
|
e723336706 | ||
|
|
b5cd4c36ab | ||
|
|
d1928f2647 | ||
|
|
61621ae00b | ||
|
|
ac20755c45 | ||
|
|
610202bbbd | ||
|
|
58ce7134e1 | ||
|
|
71f91cd79e | ||
|
|
8c821ecad3 | ||
|
|
c704940462 | ||
|
|
6a7cd1b89a | ||
|
|
e29da0ba13 | ||
|
|
7791fa10f5 | ||
|
|
48ed3c8f31 | ||
|
|
7ad7a9b94e | ||
|
|
54b929ba24 | ||
|
|
7b655c6260 | ||
|
|
4e8d41b145 | ||
|
|
f036774e07 | ||
|
|
97d1bd7685 | ||
|
|
8e39449856 | ||
|
|
30f1a5a6eb | ||
|
|
627abb047c | ||
|
|
9284008afe | ||
|
|
9159f4616f | ||
|
|
73296b1430 | ||
|
|
c74e622d69 | ||
|
|
f48eb55516 | ||
|
|
b01f6ef8d3 | ||
|
|
167185ef45 | ||
|
|
777a8ffeaa | ||
|
|
b2b3e6c485 | ||
|
|
613d502fc1 | ||
|
|
79da457db4 | ||
|
|
b1a431ed82 | ||
|
|
b0f2776a23 | ||
|
|
900c346ee8 | ||
|
|
7541a87b21 | ||
|
|
2547c67ebd | ||
|
|
5ae6a0b287 | ||
|
|
f718fc0c89 | ||
|
|
57e69ba7be | ||
|
|
6168b3e047 | ||
|
|
3e500500e0 | ||
|
|
42f06ff84a | ||
|
|
a7824fc473 | ||
|
|
67e7fd6928 | ||
|
|
450e8badba | ||
|
|
704c211e11 | ||
|
|
0ad871cb5b | ||
|
|
9920605570 | ||
|
|
b1e8678dbf | ||
|
|
e2cd2ddc0d | ||
|
|
a3be0dfddf | ||
|
|
4b615ffc0b | ||
|
|
64cbd189ad | ||
|
|
0fae5a128f | ||
|
|
518b44216f | ||
|
|
9ef4512ef9 | ||
|
|
8bb5f1e955 | ||
|
|
8f8932f617 | ||
|
|
23fd1d75c4 | ||
|
|
593dcef550 | ||
|
|
d769fe9074 |
No files matched your search
@@ -6,10 +6,10 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
|
||||
感谢您愿意为 EhViewer 做出贡献!
|
||||
提交问题报告前,还请首先完成文末的自查步骤
|
||||
|
||||
Thanks for your contribution on Ehviewer-Overhauled Project!
|
||||
Thanks for your contribution on EhViewer Project!
|
||||
Please finish verify steps which list in the end first before create bug report
|
||||
|
||||
- type: textarea
|
||||
@@ -18,7 +18,9 @@ body:
|
||||
label: 复现步骤 / Step to reproduce
|
||||
description: |
|
||||
请从全新安装应用开始,在此处写下复现的方式。请尽量附带录屏
|
||||
如果不能稳定复现,请在下方上传应用日志
|
||||
Describe how to reproduce here with fresh installation, with screen recording if possible.
|
||||
If you can't reproduce it consistently, please upload app logs below
|
||||
placeholder: |
|
||||
0. 重新安装应用 / Fresh install
|
||||
1.
|
||||
@@ -55,6 +57,15 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: 应用日志 / App logs
|
||||
description: 您可以通过设置-高级-导出日志来获得日志文件 / You can get logs file in Settings - Advanced - Dump logcat
|
||||
placeholder: 06-15 17:44:53.704 23382 23382 E ...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: more
|
||||
attributes:
|
||||
@@ -81,8 +92,8 @@ body:
|
||||
id: ci
|
||||
attributes:
|
||||
label: EhViewer CI 版本 / EhViewer CI Version
|
||||
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
|
||||
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
|
||||
description: 位于设置 - 关于中的版本号后 / Next to the version code in Settings - About
|
||||
placeholder: 26f05a0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -124,14 +135,11 @@ body:
|
||||
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
|
||||
required: false
|
||||
|
||||
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
|
||||
required: true
|
||||
|
||||
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
|
||||
- label: 您已搜索过 [Issue Tracker](https://github.com/FooIbar/EhViewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/FooIbar/EhViewer/issues), No duplicate or related open issue has been found
|
||||
required: true
|
||||
|
||||
- label: 您确保这个 Issue 只提及一个问题。如果您有多个问题报告,烦请发起多个 Issue / Ensure there is only one bug report in this issue. Please make mutiply issue for mutiply bugs
|
||||
required: true
|
||||
|
||||
- label: 您确保已使用最新 CI 版本测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest CI version
|
||||
- label: 您确保已使用最新 [CI 版本](https://github.com/FooIbar/EhViewer/actions)测试,并且该问题在最新 CI 版本中并未解决 / This bug have not solved in latest [CI version](https://github.com/FooIbar/EhViewer/actions)
|
||||
required: true
|
||||
@@ -1,5 +1 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 所有其他问题 / All other questions
|
||||
url: https://github.com/Ehviewer-Overhauled/Ehviewer/discussions
|
||||
about: 转到 Discussions / Turn to discussions
|
||||
blank_issues_enabled: false
|
||||
@@ -1,94 +0,0 @@
|
||||
name: 功能请求 / Feature request
|
||||
description: 提出一个功能建议 / Suggest an idea
|
||||
labels:
|
||||
- "enhancement"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢您愿意为 Ehviewer-Overhauled 做出贡献!
|
||||
提交功能建议前,还请首先完成文末的自查步骤
|
||||
|
||||
Thanks for your contribution on Ehviewer-Overhauled Project!
|
||||
Please finish verify steps which list in the end first before suggest an idea
|
||||
|
||||
- type: textarea
|
||||
id: request
|
||||
attributes:
|
||||
label: 需求 / Requirement
|
||||
description: |
|
||||
在此处描述您的需求。这通常会是一个您想要的功能
|
||||
Ddescribe what you need here.
|
||||
placeholder: |
|
||||
我需要 XXX 功能 ...
|
||||
I want ABC feature ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: impl
|
||||
attributes:
|
||||
label: 建议实现 / Suggested implements
|
||||
description: |
|
||||
在此处表述您建议的实现方式。如有可能,UI 类功能请求还请尽量附上图示
|
||||
Describe your suggested implements here, It's recommend to add a photo if you are making a UI feature request.
|
||||
placeholder: |
|
||||
建议在 XX 处添加 XX ...
|
||||
I recommend add ABC feature to DEF...
|
||||
图片(如果有)/ Photos (if exists)
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: more
|
||||
attributes:
|
||||
label: 备注 / Addition details
|
||||
description: |
|
||||
在此处写下其他您想说的内容
|
||||
Describe addition details here
|
||||
placeholder: |
|
||||
其他有用的信息与附件
|
||||
Additional details and attachments
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: EhViewer 版本号 / EhViewer version code
|
||||
description: 您可以在设置 - 关于处找到版本号 / You can get version code in Settings - About
|
||||
placeholder: 1.6.9.13
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: ci
|
||||
attributes:
|
||||
label: EhViewer CI 版本 / EhViewer CI Version
|
||||
description: 直接将对应 GitHub Action CI 下载页面网址填入即可 / Simply drop GitHub Action CI download page url here
|
||||
placeholder: https://github.com/Ehviewer-Overhauled/Ehviewer/actions/runs/XXXXXXXXXX
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: check
|
||||
attributes:
|
||||
label: 自查步骤 / Verify steps
|
||||
description: |
|
||||
请确认您已经遵守所有必选项
|
||||
Please ensure you have obtained all needed options
|
||||
options:
|
||||
- label: 如果您有足够的时间和能力,并愿意为此提交 PR ,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
|
||||
required: false
|
||||
|
||||
- label: 您已仔细查看并知情 [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) 中的内容 / You have checked [Q&A](https://github.com/Ehviewer-Overhauled/Ehviewer/issues/61) carefully
|
||||
required: true
|
||||
|
||||
- label: 您已搜索过 [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/Ehviewer-Overhauled/Ehviewer/issues), No duplicate or related open issue has been found
|
||||
required: true
|
||||
|
||||
- label: 您确保这个 Issue 只提及一个功能。如果您有多个功能请求,烦请发起多个 Issue / Ensure there is only one feature request in this issue. Please make mutiply issue for mutiply feature request
|
||||
required: true
|
||||
|
||||
- label: 您确保已使用最新 CI 版本测试,并且该功能在最新 CI 版本中并未按照预期实现 / This feature don't implement correctly in latest CI version
|
||||
required: true
|
||||
+37
-10
@@ -4,8 +4,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags-ignore:
|
||||
- "*"
|
||||
- 'renovate/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
@@ -13,25 +14,51 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Setup Java 19
|
||||
- name: Setup Java 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 19
|
||||
java-version: 17
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --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: Gradle Build
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "./app/src/main/rust"
|
||||
|
||||
- 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: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
arguments: assembleRelease
|
||||
|
||||
- name: Spotless Check
|
||||
run: ./gradlew spotlessCheck
|
||||
|
||||
- name: CMake Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
app/.cxx
|
||||
app/src/main/cpp/nettle/nettle
|
||||
key: ${{ runner.os }}-cmake-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-cmake-
|
||||
|
||||
- name: Gradle Build
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
- name: Update ARM64
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
name: Pull Request Check
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Setup Java 19
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 19
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
|
||||
|
||||
- name: Gradle Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
arguments: assembleRelease
|
||||
|
||||
- name: Update ARM64
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: arm64-v8a-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
|
||||
|
||||
- name: Update x86_64
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: x86_64-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-x86_64-release.apk
|
||||
|
||||
- name: Update ARM32
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: armeabi-v7a-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
|
||||
|
||||
- name: Update x86
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: x86-${{ github.sha }}
|
||||
path: app/build/outputs/apk/release/app-x86-release.apk
|
||||
@@ -11,19 +11,32 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Setup Java 19
|
||||
- name: Setup Java 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 19
|
||||
java-version: 17
|
||||
|
||||
- name: Install NDK
|
||||
run: |
|
||||
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.1.8937393" --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: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "./app/src/main/rust"
|
||||
|
||||
- name: CMake Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
app/.cxx
|
||||
app/src/main/cpp/nettle/nettle
|
||||
key: ${{ runner.os }}-cmake-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-cmake-
|
||||
|
||||
- name: Gradle Build
|
||||
uses: gradle/gradle-build-action@v2
|
||||
@@ -42,7 +55,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,23 +62,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: cpina/github-action-push-to-another-repository@v1.5
|
||||
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
|
||||
commit-message: Add apks from $GITHUB_REF
|
||||
target-directory: fdroid/tmp-apks
|
||||
@@ -1,9 +0,0 @@
|
||||
[submodule "app/src/main/cpp/libarchive"]
|
||||
path = app/src/main/cpp/libarchive
|
||||
url = https://github.com/Ehviewer-Overhauled/libarchive
|
||||
[submodule "app/src/main/cpp/liblzma"]
|
||||
path = app/src/main/cpp/liblzma
|
||||
url = https://github.com/Ehviewer-Overhauled/xz
|
||||
[submodule "app/src/main/cpp/nettle/nettle"]
|
||||
path = app/src/main/cpp/nettle/nettle
|
||||
url = https://github.com/Ehviewer-Overhauled/nettle
|
||||
@@ -1,72 +1,81 @@
|
||||
# Maintenance Mode
|
||||
|
||||
The project is in maintenance mode, will only receive bugfixes.
|
||||
|
||||
<p align="right">
|
||||
<strong>English<strong>
|
||||
<strong>English</strong>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-cn.md">
|
||||
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/docs/README/zh-cn.md">
|
||||
简体中文
|
||||
</a>
|
||||
<span> | <span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/docs/README/zh-tw.md">
|
||||
<span> | </span>
|
||||
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/docs/README/zh-tw.md">
|
||||
正體中文
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">
|
||||
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200">
|
||||
<img src="https://github.com/Ehviewer-Overhauled/Art/blob/master/launcher_icon-web.svg" width="200" alt="EhViewer">
|
||||
<br>EhViewer<br>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml">
|
||||
<img src="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
|
||||
<a href="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml">
|
||||
<img src="https://github.com/FooIbar/EhViewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
|
||||
</a>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/1.8.x.x/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/Ehviewer-Overhauled/Ehviewer" alt="LICENSE">
|
||||
<a href="https://github.com/FooIbar/EhViewer/blob/1.8.x.x/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/FooIbar/EhViewer" alt="LICENSE">
|
||||
</a>
|
||||
<a href="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer">
|
||||
<img src="https://www.codefactor.io/repository/github/Ehviewer-Overhauled/ehviewer/badge" alt="CodeFactor">
|
||||
<a href="https://www.codefactor.io/repository/github/FooIbar/EhViewer">
|
||||
<img src="https://www.codefactor.io/repository/github/FooIbar/EhViewer/badge" alt="CodeFactor">
|
||||
</a>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases">
|
||||
<img src="https://img.shields.io/github/v/release/Ehviewer-Overhauled/ehviewer?include_prereleases" alt="Release">
|
||||
<a href="https://github.com/FooIbar/EhViewer/releases">
|
||||
<img src="https://img.shields.io/github/downloads/FooIbar/EhViewer/total.svg" alt="Downloads">
|
||||
</a>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/issues">
|
||||
<img src="https://img.shields.io/github/issues/Ehviewer-Overhauled/ehviewer" alt="Issues">
|
||||
<a href="https://github.com/FooIbar/EhViewer/releases">
|
||||
<img src="https://img.shields.io/github/v/release/FooIbar/EhViewer" alt="Release">
|
||||
</a>
|
||||
<a href="https://github.com/FooIbar/EhViewer/issues">
|
||||
<img src="https://img.shields.io/github/issues/FooIbar/EhViewer" alt="Issues">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h3>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#description">
|
||||
<a href="https://github.com/FooIbar/EhViewer#description">
|
||||
Description
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#download">
|
||||
<a href="https://github.com/FooIbar/EhViewer#download">
|
||||
Download
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#screenshot">
|
||||
<a href="https://github.com/FooIbar/EhViewer#screenshot">
|
||||
Screenshot
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#thanks">
|
||||
<a href="https://github.com/FooIbar/EhViewer#thanks">
|
||||
Thanks
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer#license">
|
||||
<a href="https://github.com/FooIbar/EhViewer#license">
|
||||
License
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
|
||||
# Description
|
||||
|
||||
EhViewer fork dedicated to lightweight and high-performance
|
||||
|
||||
with Material Design 3 and Dynamic Color Support
|
||||
with [Material Design 3](https://m3.material.io/)
|
||||
and [Dynamic Color](https://m3.material.io/styles/color/dynamic-color/overview) Support
|
||||
|
||||
# Download
|
||||
|
||||
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases"><img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/></a> <a href="https://github.com/Ehviewer-Overhauled/fdroid-repo"><img alt="Git it on F-Droid" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-fdroid.svg" width="200px"/></a>
|
||||
<a href="https://github.com/FooIbar/EhViewer/releases">
|
||||
<img alt="Get it on GitHub" src="https://github.com/Ehviewer-Overhauled/Art/blob/master/get-it-on-github.svg" width="200px"/>
|
||||
</a>
|
||||
|
||||
# Screenshot
|
||||
|
||||
@@ -77,24 +86,26 @@ with Material Design 3 and Dynamic Color Support
|
||||
|
||||
Here is the libraries
|
||||
|
||||
- [Arrow](https://arrow-kt.io/)
|
||||
- [AOSP & AndroidX](http://source.android.com/)
|
||||
- [Coil](https://coil-kt.github.io/coil/)
|
||||
- [Kotlin & KotlinX](https://kotlinlang.org/)
|
||||
- [FullDraggableDrawer](https://github.com/PureWriter/FullDraggableDrawer)
|
||||
- [GnuTLS](https://gnutls.org/)
|
||||
- [material-components-android](https://github.com/material-components/material-components-android)
|
||||
- [MCA](https://github.com/material-components/material-components-android)
|
||||
- [material-design-icons](https://github.com/google/material-design-icons)
|
||||
- [jsoup](https://github.com/jhy/jsoup)
|
||||
- [okhttp](https://github.com/square/okhttp)
|
||||
- [Okhttp](https://square.github.io/okhttp/)
|
||||
- [RikkaX](https://github.com/RikkaApps/RikkaX)
|
||||
- [libarchive](http://www.libarchive.org/)
|
||||
- [XZ Utils](https://tukaani.org/xz/)
|
||||
- [Libarchive](http://www.libarchive.org/)
|
||||
- [Coil](https://coil-kt.github.io/coil/)
|
||||
- [Jsoup](https://jsoup.org/)
|
||||
|
||||
Specially thanks [Tachiyomi](https://tachiyomi.org/) for its reader implementation!
|
||||
|
||||
# License
|
||||
|
||||
Copyright 2014-2019 Hippo Seven
|
||||
Copyright 2020-2022 NekoInverter
|
||||
Copyright 2022-2023 Tarsin Norbin
|
||||
Copyright 2023 Foolbar
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-parcelize'
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
buildToolsVersion "33.0.1"
|
||||
ndkVersion "25.1.8937393"
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable = true
|
||||
reset()
|
||||
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
|
||||
universalApk = true
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
sign {
|
||||
storeFile file(signStoreFile)
|
||||
storePassword signStorePassword
|
||||
keyAlias signkeyAlias
|
||||
keyPassword signkeyPassword
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "moe.tarsin.ehviewer"
|
||||
minSdkVersion 28
|
||||
targetSdkVersion 33
|
||||
versionCode 180030
|
||||
versionName "1.8.5.3-alpha03"
|
||||
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path 'src/main/cpp/CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_19
|
||||
targetCompatibility JavaVersion.VERSION_19
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '19'
|
||||
freeCompilerArgs += [
|
||||
// https://kotlinlang.org/docs/compiler-reference.html#progressive
|
||||
"-progressive",
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=true"
|
||||
]
|
||||
}
|
||||
|
||||
lint {
|
||||
abortOnError true
|
||||
checkReleaseBuilds false
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += "/META-INF/**"
|
||||
excludes += "/kotlin/**"
|
||||
excludes += "**.txt"
|
||||
excludes += "**.bin"
|
||||
}
|
||||
}
|
||||
|
||||
dependenciesInfo.includeInApk false
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.sign
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
compose true
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.0-alpha02"
|
||||
}
|
||||
|
||||
namespace 'com.hippo.ehviewer'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def activity_version = "1.6.1"
|
||||
implementation "androidx.activity:activity-ktx:$activity_version"
|
||||
|
||||
implementation "androidx.annotation:annotation:1.5.0"
|
||||
implementation "androidx.annotation:annotation-experimental:1.3.0"
|
||||
implementation "androidx.appcompat:appcompat:1.7.0-alpha01"
|
||||
|
||||
def arch_core_version = "2.1.0"
|
||||
implementation "androidx.arch.core:core-common:$arch_core_version"
|
||||
implementation "androidx.arch.core:core-runtime:$arch_core_version"
|
||||
|
||||
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
|
||||
implementation "androidx.browser:browser:1.4.0"
|
||||
|
||||
def collection_version = "1.3.0-alpha02"
|
||||
implementation "androidx.collection:collection:$collection_version"
|
||||
implementation "androidx.collection:collection-ktx:$collection_version"
|
||||
|
||||
implementation platform("androidx.compose:compose-bom:2022.12.00")
|
||||
implementation "androidx.compose.animation:animation"
|
||||
implementation "androidx.compose.foundation:foundation"
|
||||
implementation "androidx.compose.foundation:foundation-layout"
|
||||
implementation "androidx.compose.material3:material3"
|
||||
implementation "androidx.compose.runtime:runtime"
|
||||
implementation "androidx.compose.runtime:runtime-saveable"
|
||||
implementation "androidx.compose.ui:ui"
|
||||
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha05"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
|
||||
def core_version = "1.9.0"
|
||||
implementation "androidx.core:core-ktx:$core_version"
|
||||
|
||||
implementation "androidx.customview:customview:1.2.0-alpha02"
|
||||
implementation "androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03"
|
||||
|
||||
def fragment_version = "1.6.0-alpha04"
|
||||
implementation "androidx.fragment:fragment-ktx:$fragment_version"
|
||||
|
||||
def lifecycle_version = "2.6.0-alpha03"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
|
||||
|
||||
implementation "androidx.loader:loader:1.1.0"
|
||||
|
||||
def nav_version = "2.5.3"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||
|
||||
implementation "androidx.paging:paging-runtime-ktx:3.2.0-alpha03"
|
||||
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0-rc01'
|
||||
|
||||
def room_version = "2.5.0-rc01"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
ksp "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-paging:$room_version"
|
||||
|
||||
implementation 'androidx.startup:startup-runtime:1.2.0-alpha01'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
||||
implementation "androidx.transition:transition-ktx:1.4.1"
|
||||
implementation "androidx.vectordrawable:vectordrawable:1.2.0-beta01"
|
||||
implementation "androidx.vectordrawable:vectordrawable-animated:1.1.0"
|
||||
implementation 'androidx.viewpager:viewpager:1.1.0-alpha01'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||
|
||||
implementation 'com.drakeet.drawer:drawer:1.0.3'
|
||||
implementation 'com.google.android.material:material:1.8.0-rc01'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11'
|
||||
implementation 'com.squareup.okhttp3:okhttp'
|
||||
implementation 'com.squareup.okhttp3:okhttp-coroutines'
|
||||
|
||||
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.2.0'
|
||||
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'
|
||||
implementation 'dev.rikka.rikkax.material:material-preference:2.0.0'
|
||||
implementation 'io.coil-kt:coil:2.2.2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||
implementation 'org.jsoup:jsoup:1.15.3'
|
||||
|
||||
// Tachiyomi Reader
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
implementation 'dev.chrisbanes.insetter:insetter:0.6.1'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'dev.rikka.rikkax.appcompat', module: 'appcompat'
|
||||
exclude group: 'dev.rikka.rikkax.material', module: 'material'
|
||||
exclude group: 'androidx.compose.material'
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-android-extensions-runtime'
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk7'
|
||||
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
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.diffplug.spotless")
|
||||
id("com.mikepenz.aboutlibraries.plugin")
|
||||
id("org.mozilla.rust-android-gradle.rust-android")
|
||||
id("dev.shreyaspatil.compose-compiler-report-generator")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 34
|
||||
buildToolsVersion = "34.0.0"
|
||||
ndkVersion = "26.0.10792818"
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
reset()
|
||||
if (isRelease) {
|
||||
include("arm64-v8a", "x86_64", "armeabi-v7a", "x86")
|
||||
isUniversalApk = true
|
||||
} else {
|
||||
include("arm64-v8a")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val signConfig = signingConfigs.create("release") {
|
||||
storeFile = File(projectDir.path + "/keystore/androidkey.jks")
|
||||
storePassword = "000000"
|
||||
keyAlias = "key0"
|
||||
keyPassword = "000000"
|
||||
enableV3Signing = true
|
||||
enableV4Signing = true
|
||||
}
|
||||
|
||||
val branch = providers.exec {
|
||||
commandLine = "git branch --show-current".split(' ')
|
||||
}.standardOutput.asText.get().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 = 34
|
||||
versionCode = 180043
|
||||
versionName = "1.8.9.7"
|
||||
versionNameSuffix = "-gms"
|
||||
resourceConfigurations.addAll(
|
||||
listOf(
|
||||
"zh",
|
||||
"zh-rCN",
|
||||
"zh-rHK",
|
||||
"zh-rTW",
|
||||
"es",
|
||||
"ja",
|
||||
"ko",
|
||||
"fr",
|
||||
"de",
|
||||
"th",
|
||||
"tr",
|
||||
"nb-rNO",
|
||||
),
|
||||
)
|
||||
buildConfigField("String", "BRANCH", "\"$branch\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"$commitSha\"")
|
||||
buildConfigField("String", "REPO_NAME", "\"$repoName\"")
|
||||
buildConfigField("String", "VERSION_NAME_SUFFIX", "\"$versionNameSuffix\"")
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = File("src/main/cpp/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf(
|
||||
// https://kotlinlang.org/docs/compiler-reference.html#progressive
|
||||
"-progressive",
|
||||
"-Xjvm-default=all",
|
||||
"-Xlambdas=indy",
|
||||
"-XXLanguage:+BreakContinueInInlineLambdas",
|
||||
|
||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
|
||||
"-opt-in=androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi",
|
||||
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
|
||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||
"-opt-in=androidx.paging.ExperimentalPagingApi",
|
||||
"-opt-in=kotlin.contracts.ExperimentalContracts",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
"-opt-in=splitties.experimental.ExperimentalSplittiesApi",
|
||||
"-opt-in=splitties.preferences.DataStorePreferencesPreview",
|
||||
)
|
||||
}
|
||||
|
||||
lint {
|
||||
abortOnError = true
|
||||
checkReleaseBuilds = false
|
||||
disable.add("MissingTranslation")
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/**"
|
||||
excludes += "/kotlin/**"
|
||||
excludes += "**.txt"
|
||||
excludes += "**.bin"
|
||||
}
|
||||
}
|
||||
|
||||
dependenciesInfo.includeInApk = false
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles("proguard-rules.pro")
|
||||
signingConfig = signConfig
|
||||
buildConfigField("String", "BUILD_TIME", "\"$buildTime\"")
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix = ".debug"
|
||||
buildConfigField("String", "BUILD_TIME", "\"\"")
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
compose = true
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.3"
|
||||
}
|
||||
|
||||
namespace = "com.hippo.ehviewer"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://developer.android.com/jetpack/androidx/releases/activity
|
||||
implementation("androidx.activity:activity-compose:1.8.0-rc01")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")
|
||||
implementation("androidx.browser:browser:1.6.0")
|
||||
implementation("androidx.collection:collection-ktx:1.3.0-rc01")
|
||||
|
||||
// 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.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.12.0")
|
||||
|
||||
implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha12")
|
||||
implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.7.0-alpha05")
|
||||
// https://developer.android.com/jetpack/androidx/releases/lifecycle
|
||||
implementation("androidx.lifecycle:lifecycle-process:2.6.2")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/navigation
|
||||
val navigation = "2.7.3"
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:$navigation")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:$navigation")
|
||||
implementation("androidx.navigation:navigation-compose:$navigation")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/paging
|
||||
val paging = "3.2.1"
|
||||
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 = "2.6.0-rc01"
|
||||
ksp("androidx.room:room-compiler:$room")
|
||||
implementation("androidx.room:room-paging:$room")
|
||||
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01")
|
||||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||
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
|
||||
// https://github.com/google/accompanist/releases
|
||||
val accompanist = "0.32.0"
|
||||
implementation("com.google.accompanist:accompanist-themeadapter-material3:$accompanist")
|
||||
implementation("com.google.accompanist:accompanist-webview:$accompanist")
|
||||
implementation("com.google.android.material:material:1.11.0-alpha03")
|
||||
|
||||
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.5.0")
|
||||
|
||||
implementation("com.mikepenz:aboutlibraries-core:10.9.1")
|
||||
|
||||
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(platform("io.arrow-kt:arrow-stack:1.2.1"))
|
||||
implementation("io.arrow-kt:arrow-fx-coroutines")
|
||||
|
||||
// https://coil-kt.github.io/coil/changelog/
|
||||
implementation(platform("io.coil-kt:coil-bom:2.4.0"))
|
||||
implementation("io.coil-kt:coil-compose")
|
||||
implementation("io.coil-kt:coil-gif")
|
||||
|
||||
val ktor = "2.3.4"
|
||||
implementation("io.ktor:ktor-io-jvm:$ktor")
|
||||
implementation("io.ktor:ktor-utils-jvm:$ktor")
|
||||
|
||||
implementation("org.chromium.net:cronet-api:113.5672.61")
|
||||
implementation("com.google.android.gms:play-services-cronet:18.0.1")
|
||||
|
||||
val serialization = "1.6.0"
|
||||
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"
|
||||
}
|
||||
|
||||
spotless {
|
||||
kotlin {
|
||||
ktlint()
|
||||
}
|
||||
kotlinGradle {
|
||||
ktlint().editorConfigOverride(
|
||||
mapOf(
|
||||
"ktlint_standard_multiline-expression-wrapping" to "disabled",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.configureEach {
|
||||
if ((name == "mergeDebugJniLibFolders" || name == "mergeReleaseJniLibFolders")) {
|
||||
dependsOn("cargoBuild")
|
||||
// fix mergeDebugJniLibFolders UP-TO-DATE
|
||||
inputs.dir(layout.buildDirectory.dir("rustJniLibs/android"))
|
||||
}
|
||||
}
|
||||
Vendored
+14
@@ -6,8 +6,22 @@
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# https://issuetracker.google.com/issues/190382641
|
||||
-keepclassmembers class kotlin.SafePublicationLazyImpl {
|
||||
java.lang.Object _value;
|
||||
}
|
||||
|
||||
-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 { *; }
|
||||
-keep class com.hippo.ehviewer.ui.scene.SearchBarScene { getBinding(); }
|
||||
|
||||
# Ktor logger
|
||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
||||
|
||||
-keepattributes LineNumberTable,SourceFile
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "c801ee62729ba20a203b150211234d5c",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "OK_HTTP_3_COOKIE",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`NAME` TEXT, `VALUE` TEXT, `EXPIRES_AT` INTEGER NOT NULL, `DOMAIN` TEXT, `PATH` TEXT, `SECURE` INTEGER NOT NULL, `HTTP_ONLY` INTEGER NOT NULL, `PERSISTENT` INTEGER NOT NULL, `HOST_ONLY` INTEGER NOT NULL, `_id` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "VALUE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "expiresAt",
|
||||
"columnName": "EXPIRES_AT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "domain",
|
||||
"columnName": "DOMAIN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "PATH",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "secure",
|
||||
"columnName": "SECURE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpOnly",
|
||||
"columnName": "HTTP_ONLY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "persistent",
|
||||
"columnName": "PERSISTENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hostOnly",
|
||||
"columnName": "HOST_ONLY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"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, 'c801ee62729ba20a203b150211234d5c')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "1b80cd29939b0f43934721f1289ba94d",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "OK_HTTP_3_COOKIE",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`NAME` TEXT NOT NULL, `VALUE` TEXT NOT NULL, `EXPIRES_AT` INTEGER NOT NULL, `DOMAIN` TEXT NOT NULL, `PATH` TEXT NOT NULL, `SECURE` INTEGER NOT NULL, `HTTP_ONLY` INTEGER NOT NULL, `PERSISTENT` INTEGER NOT NULL, `HOST_ONLY` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "VALUE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "expiresAt",
|
||||
"columnName": "EXPIRES_AT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "domain",
|
||||
"columnName": "DOMAIN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "path",
|
||||
"columnName": "PATH",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "secure",
|
||||
"columnName": "SECURE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "httpOnly",
|
||||
"columnName": "HTTP_ONLY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "persistent",
|
||||
"columnName": "PERSISTENT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "hostOnly",
|
||||
"columnName": "HOST_ONLY",
|
||||
"affinity": "INTEGER",
|
||||
"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, '1b80cd29939b0f43934721f1289ba94d')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -14,5 +15,8 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:factor="2.0" />
|
||||
<resources>
|
||||
|
||||
<string name="app_name" translatable="false">EhViewer Debug</string>
|
||||
|
||||
</resources>
|
||||
@@ -1,19 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
@@ -27,44 +12,47 @@
|
||||
<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" />
|
||||
|
||||
<application
|
||||
android:name="com.hippo.ehviewer.EhApplication"
|
||||
android:name=".EhApplication"
|
||||
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:supportsRtl="true"
|
||||
android:label="@string/app_name"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
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="com.hippo.ehviewer.ui.MainActivity"
|
||||
android:name=".ui.ConfigureActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:banner="@mipmap/ic_leanback_banner"
|
||||
android:configChanges="screenSize"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -82,29 +70,27 @@
|
||||
<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" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:label="@string/image_search">
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="eu.kanade.tachiyomi.ui.reader.ReaderActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
@@ -138,25 +124,18 @@
|
||||
<data android:mimeType="application/x-xz-compressed-tar" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="com.hippo.ehviewer.ui.SettingsActivity"
|
||||
android:name=".ui.SecurityActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
android:label="@string/settings" />
|
||||
<activity
|
||||
android:name="com.hippo.ehviewer.ui.SecurityActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:label="@string/settings"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
|
||||
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"
|
||||
@@ -166,9 +145,8 @@
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.hippo.ehviewer.download.DownloadService"
|
||||
android:name=".download.DownloadService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:label="@string/download_service_label" />
|
||||
|
||||
@@ -177,11 +155,10 @@
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
</manifest>
|
||||
@@ -1,7 +1,9 @@
|
||||
project(ehviewer)
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(ehviewer C ASM)
|
||||
include(FetchContent)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -funroll-loops -flto=thin \
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
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 \
|
||||
@@ -12,8 +14,30 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Ofast -fvisibility=hidden -fvisibility-inli
|
||||
-mllvm -polly-vectorizer=stripmine \
|
||||
-mllvm -polly-detect-profitability-min-per-loop-insts=40 \
|
||||
-mllvm -polly-invariant-load-hoisting")
|
||||
endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
# Change the default path of fetch content to avoid downloading the same dependencies per architecture
|
||||
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://github.com/FooIbar/xz.git
|
||||
GIT_TAG v5.4.4
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(liblzma)
|
||||
include_directories(${liblzma_SOURCE_DIR}/src/liblzma/api)
|
||||
|
||||
# Build GNUTLS libnettle
|
||||
FetchContent_Declare(nettle
|
||||
URL https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz
|
||||
URL_MD5 29fcd2dec6bf5b48e5e3ffb3cbc4779e
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/nettle/nettle
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(nettle)
|
||||
add_subdirectory(nettle)
|
||||
|
||||
# Configure libnettle support for libarchive
|
||||
@@ -27,24 +51,30 @@ set(HAVE_NETTLE_PBKDF2_H 1)
|
||||
set(HAVE_NETTLE_RIPEMD160_H 1)
|
||||
set(HAVE_NETTLE_SHA_H 1)
|
||||
|
||||
# Build liblzma
|
||||
add_subdirectory(liblzma)
|
||||
|
||||
# Configure lzma support for libarchive
|
||||
include_directories(liblzma/src/liblzma/api)
|
||||
SET(HAVE_LIBLZMA 1)
|
||||
SET(HAVE_LZMA_H 1)
|
||||
SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
|
||||
SET(HAVE_LZMADEC_H 1)
|
||||
SET(HAVE_LIBLZMADEC 1)
|
||||
|
||||
# Configure libarchive link's static lib
|
||||
SET(LIBARCHIVE_CUSTOM_LIBS "liblzma" "nettle")
|
||||
option(ENABLE_TAR OFF)
|
||||
option(ENABLE_CPIO OFF)
|
||||
option(ENABLE_CAT OFF)
|
||||
option(ENABLE_UNZIP OFF)
|
||||
option(ENABLE_TEST OFF)
|
||||
|
||||
# Build libarchive
|
||||
add_subdirectory(libarchive)
|
||||
# Configure libarchive link's static lib
|
||||
SET(LIBARCHIVE_CUSTOM_LIBS "nettle" "liblzma")
|
||||
|
||||
FetchContent_Declare(libarchive
|
||||
GIT_REPOSITORY https://github.com/FooIbar/libarchive.git
|
||||
GIT_TAG v3.7.2
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(libarchive)
|
||||
include_directories(${libarchive_SOURCE_DIR}/libarchive)
|
||||
|
||||
# Build and link our app's native lib
|
||||
add_library(${PROJECT_NAME} SHARED archive.c fdutils.c)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC libarchive/libarchive)
|
||||
target_link_libraries(${PROJECT_NAME} archive_static log -Wl,--gc-sections)
|
||||
add_library(${PROJECT_NAME} SHARED archive.c gifutils.c natsort/strnatcmp.c)
|
||||
target_link_libraries(${PROJECT_NAME} archive_static log)
|
||||
+122
-55
@@ -17,14 +17,12 @@
|
||||
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
@@ -34,7 +32,9 @@
|
||||
|
||||
#define LOG_TAG "libarchive_wrapper"
|
||||
|
||||
#include "natsort/strnatcmp.h"
|
||||
#include "ehviewer.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
typedef struct {
|
||||
int using;
|
||||
@@ -46,12 +46,25 @@ typedef struct {
|
||||
typedef struct {
|
||||
const char *filename;
|
||||
int index;
|
||||
size_t size;
|
||||
} entry;
|
||||
|
||||
pthread_mutex_t ctx_lock;
|
||||
_Atomic mcs_lock ctx_lock;
|
||||
static archive_ctx **ctx_pool;
|
||||
#define CTX_POOL_SIZE 20
|
||||
|
||||
static void *mempool = NULL;
|
||||
static size_t *mempoolofs = NULL;
|
||||
|
||||
#define PAGE_ALIGN(x) ((x + ~PAGE_MASK) & PAGE_MASK)
|
||||
|
||||
#define MEMPOOL_ADDR_BY_SORTED_IDX(x) (mempool + (index ? mempoolofs[index - 1] : 0))
|
||||
#define MEMPOOL_SIZE (mempoolofs[entryCount - 1])
|
||||
#define MEMPOOL_ENTITY_SIZE(x) PAGE_ALIGN(entries[x].size)
|
||||
|
||||
#define PROT_RW (PROT_WRITE | PROT_READ)
|
||||
#define MAP_ANON_POOL (MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_UNINITIALIZED)
|
||||
|
||||
static bool need_encrypt = false;
|
||||
static char *passwd = NULL;
|
||||
static void *archiveAddr = NULL;
|
||||
@@ -59,9 +72,7 @@ static size_t archiveSize = 0;
|
||||
static entry *entries = NULL;
|
||||
static size_t entryCount = 0;
|
||||
|
||||
#define POPULATE_THRESHOLD (512 * 1024 * 1024) // 512 MiB
|
||||
|
||||
const char supportExt[9][6] = {
|
||||
const char supportExt[10][6] = {
|
||||
"jpeg",
|
||||
"jpg",
|
||||
"png",
|
||||
@@ -70,7 +81,8 @@ const char supportExt[9][6] = {
|
||||
"bmp",
|
||||
"ico",
|
||||
"wbmp",
|
||||
"heif"
|
||||
"heif",
|
||||
"avif"
|
||||
};
|
||||
|
||||
static inline int filename_is_playable_file(const char *name) {
|
||||
@@ -78,7 +90,7 @@ static inline int filename_is_playable_file(const char *name) {
|
||||
if (!dotptr++)
|
||||
return false;
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
for (i = 0; i < 10; i++)
|
||||
if (strcmp(dotptr, supportExt[i]) == 0)
|
||||
return true;
|
||||
return false;
|
||||
@@ -87,9 +99,7 @@ static inline int filename_is_playable_file(const char *name) {
|
||||
static inline int compare_entries(const void *a, const void *b) {
|
||||
const char *fa = ((entry *) a)->filename;
|
||||
const char *fb = ((entry *) b)->filename;
|
||||
int a_len = (int) strlen(fa);
|
||||
int b_len = (int) strlen(fb);
|
||||
return a_len == b_len ? strcmp(fa, fb) : a_len - b_len;
|
||||
return strnatcmp(fa, fb);
|
||||
}
|
||||
|
||||
static long archive_map_entries_index(archive_ctx *ctx) {
|
||||
@@ -99,6 +109,7 @@ static long archive_map_entries_index(archive_ctx *ctx) {
|
||||
if (filename_is_playable_file(name)) {
|
||||
entries[count].filename = strdup(name);
|
||||
entries[count].index = count;
|
||||
entries[count].size = archive_entry_size(ctx->entry);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -108,6 +119,48 @@ static long archive_map_entries_index(archive_ctx *ctx) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool archive_prealloc_mempool() {
|
||||
mempoolofs = calloc(entryCount, sizeof(size_t));
|
||||
for (int i = 0; i < entryCount; ++i) {
|
||||
if (!i)
|
||||
mempoolofs[i] = PAGE_ALIGN(entries[i].size);
|
||||
else
|
||||
mempoolofs[i] = PAGE_ALIGN(entries[i].size) + mempoolofs[i - 1];
|
||||
}
|
||||
mempool = mmap(0, MEMPOOL_SIZE, PROT_RW, MAP_ANON_POOL, -1, 0);
|
||||
if (mempool == MAP_FAILED) {
|
||||
LOGE("%s%s", "mmap failed with error ", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Mincore does not support anon mapping yet
|
||||
// Now that we use MADV_FREE, how can we detect whether pages is reclaimed?
|
||||
static bool is_mempool_pages_present_and_lock(int index) {
|
||||
void *addr = MEMPOOL_ADDR_BY_SORTED_IDX(index);
|
||||
size_t size = MEMPOOL_ENTITY_SIZE(index);
|
||||
mlock(addr, size);
|
||||
#if 0
|
||||
unsigned char vec[size / PAGE_SIZE];
|
||||
unsigned char r = 1;
|
||||
mincore(addr, size, vec);
|
||||
for (int i = 0; i < size / PAGE_SIZE; ++i) r &= vec[i];
|
||||
return r;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#define MEMPOOL_IN_CORE_AND_LOCK(x) is_mempool_pages_present_and_lock(x)
|
||||
|
||||
static void mempool_release_pages(void *addr, size_t size) {
|
||||
size = PAGE_ALIGN(size);
|
||||
munlock(addr, size);
|
||||
int ret = madvise(addr, size, MADV_FREE);
|
||||
if (ret == -EINVAL) madvise(addr, size, MADV_DONTNEED);
|
||||
}
|
||||
|
||||
static long archive_list_all_entries(archive_ctx *ctx) {
|
||||
long count = 0;
|
||||
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK)
|
||||
@@ -158,7 +211,6 @@ static int archive_alloc_ctx(archive_ctx **ctxptr) {
|
||||
}
|
||||
|
||||
static int archive_skip_to_index(archive_ctx *ctx, int index) {
|
||||
assert(!(ctx->next_index > index));
|
||||
while (archive_read_next_header(ctx->arc, &ctx->entry) == ARCHIVE_OK) {
|
||||
if (!filename_is_playable_file(archive_entry_pathname(ctx->entry)))
|
||||
continue;
|
||||
@@ -172,7 +224,8 @@ static int archive_skip_to_index(archive_ctx *ctx, int index) {
|
||||
static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
|
||||
int ret;
|
||||
archive_ctx *ctx = NULL;
|
||||
pthread_mutex_lock(&ctx_lock);
|
||||
mcs_lock_t local_lock;
|
||||
lock_mcs(&ctx_lock, &local_lock);
|
||||
for (int i = 0; i < CTX_POOL_SIZE; i++) {
|
||||
if (!ctx_pool[i])
|
||||
continue;
|
||||
@@ -187,7 +240,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
|
||||
}
|
||||
if (ctx)
|
||||
ctx->using = 1;
|
||||
pthread_mutex_unlock(&ctx_lock);
|
||||
unlock_mcs(&ctx_lock, &local_lock);
|
||||
|
||||
if (!ctx) {
|
||||
archive_ctx *victimCtx = NULL;
|
||||
@@ -196,7 +249,7 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
|
||||
ret = archive_alloc_ctx(&ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
pthread_mutex_lock(&ctx_lock);
|
||||
lock_mcs(&ctx_lock, &local_lock);
|
||||
for (int i = 0; i < CTX_POOL_SIZE; i++) {
|
||||
if (!ctx_pool[i]) {
|
||||
ctx_pool[i] = ctx;
|
||||
@@ -210,11 +263,9 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
|
||||
victimIdx = i;
|
||||
}
|
||||
}
|
||||
if (replace) {
|
||||
archive_release_ctx(victimCtx);
|
||||
ctx_pool[victimIdx] = ctx;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx_lock);
|
||||
if (replace) ctx_pool[victimIdx] = ctx;
|
||||
unlock_mcs(&ctx_lock, &local_lock);
|
||||
if (replace) archive_release_ctx(victimCtx);
|
||||
}
|
||||
ret = archive_skip_to_index(ctx, idx);
|
||||
if (ret != idx) {
|
||||
@@ -228,13 +279,12 @@ static int archive_get_ctx(archive_ctx **ctxptr, int idx) {
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd, jlong size) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_openArchive(JNIEnv *env, jclass thiz, jint fd,
|
||||
jlong size) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
archive_ctx *ctx = NULL;
|
||||
int mmap_flags = MAP_PRIVATE;
|
||||
if (size <= POPULATE_THRESHOLD)
|
||||
mmap_flags |= MAP_POPULATE;
|
||||
archiveAddr = mmap64(0, size, PROT_READ, mmap_flags, fd, 0);
|
||||
if (archiveAddr == MAP_FAILED) {
|
||||
LOGE("%s%s", "mmap64 failed with error ", strerror(errno));
|
||||
@@ -242,7 +292,6 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
|
||||
}
|
||||
archiveSize = size;
|
||||
madvise_log_if_error(archiveAddr, archiveSize, MADV_WILLNEED);
|
||||
pthread_mutex_init(&ctx_lock, 0);
|
||||
ctx_pool = calloc(CTX_POOL_SIZE, sizeof(archive_ctx **));
|
||||
if (!ctx_pool) {
|
||||
LOGE("Allocate archive ctx pool failed:ENOMEM");
|
||||
@@ -255,7 +304,7 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
|
||||
LOGE("%s%s", "Archive open failed:", archive_error_string(ctx->arc));
|
||||
} else {
|
||||
entryCount = archive_list_all_entries(ctx);
|
||||
LOGI("%s%ld%s", "Found ", r, " image entries in archive");
|
||||
LOGI("%s%zu%s", "Found ", entryCount, " image entries in archive");
|
||||
|
||||
// We must read through the file|vm then we can know whether it is encrypted
|
||||
int encryptRet = archive_read_has_encrypted_entries(ctx->arc);
|
||||
@@ -292,53 +341,66 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd
|
||||
} else {
|
||||
entries = calloc(entryCount, sizeof(entry));
|
||||
r = archive_map_entries_index(ctx);
|
||||
if (!archive_prealloc_mempool()) {
|
||||
r = 0;
|
||||
for (int i = 0; i < entryCount; ++i) {
|
||||
free((void *) entries[i].filename);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
}
|
||||
|
||||
archive_release_ctx(ctx);
|
||||
return r;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "ConstantConditionsOC"
|
||||
#pragma ide diagnostic ignored "UnreachableCode"
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_extractToByteBuffer(JNIEnv *env, jobject thiz, jint index) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToByteBuffer(JNIEnv *env, jclass thiz,
|
||||
jint index) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
index = entries[index].index;
|
||||
archive_ctx *ctx = NULL;
|
||||
int ret;
|
||||
ret = archive_get_ctx(&ctx, index);
|
||||
if (ret)
|
||||
return 0;
|
||||
long long size = archive_entry_size(ctx->entry);
|
||||
void *buffer = malloc(size);
|
||||
if (!buffer) {
|
||||
void *addr = MEMPOOL_ADDR_BY_SORTED_IDX(index);
|
||||
size_t size = entries[index].size;
|
||||
if (!MEMPOOL_IN_CORE_AND_LOCK(index)) {
|
||||
index = entries[index].index;
|
||||
archive_ctx *ctx = NULL;
|
||||
int ret;
|
||||
ret = archive_get_ctx(&ctx, index);
|
||||
if (ret) return 0;
|
||||
ret = archive_read_data(ctx->arc, addr, size);
|
||||
ctx->using = 0;
|
||||
LOGE("Allocate buffer for decompression failed:ENOMEM");
|
||||
return 0;
|
||||
if (ret == size)
|
||||
return (*env)->NewDirectByteBuffer(env, addr, size);
|
||||
if (ret != size)
|
||||
LOGE("%s", "No enough data read, WTF?");
|
||||
if (ret < 0)
|
||||
LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc));
|
||||
mempool_release_pages(addr, size);
|
||||
} else {
|
||||
return (*env)->NewDirectByteBuffer(env, addr, size);
|
||||
}
|
||||
ret = archive_read_data(ctx->arc, buffer, size);
|
||||
ctx->using = 0;
|
||||
if (ret == size)
|
||||
return (*env)->NewDirectByteBuffer(env, buffer, size);
|
||||
if (ret != size)
|
||||
LOGE("%s", "No enough data read, WTF?");
|
||||
if (ret < 0)
|
||||
LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc));
|
||||
free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_closeArchive(JNIEnv *env, jclass thiz) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
for (int i = 0; i < CTX_POOL_SIZE; i++)
|
||||
archive_release_ctx(ctx_pool[i]);
|
||||
free(passwd);
|
||||
free(ctx_pool);
|
||||
pthread_mutex_destroy(&ctx_lock);
|
||||
passwd = NULL;
|
||||
need_encrypt = false;
|
||||
munmap(archiveAddr, archiveSize);
|
||||
munmap(mempool, MEMPOOL_SIZE);
|
||||
free(mempoolofs);
|
||||
for (int i = 0; i < entryCount; ++i) {
|
||||
free((void *) entries[i].filename);
|
||||
}
|
||||
@@ -346,14 +408,15 @@ Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *env, jobject thiz) {
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *env, jobject thiz) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_needPassword(JNIEnv *env, jclass thiz) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
return need_encrypt;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jstring str) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_providePassword(JNIEnv *env, jclass thiz,
|
||||
jstring str) {
|
||||
EH_UNUSED(thiz);
|
||||
struct archive_entry *entry;
|
||||
archive_ctx *ctx;
|
||||
@@ -383,7 +446,8 @@ Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *env, jobject thiz, jst
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint index) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_getFilename(JNIEnv *env, jclass thiz,
|
||||
jint index) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
index = entries[index].index;
|
||||
@@ -398,7 +462,8 @@ Java_com_hippo_UriArchiveAccessor_getFilename(JNIEnv *env, jobject thiz, jint in
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint index, jint fd) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_extractToFd(JNIEnv *env, jclass thiz,
|
||||
jint index, jint fd) {
|
||||
EH_UNUSED(env);
|
||||
EH_UNUSED(thiz);
|
||||
index = entries[index].index;
|
||||
@@ -412,8 +477,10 @@ Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint in
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_hippo_UriArchiveAccessor_releaseByteBuffer(JNIEnv *env, jobject thiz, jobject buffer) {
|
||||
Java_com_hippo_ehviewer_gallery_ArchivePageLoaderKt_releaseByteBuffer(JNIEnv *env, jclass thiz,
|
||||
jobject buffer) {
|
||||
EH_UNUSED(thiz);
|
||||
void *addr = (*env)->GetDirectBufferAddress(env, buffer);
|
||||
free(addr);
|
||||
}
|
||||
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
|
||||
mempool_release_pages(addr, size);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#include <jni.h>
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_hippo_Native_getFd(JNIEnv *env, jclass clazz, jobject fileDescriptor) {
|
||||
jint fd = -1;
|
||||
jclass fdClass = (*env)->FindClass(env, "java/io/FileDescriptor");
|
||||
|
||||
if (fdClass != NULL) {
|
||||
jfieldID fdClassDescriptorFieldID = (*env)->GetFieldID(env, fdClass, "descriptor", "I");
|
||||
if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
|
||||
fd = (*env)->GetIntField(env, fileDescriptor, fdClassDescriptorFieldID);
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <jni.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <android/log.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define LOG_TAG "gifUtils"
|
||||
|
||||
#include "ehviewer.h"
|
||||
|
||||
#define GIF_HEADER_87A "GIF87a"
|
||||
#define GIF_HEADER_89A "GIF89a"
|
||||
|
||||
static int FRAME_DELAY_START_MARKER = 0x0021F904;
|
||||
|
||||
typedef signed char byte;
|
||||
|
||||
#define FRAME_DELAY_START_MARKER ((byte*)(&FRAME_DELAY_START_MARKER))
|
||||
#define MINIMUM_FRAME_DELAY 2
|
||||
#define DEFAULT_FRAME_DELAY 10
|
||||
|
||||
static inline bool isGif(void *addr) {
|
||||
return !memcmp(addr, GIF_HEADER_87A, 6) || !memcmp(addr, GIF_HEADER_89A, 6);
|
||||
}
|
||||
|
||||
static void doRewrite(byte *addr, size_t size) {
|
||||
if (size < 7 || !isGif(addr)) return;
|
||||
for (size_t i = 0; i < size - 8; i++) {
|
||||
// TODO: Optimize this hex find with SIMD?
|
||||
if (addr[i] == FRAME_DELAY_START_MARKER[3] && addr[i + 1] == FRAME_DELAY_START_MARKER[2] &&
|
||||
addr[i + 2] == FRAME_DELAY_START_MARKER[1] &&
|
||||
addr[i + 3] == FRAME_DELAY_START_MARKER[0]) {
|
||||
byte *end = addr + i + 4;
|
||||
if (end[4] != 0) continue;
|
||||
int frameDelay = end[2] << 8 | end[1];
|
||||
if (frameDelay > MINIMUM_FRAME_DELAY)
|
||||
break; // Quit if the first block looks normal, for performance
|
||||
end[1] = DEFAULT_FRAME_DELAY;
|
||||
end[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource(JNIEnv *env, jclass clazz, jobject buffer) {
|
||||
byte *addr = (*env)->GetDirectBufferAddress(env, buffer);
|
||||
size_t size = (*env)->GetDirectBufferCapacity(env, buffer);
|
||||
doRewrite(addr, size);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_hippo_ehviewer_image_ImageKt_rewriteGifSource2(JNIEnv *env, jclass clazz, jint fd) {
|
||||
struct stat64 st;
|
||||
fstat64(fd, &st);
|
||||
size_t size = st.st_size;
|
||||
byte *addr = mmap64(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (addr == MAP_FAILED) return;
|
||||
doRewrite(addr, size);
|
||||
munmap(addr, size);
|
||||
}
|
||||
Submodule app/src/main/cpp/libarchive deleted from c9298fd6dc.
Submodule app/src/main/cpp/liblzma deleted from b35079d589.
@@ -0,0 +1,177 @@
|
||||
/* -*- mode: c; c-file-style: "k&r" -*-
|
||||
|
||||
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
|
||||
Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/* partial change history:
|
||||
*
|
||||
* 2004-10-10 mbp: Lift out character type dependencies into macros.
|
||||
*
|
||||
* Eric Sosman pointed out that ctype functions take a parameter whose
|
||||
* value must be that of an unsigned int, even on platforms that have
|
||||
* negative chars in their default char type.
|
||||
*/
|
||||
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <ctype.h>
|
||||
|
||||
#include "strnatcmp.h"
|
||||
|
||||
|
||||
/* These are defined as macros to make it easier to adapt this code to
|
||||
* different characters types or comparison functions. */
|
||||
static inline int
|
||||
nat_isdigit(nat_char a)
|
||||
{
|
||||
return isdigit((unsigned char) a);
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
nat_isspace(nat_char a)
|
||||
{
|
||||
return isspace((unsigned char) a);
|
||||
}
|
||||
|
||||
|
||||
static inline nat_char
|
||||
nat_toupper(nat_char a)
|
||||
{
|
||||
return toupper((unsigned char) a);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_right(nat_char const *a, nat_char const *b)
|
||||
{
|
||||
int bias = 0;
|
||||
|
||||
/* The longest run of digits wins. That aside, the greatest
|
||||
value wins, but we can't know that it will until we've scanned
|
||||
both numbers to know that they have the same magnitude, so we
|
||||
remember it in BIAS. */
|
||||
for (;; a++, b++) {
|
||||
if (!nat_isdigit(*a) && !nat_isdigit(*b))
|
||||
return bias;
|
||||
if (!nat_isdigit(*a))
|
||||
return -1;
|
||||
if (!nat_isdigit(*b))
|
||||
return +1;
|
||||
if (*a < *b) {
|
||||
if (!bias)
|
||||
bias = -1;
|
||||
} else if (*a > *b) {
|
||||
if (!bias)
|
||||
bias = +1;
|
||||
} else if (!*a && !*b)
|
||||
return bias;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
compare_left(nat_char const *a, nat_char const *b)
|
||||
{
|
||||
/* Compare two left-aligned numbers: the first to have a
|
||||
different value wins. */
|
||||
for (;; a++, b++) {
|
||||
if (!nat_isdigit(*a) && !nat_isdigit(*b))
|
||||
return 0;
|
||||
if (!nat_isdigit(*a))
|
||||
return -1;
|
||||
if (!nat_isdigit(*b))
|
||||
return +1;
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
if (*a > *b)
|
||||
return +1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
strnatcmp0(nat_char const *a, nat_char const *b, int fold_case)
|
||||
{
|
||||
int ai, bi;
|
||||
nat_char ca, cb;
|
||||
int fractional, result;
|
||||
|
||||
ai = bi = 0;
|
||||
while (1) {
|
||||
ca = a[ai]; cb = b[bi];
|
||||
|
||||
/* skip over leading spaces or zeros */
|
||||
while (nat_isspace(ca))
|
||||
ca = a[++ai];
|
||||
|
||||
while (nat_isspace(cb))
|
||||
cb = b[++bi];
|
||||
|
||||
/* process run of digits */
|
||||
if (nat_isdigit(ca) && nat_isdigit(cb)) {
|
||||
fractional = (ca == '0' || cb == '0');
|
||||
|
||||
if (fractional) {
|
||||
if ((result = compare_left(a+ai, b+bi)) != 0)
|
||||
return result;
|
||||
} else {
|
||||
if ((result = compare_right(a+ai, b+bi)) != 0)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ca && !cb) {
|
||||
/* The strings compare the same. Perhaps the caller
|
||||
will want to call strcmp to break the tie. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fold_case) {
|
||||
ca = nat_toupper(ca);
|
||||
cb = nat_toupper(cb);
|
||||
}
|
||||
|
||||
if (ca < cb)
|
||||
return -1;
|
||||
|
||||
if (ca > cb)
|
||||
return +1;
|
||||
|
||||
++ai; ++bi;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
strnatcmp(nat_char const *a, nat_char const *b) {
|
||||
return strnatcmp0(a, b, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Compare, recognizing numeric string and ignoring case. */
|
||||
int
|
||||
strnatcasecmp(nat_char const *a, nat_char const *b) {
|
||||
return strnatcmp0(a, b, 1);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/* -*- mode: c; c-file-style: "k&r" -*-
|
||||
|
||||
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
|
||||
Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* CUSTOMIZATION SECTION
|
||||
*
|
||||
* You can change this typedef, but must then also change the inline
|
||||
* functions in strnatcmp.c */
|
||||
typedef char nat_char;
|
||||
|
||||
int strnatcmp(nat_char const *a, nat_char const *b);
|
||||
int strnatcasecmp(nat_char const *a, nat_char const *b);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
/nettle
|
||||
@@ -32,7 +32,6 @@ set(LIBNETTLE_SOURCES
|
||||
nettle/aes256-meta.c
|
||||
nettle/nist-keywrap.c
|
||||
nettle/arcfour.c
|
||||
nettle/arcfour-crypt.c
|
||||
nettle/arctwo.c
|
||||
nettle/arctwo-meta.c
|
||||
nettle/blowfish.c
|
||||
@@ -177,7 +176,7 @@ set(LIBNETTLE_SOURCES
|
||||
nettle/sha1-compress.c
|
||||
nettle/sha1-meta.c
|
||||
nettle/sha256.c
|
||||
nettle/sha256-compress.c
|
||||
nettle/sha256-compress-n.c
|
||||
nettle/sha224-meta.c
|
||||
nettle/sha256-meta.c
|
||||
nettle/sha512.c
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
/* Define if building universal (internal helper macro) */
|
||||
/* #undef AC_APPLE_UNIVERSAL_BUILD */
|
||||
|
||||
/* Define to 1 if using 'alloca.c'. */
|
||||
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
|
||||
systems. This function is required for `alloca.c' support on those systems.
|
||||
*/
|
||||
/* #undef CRAY_STACKSEG_END */
|
||||
|
||||
/* Define to 1 if using `alloca.c'. */
|
||||
/* #undef C_ALLOCA */
|
||||
|
||||
/* Define to 1 if you have 'alloca', as a function or macro. */
|
||||
/* Define to 1 if you have `alloca', as a function or macro. */
|
||||
#define HAVE_ALLOCA 1
|
||||
|
||||
/* Define to 1 if <alloca.h> works. */
|
||||
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
|
||||
*/
|
||||
#define HAVE_ALLOCA_H 1
|
||||
|
||||
/* Define if __builtin_bswap64 is available */
|
||||
@@ -46,6 +52,9 @@
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#define HAVE_MALLOC_H 1
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 each of the following for which a native (ie. CPU specific)
|
||||
implementation of the corresponding routine exists. */
|
||||
/* #undef HAVE_NATIVE_memxor3 */
|
||||
@@ -71,7 +80,7 @@
|
||||
/* #undef HAVE_NATIVE_cbc_aes256_encrypt */
|
||||
/* #undef HAVE_NATIVE_chacha_core */
|
||||
/* #undef HAVE_NATIVE_chacha_2core */
|
||||
/* #undef HAVE_NATIVE_chacha_3core */
|
||||
#define HAVE_NATIVE_chacha_3core 1
|
||||
/* #undef HAVE_NATIVE_chacha_4core */
|
||||
/* #undef HAVE_NATIVE_fat_chacha_2core */
|
||||
/* #undef HAVE_NATIVE_fat_chacha_3core */
|
||||
@@ -88,13 +97,18 @@
|
||||
/* #undef HAVE_NATIVE_ecc_secp384r1_redc */
|
||||
/* #undef HAVE_NATIVE_ecc_secp521r1_modp */
|
||||
/* #undef HAVE_NATIVE_ecc_secp521r1_redc */
|
||||
/* #undef HAVE_NATIVE_poly1305_set_key */
|
||||
/* #undef HAVE_NATIVE_poly1305_block */
|
||||
/* #undef HAVE_NATIVE_poly1305_digest */
|
||||
/* #undef HAVE_NATIVE_poly1305_blocks */
|
||||
/* #undef HAVE_NATIVE_fat_poly1305_blocks */
|
||||
/* #undef HAVE_NATIVE_ghash_set_key */
|
||||
/* #undef HAVE_NATIVE_ghash_update */
|
||||
/* #undef HAVE_NATIVE_salsa20_core */
|
||||
/* #undef HAVE_NATIVE_salsa20_2core */
|
||||
#define HAVE_NATIVE_salsa20_2core 1
|
||||
/* #undef HAVE_NATIVE_fat_salsa20_2core */
|
||||
/* #undef HAVE_NATIVE_sha1_compress */
|
||||
/* #undef HAVE_NATIVE_sha256_compress */
|
||||
/* #undef HAVE_NATIVE_sha256_compress_n */
|
||||
/* #undef HAVE_NATIVE_sha512_compress */
|
||||
/* #undef HAVE_NATIVE_sha3_permute */
|
||||
/* #undef HAVE_NATIVE_umac_nh */
|
||||
@@ -107,14 +121,11 @@
|
||||
/* #undef HAVE_OPENSSL_EVP_H */
|
||||
|
||||
/* Define to 1 if you have the `secure_getenv' function. */
|
||||
/* #undef HAVE_SECURE_GETENV */
|
||||
#define HAVE_SECURE_GETENV 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#define HAVE_STDIO_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
@@ -130,9 +141,6 @@
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#define HAVE_SYS_TIME_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
@@ -149,7 +157,7 @@
|
||||
#define PACKAGE_NAME "nettle"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "nettle 3.8.1"
|
||||
#define PACKAGE_STRING "nettle 3.9.1"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "nettle"
|
||||
@@ -158,7 +166,7 @@
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "3.8.1"
|
||||
#define PACKAGE_VERSION "3.9.1"
|
||||
|
||||
/* The size of `long', as computed by sizeof. */
|
||||
#define SIZEOF_LONG __SIZEOF_LONG__
|
||||
@@ -174,13 +182,10 @@
|
||||
STACK_DIRECTION = 0 => direction of growth unknown */
|
||||
/* #undef STACK_DIRECTION */
|
||||
|
||||
/* Define to 1 if all of the C90 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
|
||||
macro is obsolete. */
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#define TIME_WITH_SYS_TIME 1
|
||||
|
||||
/* Defined if public key features are enabled */
|
||||
|
||||
Submodule app/src/main/cpp/nettle/nettle deleted from 990abad16c.
@@ -41,7 +41,7 @@ extern "C" {
|
||||
|
||||
/* Individual version numbers in decimal */
|
||||
#define NETTLE_VERSION_MAJOR 3
|
||||
#define NETTLE_VERSION_MINOR 8
|
||||
#define NETTLE_VERSION_MINOR 9
|
||||
|
||||
#define NETTLE_USE_MINI_GMP 0
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef EHVIEWER_SPINLOCK_H
|
||||
#define EHVIEWER_SPINLOCK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define cmpxchg(P, O, N) __sync_val_compare_and_swap((P), (O), (N))
|
||||
|
||||
#define barrier() __asm__ __volatile__("":::"memory")
|
||||
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
#define cpu_relax() __asm__ volatile ("wfe");
|
||||
#else
|
||||
#define cpu_relax() __asm__ volatile ("pause" ::: "memory");
|
||||
#endif
|
||||
|
||||
typedef struct mcs_lock_t mcs_lock_t;
|
||||
struct mcs_lock_t {
|
||||
mcs_lock_t *next;
|
||||
int spin;
|
||||
};
|
||||
typedef struct mcs_lock_t *mcs_lock;
|
||||
|
||||
static inline void lock_mcs(_Atomic mcs_lock *m, mcs_lock_t *me) {
|
||||
mcs_lock_t *tail;
|
||||
me->next = NULL;
|
||||
me->spin = 0;
|
||||
tail = atomic_exchange(m, me);
|
||||
if (!tail) return;
|
||||
tail->next = me;
|
||||
barrier();
|
||||
while (!me->spin) cpu_relax();
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void unlock_mcs(mcs_lock *m, mcs_lock_t *me) {
|
||||
if (!me->next) {
|
||||
if (cmpxchg(m, me, NULL) == me) return;
|
||||
while (!me->next) cpu_relax();
|
||||
}
|
||||
me->next->spin = 1;
|
||||
}
|
||||
|
||||
#endif //EHVIEWER_SPINLOCK_H
|
||||
@@ -0,0 +1,41 @@
|
||||
package androidx.navigation.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.navigation.ActivityNavigator
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
import androidx.navigation.NavOptions
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
fun onNavDestinationSelected2(item: MenuItem, navController: NavController): Boolean {
|
||||
val builder = NavOptions.Builder().setLaunchSingleTop(true)
|
||||
if (
|
||||
navController.currentDestination!!.parent!!.findNode(item.itemId)
|
||||
is ActivityNavigator.Destination
|
||||
) {
|
||||
builder.setEnterAnim(R.anim.nav_default_enter_anim)
|
||||
.setExitAnim(R.anim.nav_default_exit_anim)
|
||||
.setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
|
||||
.setPopExitAnim(R.anim.nav_default_pop_exit_anim)
|
||||
} else {
|
||||
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 {
|
||||
// TODO provide proper API instead of using Exceptions as Control-Flow.
|
||||
navController.navigate(item.itemId, null, options)
|
||||
// Return true only if the destination we've navigated to matches the MenuItem
|
||||
navController.currentDestination?.matchDestination(item.itemId) == true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun NavDestination.matchDestination(@IdRes destId: Int): Boolean =
|
||||
hierarchy.any { it.id == destId }
|
||||
@@ -7,10 +7,10 @@ import android.util.AttributeSet
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import rikka.core.res.resolveColor
|
||||
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
class ThemedSwipeRefreshLayout @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : SwipeRefreshLayout(context, attrs) {
|
||||
|
||||
init {
|
||||
@@ -22,4 +22,4 @@ class ThemedSwipeRefreshLayout @JvmOverloads constructor(
|
||||
(mCircleView.background as ShapeDrawable).paint.color = backgroundColor
|
||||
setColorSchemeColors(context.theme.resolveColor(com.google.android.material.R.attr.colorAccent))
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,15 +1,8 @@
|
||||
package com.davemorrissey.labs.subscaleview
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import com.davemorrissey.labs.subscaleview.provider.AssetInputProvider
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import com.davemorrissey.labs.subscaleview.provider.OpenStreamProvider
|
||||
import com.davemorrissey.labs.subscaleview.provider.ResourceInputProvider
|
||||
import com.davemorrissey.labs.subscaleview.provider.UriInputProvider
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Helper class used to set the source and additional attributes from a variety of sources. Supports
|
||||
@@ -83,38 +76,6 @@ class ImageSource {
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create an instance from a resource. The correct resource for the device screen resolution will be used.
|
||||
*
|
||||
* @param resId resource ID.
|
||||
* @return an [ImageSource] instance.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun resource(context: Context, resId: Int): ImageSource {
|
||||
return ImageSource(ResourceInputProvider(context, resId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from an asset name.
|
||||
*
|
||||
* @param assetName asset name.
|
||||
* @return an [ImageSource] instance.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun asset(context: Context, assetName: String): ImageSource {
|
||||
return ImageSource(AssetInputProvider(context, assetName))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from a URI.
|
||||
*
|
||||
* @param uri image URI.
|
||||
* @return an [ImageSource] instance.
|
||||
*/
|
||||
fun uri(context: Context, uri: Uri): ImageSource {
|
||||
return ImageSource(UriInputProvider(context, uri))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from an input provider.
|
||||
*
|
||||
@@ -125,16 +86,6 @@ class ImageSource {
|
||||
return ImageSource(provider)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance from an input stream.
|
||||
*
|
||||
* @param stream open input stream.
|
||||
* @return an [ImageSource] instance.
|
||||
*/
|
||||
fun inputStream(stream: InputStream): ImageSource {
|
||||
return ImageSource(OpenStreamProvider(stream))
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a loaded bitmap for display.
|
||||
*
|
||||
|
||||
+150
-159
@@ -19,12 +19,10 @@ import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.IntDef
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource.Companion.asset
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource.Companion.resource
|
||||
import com.davemorrissey.labs.subscaleview.decoder.Decoder
|
||||
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import com.hippo.ehviewer.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -33,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
|
||||
@@ -65,7 +62,7 @@ import kotlin.math.sqrt
|
||||
@Suppress("unused")
|
||||
open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attr: AttributeSet? = null
|
||||
attr: AttributeSet? = null,
|
||||
) : View(context, attr) {
|
||||
private val decoderLock: ReadWriteLock = ReentrantReadWriteLock(true)
|
||||
|
||||
@@ -81,7 +78,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
private val srcArray = FloatArray(8)
|
||||
private val dstArray = FloatArray(8)
|
||||
|
||||
//The logical density of the display
|
||||
// The logical density of the display
|
||||
private val density: Float = resources.displayMetrics.density
|
||||
|
||||
// Bitmap (preview or full image)
|
||||
@@ -142,6 +139,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
|
||||
// Double tap zoom behaviour
|
||||
private var doubleTapZoomScale = 1F
|
||||
|
||||
@ZoomStyle private var doubleTapZoomStyle = ZOOM_FOCUS_FIXED
|
||||
private var doubleTapZoomDuration = 500
|
||||
|
||||
@@ -303,42 +301,6 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
setMinimumDpi(160)
|
||||
setDoubleTapZoomDpi(160)
|
||||
setGestureDetector(context)
|
||||
|
||||
// Handle XML attributes
|
||||
if (attr != null) {
|
||||
val typedAttr = getContext().obtainStyledAttributes(attr, R.styleable.SubsamplingScaleImageView)
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_assetName)) {
|
||||
val assetName = typedAttr.getString(R.styleable.SubsamplingScaleImageView_assetName)
|
||||
if (assetName != null && assetName.isNotEmpty()) {
|
||||
setImage(asset(context, assetName))
|
||||
}
|
||||
}
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_src)) {
|
||||
val resId = typedAttr.getResourceId(R.styleable.SubsamplingScaleImageView_src, 0)
|
||||
if (resId > 0) {
|
||||
setImage(resource(context, resId))
|
||||
}
|
||||
}
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_panEnabled)) {
|
||||
setPanEnabled(typedAttr.getBoolean(R.styleable.SubsamplingScaleImageView_panEnabled, true))
|
||||
}
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_zoomEnabled)) {
|
||||
isZoomEnabled = typedAttr.getBoolean(R.styleable.SubsamplingScaleImageView_zoomEnabled, true)
|
||||
}
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_quickScaleEnabled)) {
|
||||
isQuickScaleEnabled =
|
||||
typedAttr.getBoolean(R.styleable.SubsamplingScaleImageView_quickScaleEnabled, true)
|
||||
}
|
||||
if (typedAttr.hasValue(R.styleable.SubsamplingScaleImageView_tileBackgroundColor)) {
|
||||
setTileBackgroundColor(
|
||||
typedAttr.getColor(
|
||||
R.styleable.SubsamplingScaleImageView_tileBackgroundColor,
|
||||
Color.argb(0, 0, 0, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
typedAttr.recycle()
|
||||
}
|
||||
quickScaleThreshold =
|
||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20F, context.resources.displayMetrics)
|
||||
}
|
||||
@@ -370,8 +332,9 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
imageSource.sRegion.left,
|
||||
imageSource.sRegion.top,
|
||||
imageSource.sRegion.width(),
|
||||
imageSource.sRegion.height()
|
||||
), false
|
||||
imageSource.sRegion.height(),
|
||||
),
|
||||
false,
|
||||
)
|
||||
} else if (imageSource.bitmap != null) {
|
||||
onImageLoaded(imageSource.bitmap, imageSource.isCached)
|
||||
@@ -475,66 +438,71 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun setGestureDetector(context: Context) {
|
||||
detector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
// The e1 can be null, that's weird and cause crash
|
||||
if (panEnabled && isReady && vTranslate != null && e1 != null && e2 != 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
|
||||
AnimationBuilder(PointF(sCenterXEnd, sCenterYEnd))
|
||||
.withEasing(EASE_OUT_QUAD)
|
||||
.withPanLimited(false)
|
||||
.withOrigin(ORIGIN_FLING)
|
||||
.start()
|
||||
detector = GestureDetector(
|
||||
context,
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
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
|
||||
AnimationBuilder(PointF(sCenterXEnd, sCenterYEnd))
|
||||
.withEasing(EASE_OUT_QUAD)
|
||||
.withPanLimited(false)
|
||||
.withOrigin(ORIGIN_FLING)
|
||||
.start()
|
||||
return true
|
||||
}
|
||||
return super.onFling(e1, e2, velocityX, velocityY)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return true
|
||||
}
|
||||
return super.onFling(e1, e2, velocityX, velocityY)
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
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.
|
||||
setGestureDetector(context)
|
||||
return if (isQuickScaleEnabled) {
|
||||
// Store quick scale params. This will become either a double tap zoom or a
|
||||
// quick scale depending on whether the user swipes.
|
||||
vCenterStart = PointF(e.x, e.y)
|
||||
vTranslateStart = PointF(vTranslate!!.x, vTranslate!!.y)
|
||||
scaleStart = scale
|
||||
isQuickScaling = true
|
||||
isZooming = true
|
||||
quickScaleLastDistance = -1F
|
||||
quickScaleSCenter = viewToSourceCoord(vCenterStart!!)
|
||||
quickScaleVStart = PointF(e.x, e.y)
|
||||
quickScaleVLastPoint = PointF(quickScaleSCenter!!.x, quickScaleSCenter!!.y)
|
||||
quickScaleMoved = false
|
||||
// We need to get events in onTouchEvent after this.
|
||||
false
|
||||
} else {
|
||||
// Start double tap zoom animation.
|
||||
viewToSourceCoord(PointF(e.x, e.y))?.let { center ->
|
||||
doubleTapZoom(center, PointF(e.x, e.y))
|
||||
override fun onDoubleTap(e: MotionEvent): Boolean {
|
||||
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.
|
||||
setGestureDetector(context)
|
||||
return if (isQuickScaleEnabled) {
|
||||
// Store quick scale params. This will become either a double tap zoom or a
|
||||
// quick scale depending on whether the user swipes.
|
||||
vCenterStart = PointF(e.x, e.y)
|
||||
vTranslateStart = PointF(vTranslate!!.x, vTranslate!!.y)
|
||||
scaleStart = scale
|
||||
isQuickScaling = true
|
||||
isZooming = true
|
||||
quickScaleLastDistance = -1F
|
||||
quickScaleSCenter = viewToSourceCoord(vCenterStart!!)
|
||||
quickScaleVStart = PointF(e.x, e.y)
|
||||
quickScaleVLastPoint = PointF(quickScaleSCenter!!.x, quickScaleSCenter!!.y)
|
||||
quickScaleMoved = false
|
||||
// We need to get events in onTouchEvent after this.
|
||||
false
|
||||
} else {
|
||||
// Start double tap zoom animation.
|
||||
viewToSourceCoord(PointF(e.x, e.y))?.let { center ->
|
||||
doubleTapZoom(center, PointF(e.x, e.y))
|
||||
}
|
||||
true
|
||||
}
|
||||
true
|
||||
}
|
||||
return super.onDoubleTapEvent(e)
|
||||
}
|
||||
return super.onDoubleTapEvent(e)
|
||||
}
|
||||
})
|
||||
singleDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return true
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
singleDetector = GestureDetector(
|
||||
context,
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
performClick()
|
||||
return true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
@@ -607,12 +575,16 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
return true
|
||||
}
|
||||
// Detect flings, taps and double taps
|
||||
if (!isQuickScaling && detector?.onTouchEvent(event) != false) {
|
||||
isZooming = false
|
||||
isPanning = false
|
||||
maxTouchCount = 0
|
||||
return true
|
||||
// May throw NPE
|
||||
runCatching {
|
||||
if (!isQuickScaling && detector?.onTouchEvent(event) != false) {
|
||||
isZooming = false
|
||||
isPanning = false
|
||||
maxTouchCount = 0
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (vTranslateStart == null) {
|
||||
vTranslateStart = PointF(0F, 0F)
|
||||
}
|
||||
@@ -767,7 +739,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val dx = abs(event.x - vCenterStart!!.x)
|
||||
val dy = abs(event.y - vCenterStart!!.y)
|
||||
|
||||
//On the Samsung S6 long click event does not work, because the dx > 5 usually true
|
||||
// On the Samsung S6 long click event does not work, because the dx > 5 usually true
|
||||
val offset = density * 5
|
||||
if (dx > offset || dy > offset || isPanning) {
|
||||
consumed = true
|
||||
@@ -931,7 +903,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
scaleElapsed,
|
||||
anim!!.scaleStart,
|
||||
anim!!.scaleEnd - anim!!.scaleStart,
|
||||
anim!!.duration
|
||||
anim!!.duration,
|
||||
)
|
||||
|
||||
// Apply required animation to the focal point
|
||||
@@ -940,14 +912,14 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
scaleElapsed,
|
||||
anim!!.vFocusStart!!.x,
|
||||
anim!!.vFocusEnd.x - anim!!.vFocusStart!!.x,
|
||||
anim!!.duration
|
||||
anim!!.duration,
|
||||
)
|
||||
val vFocusNowY = ease(
|
||||
anim!!.easing,
|
||||
scaleElapsed,
|
||||
anim!!.vFocusStart!!.y,
|
||||
anim!!.vFocusEnd.y - anim!!.vFocusStart!!.y,
|
||||
anim!!.duration
|
||||
anim!!.duration,
|
||||
)
|
||||
// Find out where the focal point is at this scale and adjust its position to follow the animation path
|
||||
vTranslate!!.x -= sourceToViewX(anim!!.sCenterEnd.x) - vFocusNowX
|
||||
@@ -1004,7 +976,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
tileWidth,
|
||||
tileHeight,
|
||||
0F,
|
||||
tileHeight
|
||||
tileHeight,
|
||||
)
|
||||
setMatrixArray(
|
||||
dstArray,
|
||||
@@ -1015,7 +987,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
tile.vRect.right.toFloat(),
|
||||
tile.vRect.bottom.toFloat(),
|
||||
tile.vRect.left.toFloat(),
|
||||
tile.vRect.bottom.toFloat()
|
||||
tile.vRect.bottom.toFloat(),
|
||||
)
|
||||
mtrx.setPolyToPoly(srcArray, 0, dstArray, 0, 4)
|
||||
canvas.drawBitmap(tile.bitmap!!, mtrx, bitmapPaint)
|
||||
@@ -1027,7 +999,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
"LOADING",
|
||||
(tile.vRect.left + px(5)).toFloat(),
|
||||
(tile.vRect.top + px(35)).toFloat(),
|
||||
debugTextPaint
|
||||
debugTextPaint,
|
||||
)
|
||||
}
|
||||
if (tile.visible && debug) {
|
||||
@@ -1035,7 +1007,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
"ISS " + tile.sampleSize + " RECT " + tile.sRect.top + "," + tile.sRect.left + "," + tile.sRect.bottom + "," + tile.sRect.right,
|
||||
(tile.vRect.left + px(5)).toFloat(),
|
||||
(tile.vRect.top + px(15)).toFloat(),
|
||||
debugTextPaint
|
||||
debugTextPaint,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1060,21 +1032,36 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
"Scale: " + String.format(Locale.ENGLISH, "%.2f", scale) + " (" + String.format(
|
||||
Locale.ENGLISH,
|
||||
"%.2f",
|
||||
minScale()
|
||||
minScale(),
|
||||
) + " - " + String.format(
|
||||
Locale.ENGLISH, "%.2f", maxScale
|
||||
) + ")", px(5).toFloat(), px(15).toFloat(), debugTextPaint
|
||||
Locale.ENGLISH,
|
||||
"%.2f",
|
||||
maxScale,
|
||||
) + ")",
|
||||
px(5).toFloat(),
|
||||
px(15).toFloat(),
|
||||
debugTextPaint,
|
||||
)
|
||||
canvas.drawText(
|
||||
"Translate: " + String.format(Locale.ENGLISH, "%.2f", vTranslate!!.x) + ":" + String.format(
|
||||
Locale.ENGLISH, "%.2f", vTranslate!!.y
|
||||
), px(5).toFloat(), px(30).toFloat(), debugTextPaint
|
||||
Locale.ENGLISH,
|
||||
"%.2f",
|
||||
vTranslate!!.y,
|
||||
),
|
||||
px(5).toFloat(),
|
||||
px(30).toFloat(),
|
||||
debugTextPaint,
|
||||
)
|
||||
val center = center
|
||||
canvas.drawText(
|
||||
"Source center: " + String.format(Locale.ENGLISH, "%.2f", center!!.x) + ":" + String.format(
|
||||
Locale.ENGLISH, "%.2f", center.y
|
||||
), px(5).toFloat(), px(45).toFloat(), debugTextPaint
|
||||
Locale.ENGLISH,
|
||||
"%.2f",
|
||||
center.y,
|
||||
),
|
||||
px(5).toFloat(),
|
||||
px(45).toFloat(),
|
||||
debugTextPaint,
|
||||
)
|
||||
if (anim != null) {
|
||||
val vCenterStart = sourceToViewCoord(anim!!.sCenterStart)
|
||||
@@ -1098,7 +1085,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
sourceToViewX(quickScaleSCenter!!.x),
|
||||
sourceToViewY(quickScaleSCenter!!.y),
|
||||
px(35).toFloat(),
|
||||
debugLinePaint
|
||||
debugLinePaint,
|
||||
)
|
||||
}
|
||||
if (quickScaleVStart != null && isQuickScaling) {
|
||||
@@ -1121,7 +1108,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
f4: Float,
|
||||
f5: Float,
|
||||
f6: Float,
|
||||
f7: Float
|
||||
f7: Float,
|
||||
) {
|
||||
array[0] = f0
|
||||
array[1] = f1
|
||||
@@ -1195,7 +1182,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
debug(
|
||||
"loadTile, tile.sRect=%s, tile.sampleSize=%d",
|
||||
tile.sRect,
|
||||
tile.sampleSize
|
||||
tile.sampleSize,
|
||||
)
|
||||
decoderLock.readLock().lock()
|
||||
try {
|
||||
@@ -1466,13 +1453,13 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
x * sTileWidth,
|
||||
y * sTileHeight,
|
||||
if (x == xTiles - 1) sWidth else (x + 1) * sTileWidth,
|
||||
if (y == yTiles - 1) sHeight else (y + 1) * sTileHeight
|
||||
if (y == yTiles - 1) sHeight else (y + 1) * sTileHeight,
|
||||
)
|
||||
val tile = Tile(
|
||||
sRect = sRect,
|
||||
sampleSize = sampleSize,
|
||||
vRect = Rect(0, 0, 0, 0),
|
||||
fileSRect = Rect(sRect)
|
||||
fileSRect = Rect(sRect),
|
||||
)
|
||||
tileGrid.add(tile)
|
||||
}
|
||||
@@ -1601,7 +1588,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
private fun getMaxBitmapDimensions(canvas: Canvas): Point {
|
||||
return Point(
|
||||
canvas.maximumBitmapWidth.coerceAtMost(maxTileWidth),
|
||||
canvas.maximumBitmapHeight.coerceAtMost(maxTileHeight)
|
||||
canvas.maximumBitmapHeight.coerceAtMost(maxTileHeight),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1666,15 +1653,15 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
if (vTranslate == null || !isReady) return
|
||||
|
||||
fRect[
|
||||
viewToSourceX(vRect.left.toFloat()).toInt(),
|
||||
viewToSourceY(vRect.top.toFloat()).toInt(),
|
||||
viewToSourceX(vRect.right.toFloat()).toInt()
|
||||
viewToSourceX(vRect.left.toFloat()).toInt(),
|
||||
viewToSourceY(vRect.top.toFloat()).toInt(),
|
||||
viewToSourceX(vRect.right.toFloat()).toInt(),
|
||||
] = viewToSourceY(vRect.bottom.toFloat()).toInt()
|
||||
fileSRect(fRect, fRect)
|
||||
fRect[
|
||||
fRect.left.coerceAtLeast(0),
|
||||
fRect.top.coerceAtLeast(0),
|
||||
fRect.right.coerceAtLeast(sWidth)
|
||||
fRect.left.coerceAtLeast(0),
|
||||
fRect.top.coerceAtLeast(0),
|
||||
fRect.right.coerceAtLeast(sWidth),
|
||||
] = fRect.bottom.coerceAtLeast(sHeight)
|
||||
if (!sRegion.isEmpty) {
|
||||
fRect.offset(sRegion.left, sRegion.top)
|
||||
@@ -1725,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
|
||||
@@ -1777,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
|
||||
@@ -1791,9 +1776,9 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
*/
|
||||
private fun sourceToViewRect(sRect: Rect, vTarget: Rect) {
|
||||
vTarget[
|
||||
sourceToViewX(sRect.left.toFloat()).toInt(),
|
||||
sourceToViewY(sRect.top.toFloat()).toInt(),
|
||||
sourceToViewX(sRect.right.toFloat()).toInt()
|
||||
sourceToViewX(sRect.left.toFloat()).toInt(),
|
||||
sourceToViewY(sRect.top.toFloat()).toInt(),
|
||||
sourceToViewX(sRect.right.toFloat()).toInt(),
|
||||
] = sourceToViewY(sRect.bottom.toFloat()).toInt()
|
||||
}
|
||||
|
||||
@@ -1840,11 +1825,11 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
return when (minimumScaleType) {
|
||||
SCALE_TYPE_CENTER_INSIDE -> min(
|
||||
(width - hPadding) / sWidth.toFloat(),
|
||||
(height - vPadding) / sHeight.toFloat()
|
||||
(height - vPadding) / sHeight.toFloat(),
|
||||
)
|
||||
SCALE_TYPE_CENTER_CROP -> max(
|
||||
(width - hPadding) / sWidth.toFloat(),
|
||||
(height - vPadding) / sHeight.toFloat()
|
||||
(height - vPadding) / sHeight.toFloat(),
|
||||
)
|
||||
SCALE_TYPE_FIT_WIDTH -> (width - hPadding) / sWidth.toFloat()
|
||||
SCALE_TYPE_FIT_HEIGHT -> (height - vPadding) / sHeight.toFloat()
|
||||
@@ -1951,20 +1936,20 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val scaleHeight = scale * sHeight
|
||||
when (panLimit) {
|
||||
PAN_LIMIT_CENTER -> {
|
||||
vTarget.top = -(vTranslate!!.y - height / 2).coerceAtLeast(0F)
|
||||
vTarget.left = -(vTranslate!!.x - width / 2).coerceAtLeast(0F)
|
||||
vTarget.top = (-(vTranslate!!.y - height / 2)).coerceAtLeast(0F)
|
||||
vTarget.left = (-(vTranslate!!.x - width / 2)).coerceAtLeast(0F)
|
||||
vTarget.bottom = (vTranslate!!.y - (height / 2 - scaleHeight)).coerceAtLeast(0F)
|
||||
vTarget.right = (vTranslate!!.x - (width / 2 - scaleWidth)).coerceAtLeast(0F)
|
||||
}
|
||||
PAN_LIMIT_OUTSIDE -> {
|
||||
vTarget.top = -(vTranslate!!.y - height).coerceAtLeast(0F)
|
||||
vTarget.left = -(vTranslate!!.x - width).coerceAtLeast(0F)
|
||||
vTarget.top = (-(vTranslate!!.y - height)).coerceAtLeast(0F)
|
||||
vTarget.left = (-(vTranslate!!.x - width)).coerceAtLeast(0F)
|
||||
vTarget.bottom = (vTranslate!!.y + scaleHeight).coerceAtLeast(0F)
|
||||
vTarget.right = (vTranslate!!.x + scaleWidth).coerceAtLeast(0F)
|
||||
}
|
||||
else -> {
|
||||
vTarget.top = -vTranslate!!.y.coerceAtLeast(0F)
|
||||
vTarget.left = -vTranslate!!.x.coerceAtLeast(0F)
|
||||
vTarget.top = (-vTranslate!!.y).coerceAtLeast(0F)
|
||||
vTarget.left = (-vTranslate!!.x).coerceAtLeast(0F)
|
||||
vTarget.bottom = (scaleHeight + vTranslate!!.y - height).coerceAtLeast(0F)
|
||||
vTarget.right = (scaleWidth + vTranslate!!.x - width).coerceAtLeast(0F)
|
||||
}
|
||||
@@ -2148,7 +2133,9 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val state: ImageViewState?
|
||||
get() = if (vTranslate != null && sWidth > 0 && sHeight > 0) {
|
||||
ImageViewState(scale, center!!)
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if pan gesture detection is enabled.
|
||||
@@ -2434,7 +2421,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val sRect: Rect,
|
||||
val sampleSize: Int,
|
||||
val vRect: Rect,
|
||||
val fileSRect: Rect
|
||||
val fileSRect: Rect,
|
||||
) {
|
||||
var bitmap: Bitmap? = null
|
||||
var loading = false
|
||||
@@ -2469,7 +2456,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val easing: Int,
|
||||
val origin: Int,
|
||||
val time: Long = System.currentTimeMillis(),
|
||||
val listener: OnAnimationEventListener?
|
||||
val listener: OnAnimationEventListener?,
|
||||
)
|
||||
|
||||
private data class ScaleAndTranslate(var scale: Float, val vTranslate: PointF)
|
||||
@@ -2615,12 +2602,16 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
val vxCenter = paddingLeft + (width - paddingRight - paddingLeft) / 2
|
||||
val vyCenter = paddingTop + (height - paddingBottom - paddingTop) / 2
|
||||
val targetScale = limitedScale(targetScale)
|
||||
val targetSCenter = if (panLimited) limitedSCenter(
|
||||
targetSCenter!!.x,
|
||||
targetSCenter.y,
|
||||
targetScale,
|
||||
PointF()
|
||||
) else targetSCenter!!
|
||||
val targetSCenter = if (panLimited) {
|
||||
limitedSCenter(
|
||||
targetSCenter!!.x,
|
||||
targetSCenter.y,
|
||||
targetScale,
|
||||
PointF(),
|
||||
)
|
||||
} else {
|
||||
targetSCenter!!
|
||||
}
|
||||
anim = Anim(
|
||||
scaleStart = scale,
|
||||
scaleEnd = targetScale,
|
||||
@@ -2630,13 +2621,13 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
vFocusStart = sourceToViewCoord(targetSCenter),
|
||||
vFocusEnd = PointF(
|
||||
vxCenter.toFloat(),
|
||||
vyCenter.toFloat()
|
||||
vyCenter.toFloat(),
|
||||
),
|
||||
duration = duration,
|
||||
interruptible = interruptible,
|
||||
easing = easing,
|
||||
origin = origin,
|
||||
listener = listener
|
||||
listener = listener,
|
||||
)
|
||||
if (vFocus != null) {
|
||||
// Calculate where translation will be at the end of the anim
|
||||
@@ -2648,7 +2639,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
// Adjust the position of the focus point at end so image will be in bounds
|
||||
anim!!.vFocusEnd.set(
|
||||
vFocus.x + (satEnd.vTranslate.x - vTranslateXEnd),
|
||||
vFocus.y + (satEnd.vTranslate.y - vTranslateYEnd)
|
||||
vFocus.y + (satEnd.vTranslate.y - vTranslateYEnd),
|
||||
)
|
||||
}
|
||||
invalidate()
|
||||
@@ -2660,7 +2651,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
@IntDef(
|
||||
ZOOM_FOCUS_FIXED,
|
||||
ZOOM_FOCUS_CENTER,
|
||||
ZOOM_FOCUS_CENTER_IMMEDIATE
|
||||
ZOOM_FOCUS_CENTER_IMMEDIATE,
|
||||
)
|
||||
annotation class ZoomStyle
|
||||
|
||||
@@ -2682,7 +2673,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@IntDef(
|
||||
EASE_IN_OUT_QUAD,
|
||||
EASE_OUT_QUAD
|
||||
EASE_OUT_QUAD,
|
||||
)
|
||||
annotation class EasingStyle
|
||||
|
||||
@@ -2700,7 +2691,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
@IntDef(
|
||||
PAN_LIMIT_INSIDE,
|
||||
PAN_LIMIT_OUTSIDE,
|
||||
PAN_LIMIT_CENTER
|
||||
PAN_LIMIT_CENTER,
|
||||
)
|
||||
annotation class PanLimit
|
||||
|
||||
@@ -2727,7 +2718,7 @@ open class SubsamplingScaleImageView @JvmOverloads constructor(
|
||||
SCALE_TYPE_FIT_HEIGHT,
|
||||
SCALE_TYPE_ORIGINAL_SIZE,
|
||||
SCALE_TYPE_SMART_FIT,
|
||||
SCALE_TYPE_CUSTOM
|
||||
SCALE_TYPE_CUSTOM,
|
||||
)
|
||||
annotation class ScaleType
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import kotlin.Throws
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import java.lang.Exception
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
@@ -50,4 +47,4 @@ interface Decoder {
|
||||
* resources still in use.
|
||||
*/
|
||||
fun recycle()
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,10 @@ import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import kotlin.Throws
|
||||
import com.davemorrissey.labs.subscaleview.provider.InputProvider
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import java.lang.Exception
|
||||
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
|
||||
}
|
||||
@@ -26,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()
|
||||
}
|
||||
@@ -61,4 +55,4 @@ class ImageDecoder @JvmOverloads constructor(
|
||||
override fun recycle() {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.davemorrissey.labs.subscaleview.provider
|
||||
|
||||
import android.content.Context
|
||||
import java.io.IOException
|
||||
|
||||
class AssetInputProvider(context: Context, val assetName: String) : InputProvider {
|
||||
private val assets = context.assets
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun openStream() = assets.open(assetName)
|
||||
}
|
||||
@@ -1,10 +1,3 @@
|
||||
package com.davemorrissey.labs.subscaleview.provider
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import kotlin.Throws
|
||||
|
||||
interface InputProvider {
|
||||
@Throws(IOException::class)
|
||||
fun openStream(): InputStream?
|
||||
}
|
||||
interface InputProvider
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.davemorrissey.labs.subscaleview.provider
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import kotlin.Throws
|
||||
|
||||
/**
|
||||
* Provider from an already open input stream.
|
||||
*
|
||||
*
|
||||
* Please keep in mind this provider is not reusable.
|
||||
*/
|
||||
class OpenStreamProvider(private val stream: InputStream) : InputProvider {
|
||||
@Throws(IOException::class)
|
||||
override fun openStream() = stream
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package com.davemorrissey.labs.subscaleview.provider
|
||||
|
||||
import android.content.Context
|
||||
import java.io.IOException
|
||||
|
||||
class ResourceInputProvider(context: Context, val resource: Int) : InputProvider {
|
||||
private val resources = context.resources
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun openStream() = resources.openRawResource(resource)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.davemorrissey.labs.subscaleview.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import java.io.IOException
|
||||
import kotlin.Throws
|
||||
|
||||
class UriInputProvider(context: Context, val uri: Uri) : InputProvider {
|
||||
private val resolver = context.contentResolver
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun openStream() = resolver.openInputStream(uri)
|
||||
}
|
||||
@@ -1,46 +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
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class UriArchiveAccessor(ctx: Context, uri: Uri) {
|
||||
val pfd by lazy { ctx.contentResolver.openFileDescriptor(uri, "r")!! }
|
||||
fun open(): Int {
|
||||
return openArchive(pfd.fd, pfd.statSize)
|
||||
}
|
||||
|
||||
private external fun openArchive(fd: Int, size: Long): Int
|
||||
external fun extractToByteBuffer(index: Int): ByteBuffer?
|
||||
external fun extractToFd(index: Int, fd: Int)
|
||||
external fun getFilename(index: Int): String
|
||||
external fun needPassword(): Boolean
|
||||
external fun providePassword(str: String): Boolean
|
||||
private external fun closeArchive()
|
||||
fun close() {
|
||||
closeArchive()
|
||||
pfd.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
external fun releaseByteBuffer(buffer: ByteBuffer)
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.hippo.app
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.HapticFeedbackConstants
|
||||
import com.google.android.material.slider.Slider
|
||||
|
||||
class HapticSlider @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : Slider(context, attrs) {
|
||||
init {
|
||||
addOnChangeListener { _, _, _ -> performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) }
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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.app
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.ListView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.yorozuya.ViewUtils
|
||||
|
||||
class ListCheckBoxDialogBuilder constructor(
|
||||
context: Context, items: Array<CharSequence>,
|
||||
listener: OnItemClickListener?, checkText: String?, checked: Boolean
|
||||
) : BaseDialogBuilder(
|
||||
context
|
||||
) {
|
||||
private val mCheckBox: CheckBox
|
||||
private var mDialog: AlertDialog? = null
|
||||
val isChecked: Boolean
|
||||
get() = mCheckBox.isChecked
|
||||
|
||||
override fun create(): AlertDialog {
|
||||
mDialog = super.create()
|
||||
return mDialog as AlertDialog
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(builder: ListCheckBoxDialogBuilder?, dialog: AlertDialog?, position: Int)
|
||||
}
|
||||
|
||||
init {
|
||||
val view =
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null)
|
||||
setView(view)
|
||||
val listView = ViewUtils.`$$`(view, R.id.list_view) as ListView
|
||||
mCheckBox = ViewUtils.`$$`(view, R.id.checkbox) as CheckBox
|
||||
listView.adapter = ArrayAdapter(getContext(), R.layout.item_select_dialog, items)
|
||||
listView.onItemClickListener =
|
||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long ->
|
||||
listener?.onItemClick(this@ListCheckBoxDialogBuilder, mDialog, position)
|
||||
mDialog?.dismiss()
|
||||
}
|
||||
mCheckBox.text = checkText
|
||||
mCheckBox.isChecked = checked
|
||||
}
|
||||
}
|
||||
@@ -1,985 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* 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.beerbelly;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A cache that uses a bounded amount of space on a filesystem. Each cache
|
||||
* entry has a string key and a fixed number of values. Each key must match
|
||||
* the regex <strong>[a-z0-9_-]{1,120}</strong>. Values are byte sequences,
|
||||
* accessible as streams or files. Each value must be between {@code 0} and
|
||||
* {@code Integer.MAX_VALUE} bytes in length.
|
||||
*
|
||||
* <p>The cache stores its data in a directory on the filesystem. This
|
||||
* directory must be exclusive to the cache; the cache may delete or overwrite
|
||||
* files from its directory. It is an error for multiple processes to use the
|
||||
* same cache directory at the same time.
|
||||
*
|
||||
* <p>This cache limits the number of bytes that it will store on the
|
||||
* filesystem. When the number of stored bytes exceeds the limit, the cache will
|
||||
* remove entries in the background until the limit is satisfied. The limit is
|
||||
* not strict: the cache may temporarily exceed it while waiting for files to be
|
||||
* deleted. The limit does not include filesystem overhead or the cache
|
||||
* journal so space-sensitive applications should set a conservative limit.
|
||||
*
|
||||
* <p>Clients call {@link #edit} to create or update the values of an entry. An
|
||||
* entry may have only one editor at one time; if a value is not available to be
|
||||
* edited then {@link #edit} will return null.
|
||||
* <ul>
|
||||
* <li>When an entry is being <strong>created</strong> it is necessary to
|
||||
* supply a full set of values; the empty value should be used as a
|
||||
* placeholder if necessary.
|
||||
* <li>When an entry is being <strong>edited</strong>, it is not necessary
|
||||
* to supply data for every value; values default to their previous
|
||||
* value.
|
||||
* </ul>
|
||||
* Every {@link #edit} call must be matched by a call to {@link Editor#commit}
|
||||
* or {@link Editor#abort}. Committing is atomic: a read observes the full set
|
||||
* of values as they were before or after the commit, but never a mix of values.
|
||||
*
|
||||
* <p>Clients call {@link #get} to read a snapshot of an entry. The read will
|
||||
* observe the value at the time that {@link #get} was called. Updates and
|
||||
* removals after the call do not impact ongoing reads.
|
||||
*
|
||||
* <p>This class is tolerant of some I/O errors. If files are missing from the
|
||||
* filesystem, the corresponding entries will be dropped from the cache. If
|
||||
* an error occurs while writing a cache value, the edit will fail silently.
|
||||
* Callers should handle other problems by catching {@code IOException} and
|
||||
* responding appropriately.
|
||||
*/
|
||||
public final class DiskLruCache implements Closeable {
|
||||
static final String JOURNAL_FILE = "journal";
|
||||
static final String JOURNAL_FILE_TEMP = "journal.tmp";
|
||||
static final String JOURNAL_FILE_BACKUP = "journal.bkp";
|
||||
static final String MAGIC = "libcore.io.DiskLruCache";
|
||||
static final String VERSION_1 = "1";
|
||||
static final long ANY_SEQUENCE_NUMBER = -1;
|
||||
static final String STRING_KEY_PATTERN = "[a-z0-9_-]{1,120}";
|
||||
static final Pattern LEGAL_KEY_PATTERN = Pattern.compile(STRING_KEY_PATTERN);
|
||||
private static final String CLEAN = "CLEAN";
|
||||
private static final String DIRTY = "DIRTY";
|
||||
private static final String REMOVE = "REMOVE";
|
||||
private static final String READ = "READ";
|
||||
|
||||
/*
|
||||
* This cache uses a journal file named "journal". A typical journal file
|
||||
* looks like this:
|
||||
* libcore.io.DiskLruCache
|
||||
* 1
|
||||
* 100
|
||||
* 2
|
||||
*
|
||||
* CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
|
||||
* DIRTY 335c4c6028171cfddfbaae1a9c313c52
|
||||
* CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
|
||||
* REMOVE 335c4c6028171cfddfbaae1a9c313c52
|
||||
* DIRTY 1ab96a171faeeee38496d8b330771a7a
|
||||
* CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
|
||||
* READ 335c4c6028171cfddfbaae1a9c313c52
|
||||
* READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
|
||||
*
|
||||
* The first five lines of the journal form its header. They are the
|
||||
* constant string "libcore.io.DiskLruCache", the disk cache's version,
|
||||
* the application's version, the value count, and a blank line.
|
||||
*
|
||||
* Each of the subsequent lines in the file is a record of the state of a
|
||||
* cache entry. Each line contains space-separated values: a state, a key,
|
||||
* and optional state-specific values.
|
||||
* o DIRTY lines track that an entry is actively being created or updated.
|
||||
* Every successful DIRTY action should be followed by a CLEAN or REMOVE
|
||||
* action. DIRTY lines without a matching CLEAN or REMOVE indicate that
|
||||
* temporary files may need to be deleted.
|
||||
* o CLEAN lines track a cache entry that has been successfully published
|
||||
* and may be read. A publish line is followed by the lengths of each of
|
||||
* its values.
|
||||
* o READ lines track accesses for LRU.
|
||||
* o REMOVE lines track entries that have been deleted.
|
||||
*
|
||||
* The journal file is appended to as cache operations occur. The journal may
|
||||
* occasionally be compacted by dropping redundant lines. A temporary file named
|
||||
* "journal.tmp" will be used during compaction; that file should be deleted if
|
||||
* it exists when the cache is opened.
|
||||
*/
|
||||
private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) {
|
||||
// Eat all writes silently. Nom nom.
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This cache uses a single background thread to evict entries.
|
||||
*/
|
||||
final ThreadPoolExecutor executorService =
|
||||
new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
||||
private final File directory;
|
||||
private final File journalFile;
|
||||
private final File journalFileTmp;
|
||||
private final File journalFileBackup;
|
||||
private final int appVersion;
|
||||
private final int valueCount;
|
||||
private final LinkedHashMap<String, Entry> lruEntries =
|
||||
new LinkedHashMap<>(0, 0.75f, true);
|
||||
private long maxSize;
|
||||
private long size = 0;
|
||||
private Writer journalWriter;
|
||||
private int redundantOpCount;
|
||||
/**
|
||||
* To differentiate between old and current snapshots, each entry is given
|
||||
* a sequence number each time an edit is committed. A snapshot is stale if
|
||||
* its sequence number is not equal to its entry's sequence number.
|
||||
*/
|
||||
private long nextSequenceNumber = 0;
|
||||
|
||||
private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
|
||||
this.directory = directory;
|
||||
this.appVersion = appVersion;
|
||||
this.journalFile = new File(directory, JOURNAL_FILE);
|
||||
this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
|
||||
this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
|
||||
this.valueCount = valueCount;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the cache in {@code directory}, creating a cache if none exists
|
||||
* there.
|
||||
*
|
||||
* @param directory a writable directory
|
||||
* @param valueCount the number of values per cache entry. Must be positive.
|
||||
* @param maxSize the maximum number of bytes this cache should use to store
|
||||
* @throws IOException if reading or writing the cache directory fails
|
||||
*/
|
||||
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
|
||||
throws IOException {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("maxSize <= 0");
|
||||
}
|
||||
if (valueCount <= 0) {
|
||||
throw new IllegalArgumentException("valueCount <= 0");
|
||||
}
|
||||
|
||||
// If a bkp file exists, use it instead.
|
||||
File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
|
||||
if (backupFile.exists()) {
|
||||
File journalFile = new File(directory, JOURNAL_FILE);
|
||||
// If journal file also exists just delete backup file.
|
||||
if (journalFile.exists()) {
|
||||
backupFile.delete();
|
||||
} else {
|
||||
renameTo(backupFile, journalFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer to pick up where we left off.
|
||||
DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
|
||||
if (cache.journalFile.exists()) {
|
||||
try {
|
||||
cache.readJournal();
|
||||
cache.processJournal();
|
||||
return cache;
|
||||
} catch (IOException journalIsCorrupt) {
|
||||
System.out
|
||||
.println("DiskLruCache "
|
||||
+ directory
|
||||
+ " is corrupt: "
|
||||
+ journalIsCorrupt.getMessage()
|
||||
+ ", removing");
|
||||
cache.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new empty cache.
|
||||
directory.mkdirs();
|
||||
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
|
||||
cache.rebuildJournal();
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static void deleteIfExists(File file) throws IOException {
|
||||
if (file.exists() && !file.delete()) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {
|
||||
if (deleteDestination) {
|
||||
deleteIfExists(to);
|
||||
}
|
||||
if (!from.renameTo(to)) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
private static String inputStreamToString(InputStream in) throws IOException {
|
||||
return Util.readFully(new InputStreamReader(in, Util.UTF_8));
|
||||
}
|
||||
|
||||
private void readJournal() throws IOException {
|
||||
StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
|
||||
try {
|
||||
String magic = reader.readLine();
|
||||
String version = reader.readLine();
|
||||
String appVersionString = reader.readLine();
|
||||
String valueCountString = reader.readLine();
|
||||
String blank = reader.readLine();
|
||||
if (!MAGIC.equals(magic)
|
||||
|| !VERSION_1.equals(version)
|
||||
|| !Integer.toString(appVersion).equals(appVersionString)
|
||||
|| !Integer.toString(valueCount).equals(valueCountString)
|
||||
|| !"".equals(blank)) {
|
||||
throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
|
||||
+ valueCountString + ", " + blank + "]");
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
readJournalLine(reader.readLine());
|
||||
lineCount++;
|
||||
} catch (EOFException endOfJournal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
redundantOpCount = lineCount - lruEntries.size();
|
||||
|
||||
// If we ended on a truncated line, rebuild the journal before appending to it.
|
||||
if (reader.hasUnterminatedLine()) {
|
||||
rebuildJournal();
|
||||
} else {
|
||||
journalWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(journalFile, true), Util.US_ASCII));
|
||||
}
|
||||
} finally {
|
||||
Util.closeQuietly(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private void readJournalLine(String line) throws IOException {
|
||||
int firstSpace = line.indexOf(' ');
|
||||
if (firstSpace == -1) {
|
||||
throw new IOException("unexpected journal line: " + line);
|
||||
}
|
||||
|
||||
int keyBegin = firstSpace + 1;
|
||||
int secondSpace = line.indexOf(' ', keyBegin);
|
||||
final String key;
|
||||
if (secondSpace == -1) {
|
||||
key = line.substring(keyBegin);
|
||||
if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
|
||||
lruEntries.remove(key);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
key = line.substring(keyBegin, secondSpace);
|
||||
}
|
||||
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null) {
|
||||
entry = new Entry(key);
|
||||
lruEntries.put(key, entry);
|
||||
}
|
||||
|
||||
if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
|
||||
String[] parts = line.substring(secondSpace + 1).split(" ");
|
||||
entry.readable = true;
|
||||
entry.currentEditor = null;
|
||||
entry.setLengths(parts);
|
||||
} else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
|
||||
entry.currentEditor = new Editor(entry);
|
||||
} else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {
|
||||
// This work was already done by calling lruEntries.get().
|
||||
} else {
|
||||
throw new IOException("unexpected journal line: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the initial size and collects garbage as a part of opening the
|
||||
* cache. Dirty entries are assumed to be inconsistent and will be deleted.
|
||||
*/
|
||||
private void processJournal() throws IOException {
|
||||
deleteIfExists(journalFileTmp);
|
||||
for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
|
||||
Entry entry = i.next();
|
||||
if (entry.currentEditor == null) {
|
||||
for (int t = 0; t < valueCount; t++) {
|
||||
size += entry.lengths[t];
|
||||
}
|
||||
} else {
|
||||
entry.currentEditor = null;
|
||||
for (int t = 0; t < valueCount; t++) {
|
||||
deleteIfExists(entry.getCleanFile(t));
|
||||
deleteIfExists(entry.getDirtyFile(t));
|
||||
}
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
} private final Callable<Void> cleanupCallable = new Callable<>() {
|
||||
public Void call() throws Exception {
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (journalWriter == null) {
|
||||
return null; // Closed.
|
||||
}
|
||||
trimToSize();
|
||||
if (journalRebuildRequired()) {
|
||||
rebuildJournal();
|
||||
redundantOpCount = 0;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new journal that omits redundant information. This replaces the
|
||||
* current journal if it exists.
|
||||
*/
|
||||
private synchronized void rebuildJournal() throws IOException {
|
||||
if (journalWriter != null) {
|
||||
journalWriter.close();
|
||||
}
|
||||
|
||||
try (Writer writer = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII))) {
|
||||
writer.write(MAGIC);
|
||||
writer.write("\n");
|
||||
writer.write(VERSION_1);
|
||||
writer.write("\n");
|
||||
writer.write(Integer.toString(appVersion));
|
||||
writer.write("\n");
|
||||
writer.write(Integer.toString(valueCount));
|
||||
writer.write("\n");
|
||||
writer.write("\n");
|
||||
|
||||
for (Entry entry : lruEntries.values()) {
|
||||
if (entry.currentEditor != null) {
|
||||
writer.write(DIRTY + ' ' + entry.key + '\n');
|
||||
} else {
|
||||
writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (journalFile.exists()) {
|
||||
renameTo(journalFile, journalFileBackup, true);
|
||||
}
|
||||
renameTo(journalFileTmp, journalFile, false);
|
||||
journalFileBackup.delete();
|
||||
|
||||
journalWriter = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if it contains the key
|
||||
*/
|
||||
public synchronized boolean contain(String key) {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
return entry != null && entry.readable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the entry named {@code key}, or null if it doesn't
|
||||
* exist is not currently readable. If a value is returned, it is moved to
|
||||
* the head of the LRU queue.
|
||||
*/
|
||||
public synchronized Snapshot get(String key) throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!entry.readable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Open all streams eagerly to guarantee that we see a single published
|
||||
// snapshot. If we opened streams lazily then the streams could come
|
||||
// from different edits.
|
||||
InputStream[] ins = new InputStream[valueCount];
|
||||
try {
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
ins[i] = new FileInputStream(entry.getCleanFile(i));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// A file must have been deleted manually!
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
if (ins[i] != null) {
|
||||
Util.closeQuietly(ins[i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
journalWriter.append(READ + ' ').append(key).append(String.valueOf('\n'));
|
||||
if (journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an editor for the entry named {@code key}, or null if another
|
||||
* edit is in progress.
|
||||
*/
|
||||
public Editor edit(String key) throws IOException {
|
||||
return edit(key, ANY_SEQUENCE_NUMBER);
|
||||
}
|
||||
|
||||
private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
|
||||
|| entry.sequenceNumber != expectedSequenceNumber)) {
|
||||
return null; // Snapshot is stale.
|
||||
}
|
||||
if (entry == null) {
|
||||
entry = new Entry(key);
|
||||
lruEntries.put(key, entry);
|
||||
} else if (entry.currentEditor != null) {
|
||||
return null; // Another edit is in progress.
|
||||
}
|
||||
|
||||
Editor editor = new Editor(entry);
|
||||
entry.currentEditor = editor;
|
||||
|
||||
// Flush the journal before creating files to prevent file leaks.
|
||||
journalWriter.write(DIRTY + ' ' + key + '\n');
|
||||
journalWriter.flush();
|
||||
return editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where this cache stores its data.
|
||||
*/
|
||||
public File getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of bytes that this cache should use to store
|
||||
* its data.
|
||||
*/
|
||||
public synchronized long getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the maximum number of bytes the cache can store and queues a job
|
||||
* to trim the existing store, if necessary.
|
||||
*/
|
||||
public synchronized void setMaxSize(long maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes currently being used to store the values in
|
||||
* this cache. This may be greater than the max size if a background
|
||||
* deletion is pending.
|
||||
*/
|
||||
public synchronized long size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
|
||||
Entry entry = editor.entry;
|
||||
if (entry.currentEditor != editor) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
// If this edit is creating the entry for the first time, every index must have a value.
|
||||
if (success && !entry.readable) {
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
if (!editor.written[i]) {
|
||||
editor.abort();
|
||||
throw new IllegalStateException("Newly created entry didn't create value for index " + i);
|
||||
}
|
||||
if (!entry.getDirtyFile(i).exists()) {
|
||||
editor.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
File dirty = entry.getDirtyFile(i);
|
||||
if (success) {
|
||||
if (dirty.exists()) {
|
||||
File clean = entry.getCleanFile(i);
|
||||
dirty.renameTo(clean);
|
||||
long oldLength = entry.lengths[i];
|
||||
long newLength = clean.length();
|
||||
entry.lengths[i] = newLength;
|
||||
size = size - oldLength + newLength;
|
||||
}
|
||||
} else {
|
||||
deleteIfExists(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
entry.currentEditor = null;
|
||||
if (entry.readable | success) {
|
||||
entry.readable = true;
|
||||
journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
|
||||
if (success) {
|
||||
entry.sequenceNumber = nextSequenceNumber++;
|
||||
}
|
||||
} else {
|
||||
lruEntries.remove(entry.key);
|
||||
journalWriter.write(REMOVE + ' ' + entry.key + '\n');
|
||||
}
|
||||
journalWriter.flush();
|
||||
|
||||
if (size > maxSize || journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We only rebuild the journal when it will halve the size of the journal
|
||||
* and eliminate at least 2000 ops.
|
||||
*/
|
||||
private boolean journalRebuildRequired() {
|
||||
final int redundantOpCompactThreshold = 2000;
|
||||
return redundantOpCount >= redundantOpCompactThreshold //
|
||||
&& redundantOpCount >= lruEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the entry for {@code key} if it exists and can be removed. Entries
|
||||
* actively being edited cannot be removed.
|
||||
*
|
||||
* @return true if an entry was removed.
|
||||
*/
|
||||
public synchronized boolean remove(String key) throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null || entry.currentEditor != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
File file = entry.getCleanFile(i);
|
||||
if (file.exists() && !file.delete()) {
|
||||
throw new IOException("failed to delete " + file);
|
||||
}
|
||||
size -= entry.lengths[i];
|
||||
entry.lengths[i] = 0;
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
journalWriter.append(REMOVE + ' ').append(key).append(String.valueOf('\n'));
|
||||
lruEntries.remove(key);
|
||||
|
||||
if (journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this cache has been closed.
|
||||
*/
|
||||
public synchronized boolean isClosed() {
|
||||
return journalWriter == null;
|
||||
}
|
||||
|
||||
private void checkNotClosed() {
|
||||
if (journalWriter == null) {
|
||||
throw new IllegalStateException("cache is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force buffered operations to the filesystem.
|
||||
*/
|
||||
public synchronized void flush() throws IOException {
|
||||
checkNotClosed();
|
||||
trimToSize();
|
||||
journalWriter.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this cache. Stored values will remain on the filesystem.
|
||||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
if (journalWriter == null) {
|
||||
return; // Already closed.
|
||||
}
|
||||
for (Entry entry : new ArrayList<>(lruEntries.values())) {
|
||||
if (entry.currentEditor != null) {
|
||||
entry.currentEditor.abort();
|
||||
}
|
||||
}
|
||||
trimToSize();
|
||||
journalWriter.close();
|
||||
journalWriter = null;
|
||||
}
|
||||
|
||||
private void trimToSize() throws IOException {
|
||||
while (size > maxSize) {
|
||||
Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
|
||||
remove(toEvict.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the cache and deletes all of its stored values. This will delete
|
||||
* all files in the cache directory including files that weren't created by
|
||||
* the cache.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
close();
|
||||
Util.deleteContents(directory);
|
||||
}
|
||||
|
||||
private void validateKey(String key) {
|
||||
Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("keys must match regex "
|
||||
+ STRING_KEY_PATTERN + ": \"" + key + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A snapshot of the values for an entry.
|
||||
*/
|
||||
public final class Snapshot implements Closeable {
|
||||
private final String key;
|
||||
private final long sequenceNumber;
|
||||
private final InputStream[] ins;
|
||||
private final long[] lengths;
|
||||
|
||||
private Snapshot(String key, long sequenceNumber, InputStream[] ins, long[] lengths) {
|
||||
this.key = key;
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.ins = ins;
|
||||
this.lengths = lengths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an editor for this snapshot's entry, or null if either the
|
||||
* entry has changed since this snapshot was created or if another edit
|
||||
* is in progress.
|
||||
*/
|
||||
public Editor edit() throws IOException {
|
||||
return DiskLruCache.this.edit(key, sequenceNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unbuffered stream with the value for {@code index}.
|
||||
*/
|
||||
public InputStream getInputStream(int index) {
|
||||
return ins[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value for {@code index}.
|
||||
*/
|
||||
public String getString(int index) throws IOException {
|
||||
return inputStreamToString(getInputStream(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte length of the value for {@code index}.
|
||||
*/
|
||||
public long getLength(int index) {
|
||||
return lengths[index];
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (InputStream in : ins) {
|
||||
Util.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the values for an entry.
|
||||
*/
|
||||
public final class Editor {
|
||||
private final Entry entry;
|
||||
private final boolean[] written;
|
||||
private boolean hasErrors;
|
||||
private boolean committed;
|
||||
|
||||
private Editor(Entry entry) {
|
||||
this.entry = entry;
|
||||
this.written = (entry.readable) ? null : new boolean[valueCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unbuffered input stream to read the last committed value,
|
||||
* or null if no value has been committed.
|
||||
*/
|
||||
public InputStream newInputStream(int index) {
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (entry.currentEditor != this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!entry.readable) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new FileInputStream(entry.getCleanFile(index));
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last committed value as a string, or null if no value
|
||||
* has been committed.
|
||||
*/
|
||||
public String getString(int index) throws IOException {
|
||||
InputStream in = newInputStream(index);
|
||||
return in != null ? inputStreamToString(in) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new unbuffered output stream to write the value at
|
||||
* {@code index}. If the underlying output stream encounters errors
|
||||
* when writing to the filesystem, this edit will be aborted when
|
||||
* {@link #commit} is called. The returned output stream does not throw
|
||||
* IOExceptions.
|
||||
*/
|
||||
public OutputStream newOutputStream(int index) throws IOException {
|
||||
if (index < 0 || index >= valueCount) {
|
||||
throw new IllegalArgumentException("Expected index " + index + " to "
|
||||
+ "be greater than 0 and less than the maximum value count "
|
||||
+ "of " + valueCount);
|
||||
}
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (entry.currentEditor != this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!entry.readable) {
|
||||
written[index] = true;
|
||||
}
|
||||
File dirtyFile = entry.getDirtyFile(index);
|
||||
FileOutputStream outputStream;
|
||||
try {
|
||||
outputStream = new FileOutputStream(dirtyFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
// Attempt to recreate the cache directory.
|
||||
directory.mkdirs();
|
||||
try {
|
||||
outputStream = new FileOutputStream(dirtyFile);
|
||||
} catch (FileNotFoundException e2) {
|
||||
// We are unable to recover. Silently eat the writes.
|
||||
return NULL_OUTPUT_STREAM;
|
||||
}
|
||||
}
|
||||
return new FaultHidingOutputStream(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value at {@code index} to {@code value}.
|
||||
*/
|
||||
public void set(int index, String value) throws IOException {
|
||||
Writer writer = null;
|
||||
try {
|
||||
writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8);
|
||||
writer.write(value);
|
||||
} finally {
|
||||
Util.closeQuietly(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits this edit so it is visible to readers. This releases the
|
||||
* edit lock so another edit may be started on the same key.
|
||||
*/
|
||||
public void commit() throws IOException {
|
||||
if (hasErrors) {
|
||||
completeEdit(this, false);
|
||||
remove(entry.key); // The previous entry is stale.
|
||||
} else {
|
||||
completeEdit(this, true);
|
||||
}
|
||||
committed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts this edit. This releases the edit lock so another edit may be
|
||||
* started on the same key.
|
||||
*/
|
||||
public void abort() throws IOException {
|
||||
completeEdit(this, false);
|
||||
}
|
||||
|
||||
public void abortUnlessCommitted() {
|
||||
if (!committed) {
|
||||
try {
|
||||
abort();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FaultHidingOutputStream extends FilterOutputStream {
|
||||
private FaultHidingOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) {
|
||||
try {
|
||||
out.write(oneByte);
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) {
|
||||
try {
|
||||
out.write(buffer, offset, length);
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class Entry {
|
||||
private final String key;
|
||||
|
||||
/**
|
||||
* Lengths of this entry's files.
|
||||
*/
|
||||
private final long[] lengths;
|
||||
|
||||
/**
|
||||
* True if this entry has ever been published.
|
||||
*/
|
||||
private boolean readable;
|
||||
|
||||
/**
|
||||
* The ongoing edit or null if this entry is not being edited.
|
||||
*/
|
||||
private Editor currentEditor;
|
||||
|
||||
/**
|
||||
* The sequence number of the most recently committed edit to this entry.
|
||||
*/
|
||||
private long sequenceNumber;
|
||||
|
||||
private Entry(String key) {
|
||||
this.key = key;
|
||||
this.lengths = new long[valueCount];
|
||||
}
|
||||
|
||||
public String getLengths() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (long size : lengths) {
|
||||
result.append(' ').append(size);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lengths using decimal numbers like "10123".
|
||||
*/
|
||||
private void setLengths(String[] strings) throws IOException {
|
||||
if (strings.length != valueCount) {
|
||||
throw invalidLengths(strings);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
lengths[i] = Long.parseLong(strings[i]);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw invalidLengths(strings);
|
||||
}
|
||||
}
|
||||
|
||||
private IOException invalidLengths(String[] strings) throws IOException {
|
||||
throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings));
|
||||
}
|
||||
|
||||
public File getCleanFile(int i) {
|
||||
return new File(directory, key + "." + i);
|
||||
}
|
||||
|
||||
public File getDirtyFile(int i) {
|
||||
return new File(directory, key + "." + i + ".tmp");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,504 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.beerbelly;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
import com.hippo.streampipe.OutputStreamPipe;
|
||||
import com.hippo.util.HashCodeUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class SimpleDiskCache {
|
||||
|
||||
private static final String TAG = SimpleDiskCache.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Disk cache is free
|
||||
*/
|
||||
private static final int STATE_DISK_CACHE_NONE = 0;
|
||||
|
||||
/**
|
||||
* Disk cache is flushing or clearing
|
||||
*/
|
||||
private static final int STATE_DISK_CACHE_IN_USE = 1;
|
||||
|
||||
/**
|
||||
* Disk cache is reading or writing
|
||||
*/
|
||||
private static final int STATE_DISK_CACHE_BUSY = 2;
|
||||
|
||||
private final File mCacheDir;
|
||||
private final int mSize;
|
||||
@NonNull
|
||||
private final Map<String, CounterLock> mLockMap = new HashMap<>();
|
||||
@NonNull
|
||||
private final LockPool mLockPool = new LockPool();
|
||||
private int mDiskCacheState;
|
||||
@Nullable
|
||||
private DiskLruCache mDiskLruCache;
|
||||
|
||||
public SimpleDiskCache(File cacheDir, int size) {
|
||||
mCacheDir = cacheDir;
|
||||
mSize = size;
|
||||
|
||||
try {
|
||||
init();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hashing method that changes a string (like a URL) into a hash suitable
|
||||
* for using as a disk filename.
|
||||
*
|
||||
* @param key The key used to store the file
|
||||
*/
|
||||
private static String hashKeyForDisk(final String key) {
|
||||
String cacheKey;
|
||||
try {
|
||||
final MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
digest.update(key.getBytes());
|
||||
cacheKey = HashCodeUtils.bytesToHexString(digest.digest());
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
cacheKey = String.valueOf(key.hashCode());
|
||||
}
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
public synchronized boolean isValid() {
|
||||
return mDiskLruCache != null;
|
||||
}
|
||||
|
||||
private synchronized void init() throws IOException {
|
||||
if (!isValid()) {
|
||||
mDiskLruCache = DiskLruCache.open(mCacheDir, 1, 1, mSize);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized long size() {
|
||||
if (null != mDiskLruCache) {
|
||||
return mDiskLruCache.size();
|
||||
} else {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void flush() {
|
||||
// Wait for cache available
|
||||
while (mDiskCacheState != STATE_DISK_CACHE_NONE) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
mDiskCacheState = STATE_DISK_CACHE_BUSY;
|
||||
|
||||
if (null != mDiskLruCache) {
|
||||
try {
|
||||
mDiskLruCache.flush();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "SimpleDiskCache flush", e);
|
||||
}
|
||||
}
|
||||
|
||||
mDiskCacheState = STATE_DISK_CACHE_NONE;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
public synchronized boolean clear() {
|
||||
boolean result = true;
|
||||
if (null == mDiskLruCache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for cache available
|
||||
while (mDiskCacheState != STATE_DISK_CACHE_NONE) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
mDiskCacheState = STATE_DISK_CACHE_BUSY;
|
||||
|
||||
try {
|
||||
mDiskLruCache.delete();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "SimpleDiskCache clearCache", e);
|
||||
}
|
||||
mDiskLruCache = null;
|
||||
try {
|
||||
init();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "SimpleDiskCache init", e);
|
||||
result = false;
|
||||
}
|
||||
|
||||
mDiskCacheState = STATE_DISK_CACHE_NONE;
|
||||
notifyAll();
|
||||
return result;
|
||||
}
|
||||
|
||||
private synchronized CounterLock obtainLock(String key) {
|
||||
// Wait for clear over
|
||||
while (mDiskCacheState == STATE_DISK_CACHE_BUSY) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
mDiskCacheState = STATE_DISK_CACHE_IN_USE;
|
||||
|
||||
// Get lock from key
|
||||
CounterLock lock;
|
||||
lock = mLockMap.get(key);
|
||||
if (lock == null) {
|
||||
lock = mLockPool.get();
|
||||
mLockMap.put(key, lock);
|
||||
}
|
||||
lock.obtain();
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
private synchronized void releaseLock(String key, CounterLock lock) {
|
||||
lock.release();
|
||||
if (lock.isFree()) {
|
||||
mLockMap.remove(key);
|
||||
mLockPool.push(lock);
|
||||
}
|
||||
|
||||
if (mLockMap.isEmpty()) {
|
||||
mDiskCacheState = STATE_DISK_CACHE_NONE;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contain(@NonNull String key) {
|
||||
String diskKey = hashKeyForDisk(key);
|
||||
CounterLock lock = obtainLock(diskKey);
|
||||
if (null == mDiskLruCache) {
|
||||
releaseLock(diskKey, lock);
|
||||
return false;
|
||||
}
|
||||
lock.readLock().lock();
|
||||
|
||||
boolean result = mDiskLruCache.contain(diskKey);
|
||||
|
||||
lock.readLock().unlock();
|
||||
releaseLock(diskKey, lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean remove(@NonNull String key) {
|
||||
String diskKey = hashKeyForDisk(key);
|
||||
CounterLock lock = obtainLock(diskKey);
|
||||
if (null == mDiskLruCache) {
|
||||
releaseLock(diskKey, lock);
|
||||
return false;
|
||||
}
|
||||
lock.writeLock().lock();
|
||||
|
||||
boolean result;
|
||||
try {
|
||||
result = mDiskLruCache.remove(diskKey);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
result = false;
|
||||
}
|
||||
|
||||
lock.writeLock().unlock();
|
||||
releaseLock(diskKey, lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean put(@NonNull String key, @NonNull InputStream is) {
|
||||
String diskKey = hashKeyForDisk(key);
|
||||
CounterLock lock = obtainLock(diskKey);
|
||||
if (null == mDiskLruCache) {
|
||||
releaseLock(diskKey, lock);
|
||||
return false;
|
||||
}
|
||||
lock.writeLock().lock();
|
||||
|
||||
boolean result = false;
|
||||
if (isValid()) {
|
||||
result = putToDisk(diskKey, is);
|
||||
}
|
||||
|
||||
lock.writeLock().unlock();
|
||||
releaseLock(diskKey, lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean putToDisk(String key, InputStream is) {
|
||||
DiskLruCache.Editor editor = null;
|
||||
OutputStream os = null;
|
||||
boolean completeEdit = false;
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
editor = mDiskLruCache.edit(key);
|
||||
if (editor == null) {
|
||||
// The editor is in progress
|
||||
return false;
|
||||
}
|
||||
|
||||
os = editor.newOutputStream(0);
|
||||
if (os != null) {
|
||||
Util.copy(is, os);
|
||||
completeEdit = true;
|
||||
editor.commit();
|
||||
return true;
|
||||
} else {
|
||||
// Can't get OutputStream
|
||||
completeEdit = true;
|
||||
editor.abort();
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Util.closeQuietly(os);
|
||||
try {
|
||||
if (!completeEdit && editor != null) {
|
||||
editor.abort();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key the key of the target
|
||||
* @return the InputStreamPipe, <code>null</code> for missing
|
||||
*/
|
||||
@Nullable
|
||||
public InputStreamPipe getInputStreamPipe(@NonNull String key) {
|
||||
if (contain(key)) {
|
||||
String diskKey = hashKeyForDisk(key);
|
||||
return new CacheInputStreamPipe(diskKey);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public OutputStreamPipe getOutputStreamPipe(@NonNull String key) {
|
||||
String diskKey = hashKeyForDisk(key);
|
||||
return new CacheOutputStreamPipe(diskKey);
|
||||
}
|
||||
|
||||
private class CacheInputStreamPipe implements InputStreamPipe {
|
||||
|
||||
private final String mKey;
|
||||
private CounterLock mLock;
|
||||
private DiskLruCache.Snapshot mCurrentSnapshot;
|
||||
|
||||
private CacheInputStreamPipe(String key) {
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void obtain() {
|
||||
if (mLock == null) {
|
||||
mLock = obtainLock(mKey);
|
||||
mLock.readLock().lock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (mCurrentSnapshot != null) {
|
||||
throw new IllegalStateException("Please close it first");
|
||||
}
|
||||
|
||||
if (mLock != null) {
|
||||
mLock.readLock().unlock();
|
||||
releaseLock(mKey, mLock);
|
||||
mLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull
|
||||
InputStream open() throws IOException {
|
||||
if (mLock == null) {
|
||||
throw new IllegalStateException("Please obtain it first");
|
||||
}
|
||||
if (mCurrentSnapshot != null) {
|
||||
throw new IllegalStateException("Please close it before reopen");
|
||||
}
|
||||
if (null == mDiskLruCache) {
|
||||
throw new IOException("Can't find disk lru cache");
|
||||
}
|
||||
|
||||
DiskLruCache.Snapshot snapshot;
|
||||
snapshot = mDiskLruCache.get(mKey);
|
||||
if (snapshot == null) {
|
||||
throw new IOException("Miss the key " + mKey);
|
||||
}
|
||||
mCurrentSnapshot = snapshot;
|
||||
return snapshot.getInputStream(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Util.closeQuietly(mCurrentSnapshot);
|
||||
mCurrentSnapshot = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheOutputStreamPipe implements OutputStreamPipe {
|
||||
|
||||
private final String mKey;
|
||||
private CounterLock mLock;
|
||||
private DiskLruCache.Editor mCurrentEditor;
|
||||
|
||||
private CacheOutputStreamPipe(String key) {
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void obtain() {
|
||||
if (mLock == null) {
|
||||
mLock = obtainLock(mKey);
|
||||
mLock.writeLock().lock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (mCurrentEditor != null) {
|
||||
throw new IllegalStateException("Please close it first");
|
||||
}
|
||||
|
||||
if (mLock != null) {
|
||||
mLock.writeLock().unlock();
|
||||
releaseLock(mKey, mLock);
|
||||
mLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull
|
||||
OutputStream open() throws IOException {
|
||||
if (mLock == null) {
|
||||
throw new IllegalStateException("Please obtain it first");
|
||||
}
|
||||
if (mCurrentEditor != null) {
|
||||
throw new IllegalStateException("Please close it before reopen");
|
||||
}
|
||||
if (null == mDiskLruCache) {
|
||||
throw new IOException("Can't find disk lru cache");
|
||||
}
|
||||
|
||||
DiskLruCache.Editor editor = mDiskLruCache.edit(mKey);
|
||||
if (editor == null) {
|
||||
throw new IOException("Miss the key " + mKey);
|
||||
}
|
||||
|
||||
OutputStream os;
|
||||
try {
|
||||
os = editor.newOutputStream(0);
|
||||
mCurrentEditor = editor;
|
||||
return os;
|
||||
} catch (IOException e) {
|
||||
// Get trouble
|
||||
try {
|
||||
editor.abort();
|
||||
} catch (IOException ex) {
|
||||
// Ignore
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (mCurrentEditor != null) {
|
||||
try {
|
||||
mCurrentEditor.commit();
|
||||
} catch (IOException e) {
|
||||
// Empty
|
||||
}
|
||||
mCurrentEditor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CounterLock extends ReentrantReadWriteLock {
|
||||
|
||||
private int mCount;
|
||||
|
||||
public boolean isFree() {
|
||||
return mCount == 0;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (--mCount < 0) {
|
||||
throw new IllegalStateException("Release time is more than occupy time");
|
||||
}
|
||||
}
|
||||
|
||||
public void obtain() {
|
||||
++mCount;
|
||||
}
|
||||
}
|
||||
|
||||
private class LockPool {
|
||||
|
||||
private static final int MAX_SIZE = 10;
|
||||
|
||||
private final CounterLock[] mArray = new CounterLock[MAX_SIZE];
|
||||
private final int mMaxSize = MAX_SIZE;
|
||||
private int mSize;
|
||||
|
||||
public void push(CounterLock lock) {
|
||||
if (lock != null && mSize < mMaxSize) {
|
||||
mArray[mSize++] = lock;
|
||||
}
|
||||
}
|
||||
|
||||
public CounterLock get() {
|
||||
if (mSize > 0) {
|
||||
CounterLock lock = mArray[--mSize];
|
||||
mArray[mSize] = null;
|
||||
return lock;
|
||||
} else {
|
||||
return new CounterLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* 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.beerbelly;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Buffers input from an {@link InputStream} for reading lines.
|
||||
*
|
||||
* <p>This class is used for buffered reading of lines. For purposes of this class, a line ends
|
||||
* with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
|
||||
* line at end of input is invalid and will be ignored, the caller may use {@code
|
||||
* hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
|
||||
*
|
||||
* <p>This class is intended for reading input that strictly consists of lines, such as line-based
|
||||
* cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
|
||||
* with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
|
||||
* end-of-input reporting and a more restrictive definition of a line.
|
||||
*
|
||||
* <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
|
||||
* and 10, respectively, and the representation of no other character contains these values.
|
||||
* We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
|
||||
* The default charset is US_ASCII.
|
||||
*/
|
||||
class StrictLineReader implements Closeable {
|
||||
private static final byte CR = (byte) '\r';
|
||||
private static final byte LF = (byte) '\n';
|
||||
|
||||
private final InputStream in;
|
||||
private final Charset charset;
|
||||
|
||||
/*
|
||||
* Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
|
||||
* and the data in the range [pos, end) is buffered for reading. At end of input, if there is
|
||||
* an unterminated line, we set end == -1, otherwise end == pos. If the underlying
|
||||
* {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
|
||||
*/
|
||||
private byte[] buf;
|
||||
private int pos;
|
||||
private int end;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code LineReader} with the specified charset and the default capacity.
|
||||
*
|
||||
* @param in the {@code InputStream} to read data from.
|
||||
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
|
||||
* supported.
|
||||
* @throws NullPointerException if {@code in} or {@code charset} is null.
|
||||
* @throws IllegalArgumentException if the specified charset is not supported.
|
||||
*/
|
||||
public StrictLineReader(InputStream in, Charset charset) {
|
||||
this(in, 8192, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code LineReader} with the specified capacity and charset.
|
||||
*
|
||||
* @param in the {@code InputStream} to read data from.
|
||||
* @param capacity the capacity of the buffer.
|
||||
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
|
||||
* supported.
|
||||
* @throws NullPointerException if {@code in} or {@code charset} is null.
|
||||
* @throws IllegalArgumentException if {@code capacity} is negative or zero
|
||||
* or the specified charset is not supported.
|
||||
*/
|
||||
public StrictLineReader(InputStream in, int capacity, Charset charset) {
|
||||
if (in == null || charset == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("capacity <= 0");
|
||||
}
|
||||
if (!(charset.equals(Util.US_ASCII))) {
|
||||
throw new IllegalArgumentException("Unsupported encoding");
|
||||
}
|
||||
|
||||
this.in = in;
|
||||
this.charset = charset;
|
||||
buf = new byte[capacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader by closing the underlying {@code InputStream} and
|
||||
* marking this reader as closed.
|
||||
*
|
||||
* @throws IOException for errors when closing the underlying {@code InputStream}.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (in) {
|
||||
if (buf != null) {
|
||||
buf = null;
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
|
||||
* this end of line marker is not included in the result.
|
||||
*
|
||||
* @return the next line from the input.
|
||||
* @throws IOException for underlying {@code InputStream} errors.
|
||||
* @throws EOFException for the end of source stream.
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
synchronized (in) {
|
||||
if (buf == null) {
|
||||
throw new IOException("LineReader is closed");
|
||||
}
|
||||
|
||||
// Read more data if we are at the end of the buffered data.
|
||||
// Though it's an error to read after an exception, we will let {@code fillBuf()}
|
||||
// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
|
||||
if (pos >= end) {
|
||||
fillBuf();
|
||||
}
|
||||
// Try to find LF in the buffered data and return the line if successful.
|
||||
for (int i = pos; i != end; ++i) {
|
||||
if (buf[i] == LF) {
|
||||
int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
|
||||
String res = new String(buf, pos, lineEnd - pos, charset);
|
||||
pos = i + 1;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's anticipate up to 80 characters on top of those already read.
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
|
||||
return new String(buf, 0, length, charset);
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
out.write(buf, pos, end - pos);
|
||||
// Mark unterminated line in case fillBuf throws EOFException or IOException.
|
||||
end = -1;
|
||||
fillBuf();
|
||||
// Try to find LF in the buffered data and return the line if successful.
|
||||
for (int i = pos; i != end; ++i) {
|
||||
if (buf[i] == LF) {
|
||||
if (i != pos) {
|
||||
out.write(buf, pos, i - pos);
|
||||
}
|
||||
pos = i + 1;
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasUnterminatedLine() {
|
||||
return end == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads new input data into the buffer. Call only with pos == end or end == -1,
|
||||
* depending on the desired outcome if the function throws.
|
||||
*/
|
||||
private void fillBuf() throws IOException {
|
||||
int result = in.read(buf, 0, buf.length);
|
||||
if (result == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
pos = 0;
|
||||
end = result;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* 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.beerbelly;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Junk drawer of utility methods.
|
||||
*/
|
||||
final class Util {
|
||||
static final Charset US_ASCII = StandardCharsets.US_ASCII;
|
||||
static final Charset UTF_8 = StandardCharsets.UTF_8;
|
||||
private static final int EOF = -1;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
static String readFully(Reader reader) throws IOException {
|
||||
try (reader) {
|
||||
StringWriter writer = new StringWriter();
|
||||
char[] buffer = new char[1024];
|
||||
int count;
|
||||
while ((count = reader.read(buffer)) != -1) {
|
||||
writer.write(buffer, 0, count);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the contents of {@code dir}. Throws an IOException if any file
|
||||
* could not be deleted, or if {@code dir} is not a readable directory.
|
||||
*/
|
||||
static void deleteContents(File dir) throws IOException {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IOException("not a readable directory: " + dir);
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteContents(file);
|
||||
}
|
||||
if (!file.delete()) {
|
||||
throw new IOException("failed to delete file: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void closeQuietly(/*Auto*/Closeable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (RuntimeException rethrown) {
|
||||
throw rethrown;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy bytes from an <code>InputStream</code> to an
|
||||
* <code>OutputStream</code>.
|
||||
*
|
||||
* @param input the InputStream
|
||||
* @param output the OutputStream
|
||||
* @return the number of bytes copied
|
||||
*/
|
||||
public static long copy(InputStream input, OutputStream output)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
long count = 0;
|
||||
int n;
|
||||
while (EOF != (n = input.read(buffer))) {
|
||||
output.write(buffer, 0, n);
|
||||
count += n;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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.database
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import android.util.SparseArray
|
||||
|
||||
class MSQLiteBuilder {
|
||||
companion object {
|
||||
const val COLUMN_ID = "_id"
|
||||
private val JAVA_TYPE_TO_SQLITE_TYPE: MutableMap<Class<*>?, String> = HashMap()
|
||||
private fun javaTypeToSQLiteType(clazz: Class<*>?): String {
|
||||
return JAVA_TYPE_TO_SQLITE_TYPE[clazz]
|
||||
?: throw IllegalStateException("Unknown type: $clazz")
|
||||
}
|
||||
|
||||
init {
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Boolean::class.javaPrimitiveType] =
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Byte::class.javaPrimitiveType] =
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Short::class.javaPrimitiveType] =
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Int::class.javaPrimitiveType] = "INTEGER NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Long::class.javaPrimitiveType] =
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Float::class.javaPrimitiveType] =
|
||||
"REAL NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[Double::class.javaPrimitiveType] = "REAL NOT NULL DEFAULT 0"
|
||||
JAVA_TYPE_TO_SQLITE_TYPE[String::class.java] = "TEXT"
|
||||
}
|
||||
}
|
||||
|
||||
private val statementsMap = SparseArray<List<String>?>()
|
||||
private var version = 0
|
||||
private var statements: MutableList<String>? = null
|
||||
|
||||
/**
|
||||
* Bump database version.
|
||||
*/
|
||||
fun version(version: Int): MSQLiteBuilder {
|
||||
check(version > this.version) {
|
||||
("New version must be bigger than current version. "
|
||||
+ "current version: " + this.version + ", new version: " + version + ".")
|
||||
}
|
||||
this.version = version
|
||||
statements = ArrayList()
|
||||
statementsMap.put(version, statements)
|
||||
return this
|
||||
}
|
||||
/**
|
||||
* Creates a table.
|
||||
*/
|
||||
/**
|
||||
* Creates a table with int [.COLUMN_ID] primary key.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun createTable(
|
||||
table: String,
|
||||
column: String = COLUMN_ID,
|
||||
clazz: Class<*>? = Int::class.javaPrimitiveType
|
||||
): MSQLiteBuilder {
|
||||
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);")
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a table.
|
||||
*/
|
||||
fun dropTable(table: String): MSQLiteBuilder {
|
||||
return statement("DROP TABLE $table;")
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a column to the table.
|
||||
*/
|
||||
fun insertColumn(table: String, column: String, clazz: Class<*>?): MSQLiteBuilder {
|
||||
return statement(
|
||||
"ALTER TABLE $table ADD COLUMN $column " + javaTypeToSQLiteType(
|
||||
clazz
|
||||
) + ";"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a statement.
|
||||
*/
|
||||
fun statement(statement: String): MSQLiteBuilder {
|
||||
check(!(version == 0 || statements == null)) { "Call version() first!" }
|
||||
statements!!.add(statement)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a SQLiteOpenHelper from it.
|
||||
*/
|
||||
fun build(context: Context?, name: String?, version: Int): SQLiteOpenHelper {
|
||||
return MSQLiteOpenHelper(context, name, version, this)
|
||||
}
|
||||
|
||||
fun getStatements(oldVersion: Int, newVersion: Int): List<String> {
|
||||
val result: MutableList<String> = ArrayList()
|
||||
for (i in oldVersion + 1..newVersion) {
|
||||
val list = statementsMap[i]
|
||||
if (list != null) {
|
||||
result.addAll(list)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 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.database
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import com.hippo.util.SqlUtils
|
||||
|
||||
internal class MSQLiteOpenHelper(
|
||||
context: Context?,
|
||||
name: String?,
|
||||
private val version: Int,
|
||||
private val builder: MSQLiteBuilder
|
||||
) : SQLiteOpenHelper(context, name, null, version) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
onUpgrade(db, 0, version)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
for (command in builder.getStatements(oldVersion, newVersion)) {
|
||||
db.execSQL(command)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
SqlUtils.dropAllTable(db)
|
||||
onCreate(db)
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.drawable;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
|
||||
public class AddDeleteDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
private final Path mPath = new Path();
|
||||
|
||||
private final int mSize;
|
||||
|
||||
private float mProgress;
|
||||
|
||||
private boolean mAutoUpdateMirror = false;
|
||||
private boolean mVerticalMirror = false;
|
||||
|
||||
/**
|
||||
* @param context used to get the configuration for the drawable from
|
||||
*/
|
||||
public AddDeleteDrawable(Context context, int color) {
|
||||
Resources resources = context.getResources();
|
||||
|
||||
mSize = resources.getDimensionPixelSize(R.dimen.add_size);
|
||||
float barThickness = Math.round(resources.getDimension(R.dimen.add_thickness));
|
||||
|
||||
mPaint.setColor(color);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeJoin(Paint.Join.MITER);
|
||||
mPaint.setStrokeCap(Paint.Cap.BUTT);
|
||||
mPaint.setStrokeWidth(barThickness);
|
||||
|
||||
float halfSize = mSize / 2;
|
||||
mPath.moveTo(0f, -halfSize);
|
||||
mPath.lineTo(0, halfSize);
|
||||
mPath.moveTo(-halfSize, 0);
|
||||
mPath.lineTo(halfSize, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
Rect bounds = getBounds();
|
||||
float canvasRotate;
|
||||
if (mVerticalMirror) {
|
||||
canvasRotate = MathUtils.lerp(270, 135f, mProgress);
|
||||
} else {
|
||||
canvasRotate = MathUtils.lerp(0f, 135f, mProgress);
|
||||
}
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(bounds.centerX(), bounds.centerY());
|
||||
canvas.rotate(canvasRotate);
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mPaint.setColor(color);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
mPaint.setColorFilter(cf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return mSize * 6 / 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
return mSize * 6 / 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set, canvas is flipped when progress reached to end and going back to start.
|
||||
*/
|
||||
protected void setVerticalMirror(boolean verticalMirror) {
|
||||
mVerticalMirror = verticalMirror;
|
||||
}
|
||||
|
||||
public void setAutoUpdateMirror(boolean autoUpdateMirror) {
|
||||
mAutoUpdateMirror = autoUpdateMirror;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public void setProgress(float progress) {
|
||||
if (mAutoUpdateMirror) {
|
||||
if (progress == 1f) {
|
||||
setVerticalMirror(true);
|
||||
} else if (progress == 0f) {
|
||||
setVerticalMirror(false);
|
||||
}
|
||||
}
|
||||
mProgress = progress;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void setAdd(long duration) {
|
||||
setShape(false, duration);
|
||||
}
|
||||
|
||||
public void setDelete(long duration) {
|
||||
setShape(true, duration);
|
||||
}
|
||||
|
||||
public void setShape(boolean delete, long duration) {
|
||||
if (!((!delete && mProgress == 0f) || (delete && mProgress == 1f))) {
|
||||
float endProgress = delete ? 1f : 0f;
|
||||
if (duration <= 0) {
|
||||
setProgress(endProgress);
|
||||
} else {
|
||||
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "progress", endProgress);
|
||||
oa.setDuration(duration);
|
||||
oa.setAutoCancel(true);
|
||||
oa.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
}
|
||||
@@ -1,190 +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.drawable;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
public class DrawableWrapper extends Drawable implements Drawable.Callback {
|
||||
private Drawable mDrawable;
|
||||
private Rect mBounds;
|
||||
|
||||
public DrawableWrapper(Drawable drawable) {
|
||||
this.setWrappedDrawable(drawable);
|
||||
}
|
||||
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (mDrawable instanceof BitmapDrawable bitmapDrawable) {
|
||||
canvas.drawBitmap(bitmapDrawable.getBitmap(), null, mBounds, bitmapDrawable.getPaint());
|
||||
} else {
|
||||
synchronized (mDrawable) {
|
||||
mDrawable.setBounds(mBounds);
|
||||
mDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
mBounds = bounds;
|
||||
}
|
||||
|
||||
public int getChangingConfigurations() {
|
||||
return this.mDrawable.getChangingConfigurations();
|
||||
}
|
||||
|
||||
public void setChangingConfigurations(int configs) {
|
||||
this.mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
|
||||
public void setDither(boolean dither) {
|
||||
this.mDrawable.setDither(dither);
|
||||
}
|
||||
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
this.mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
|
||||
public void setAlpha(int alpha) {
|
||||
this.mDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
this.mDrawable.setColorFilter(cf);
|
||||
}
|
||||
|
||||
public boolean isStateful() {
|
||||
return this.mDrawable.isStateful();
|
||||
}
|
||||
|
||||
public boolean setState(@NonNull int[] stateSet) {
|
||||
return this.mDrawable.setState(stateSet);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public int[] getState() {
|
||||
return this.mDrawable.getState();
|
||||
}
|
||||
|
||||
public void jumpToCurrentState() {
|
||||
this.mDrawable.jumpToCurrentState();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Drawable getCurrent() {
|
||||
return this.mDrawable.getCurrent();
|
||||
}
|
||||
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart);
|
||||
}
|
||||
|
||||
public int getOpacity() {
|
||||
return this.mDrawable.getOpacity();
|
||||
}
|
||||
|
||||
public Region getTransparentRegion() {
|
||||
return this.mDrawable.getTransparentRegion();
|
||||
}
|
||||
|
||||
public int getIntrinsicWidth() {
|
||||
return this.mDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
public int getIntrinsicHeight() {
|
||||
return this.mDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
public int getMinimumWidth() {
|
||||
return this.mDrawable.getMinimumWidth();
|
||||
}
|
||||
|
||||
public int getMinimumHeight() {
|
||||
return this.mDrawable.getMinimumHeight();
|
||||
}
|
||||
|
||||
public boolean getPadding(@NonNull Rect padding) {
|
||||
return this.mDrawable.getPadding(padding);
|
||||
}
|
||||
|
||||
public void invalidateDrawable(@NonNull Drawable who) {
|
||||
this.invalidateSelf();
|
||||
}
|
||||
|
||||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
|
||||
this.scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
|
||||
this.unscheduleSelf(what);
|
||||
}
|
||||
|
||||
protected boolean onLevelChange(int level) {
|
||||
return this.mDrawable.setLevel(level);
|
||||
}
|
||||
|
||||
public boolean isAutoMirrored() {
|
||||
return DrawableCompat.isAutoMirrored(this.mDrawable);
|
||||
}
|
||||
|
||||
public void setAutoMirrored(boolean mirrored) {
|
||||
DrawableCompat.setAutoMirrored(this.mDrawable, mirrored);
|
||||
}
|
||||
|
||||
public void setTint(int tint) {
|
||||
DrawableCompat.setTint(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintList(ColorStateList tint) {
|
||||
DrawableCompat.setTintList(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
|
||||
DrawableCompat.setTintMode(this.mDrawable, tintMode);
|
||||
}
|
||||
|
||||
public void setHotspot(float x, float y) {
|
||||
DrawableCompat.setHotspot(this.mDrawable, x, y);
|
||||
}
|
||||
|
||||
public void setHotspotBounds(int left, int top, int right, int bottom) {
|
||||
DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom);
|
||||
}
|
||||
|
||||
public Drawable getWrappedDrawable() {
|
||||
return this.mDrawable;
|
||||
}
|
||||
|
||||
public void setWrappedDrawable(Drawable drawable) {
|
||||
if (this.mDrawable != null) {
|
||||
this.mDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
this.mDrawable = drawable;
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +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.drawable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
|
||||
/**
|
||||
* Show a part of the original drawable
|
||||
*/
|
||||
public class PreciselyClipDrawable extends DrawableWrapper {
|
||||
|
||||
private final boolean mClip;
|
||||
private RectF mScale;
|
||||
private Rect mTemp;
|
||||
|
||||
public PreciselyClipDrawable(Drawable drawable, int offsetX, int offsetY, int width, int height) {
|
||||
super(drawable);
|
||||
float originWidth = drawable.getIntrinsicWidth();
|
||||
float originHeight = drawable.getIntrinsicHeight();
|
||||
|
||||
if (originWidth <= 0 || originHeight <= 0) {
|
||||
// Can not clip
|
||||
mClip = false;
|
||||
} else {
|
||||
mClip = true;
|
||||
mScale = new RectF();
|
||||
mScale.set(MathUtils.clamp(offsetX / originWidth, 0.0f, 1.0f),
|
||||
MathUtils.clamp(offsetY / originHeight, 0.0f, 1.0f),
|
||||
MathUtils.clamp((offsetX + width) / originWidth, 0.0f, 1.0f),
|
||||
MathUtils.clamp((offsetY + height) / originHeight, 0.0f, 1.0f));
|
||||
mTemp = new Rect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
if (mClip) {
|
||||
if (!mScale.isEmpty()) {
|
||||
mTemp.left = (int) ((mScale.left * bounds.right - mScale.right * bounds.left) /
|
||||
(mScale.left * (1 - mScale.right) - mScale.right * (1 - mScale.left)));
|
||||
mTemp.right = (int) (((1 - mScale.right) * bounds.left - (1 - mScale.left) * bounds.right) /
|
||||
(mScale.left * (1 - mScale.right) - mScale.right * (1 - mScale.left)));
|
||||
mTemp.top = (int) ((mScale.top * bounds.bottom - mScale.bottom * bounds.top) /
|
||||
(mScale.top * (1 - mScale.bottom) - mScale.bottom * (1 - mScale.top)));
|
||||
mTemp.bottom = (int) (((1 - mScale.bottom) * bounds.top - (1 - mScale.top) * bounds.bottom) /
|
||||
(mScale.top * (1 - mScale.bottom) - mScale.bottom * (1 - mScale.top)));
|
||||
super.onBoundsChange(mTemp);
|
||||
}
|
||||
} else {
|
||||
super.onBoundsChange(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
if (mClip) {
|
||||
return (int) (super.getIntrinsicWidth() * mScale.width());
|
||||
} else {
|
||||
return super.getIntrinsicWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
if (mClip) {
|
||||
return (int) (super.getIntrinsicHeight() * mScale.height());
|
||||
} else {
|
||||
return super.getIntrinsicHeight();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mClip) {
|
||||
if (!mScale.isEmpty()) {
|
||||
int saveCount = canvas.save();
|
||||
canvas.clipRect(getBounds());
|
||||
super.draw(canvas);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
} else {
|
||||
super.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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.drawable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class TriangleDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint;
|
||||
private final Path mPath;
|
||||
|
||||
public TriangleDrawable(int color) {
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
|
||||
mPaint.setColor(color);
|
||||
mPath = new Path();
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mPaint.setColor(color);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
mPath.reset();
|
||||
mPath.moveTo(bounds.left, bounds.top);
|
||||
mPath.lineTo(bounds.right, bounds.top);
|
||||
mPath.lineTo(bounds.right, bounds.bottom);
|
||||
mPath.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.drawPath(mPath, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +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.drawable;
|
||||
|
||||
import android.graphics.drawable.AnimatedImageDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.hippo.widget.ObservedTextView;
|
||||
|
||||
import coil.Coil;
|
||||
import coil.request.Disposable;
|
||||
import coil.request.ImageRequest;
|
||||
|
||||
public class UnikeryDrawable extends WrapDrawable implements ObservedTextView.OnWindowAttachListener {
|
||||
|
||||
private static final String TAG = UnikeryDrawable.class.getSimpleName();
|
||||
private final ObservedTextView mTextView;
|
||||
private String mUrl;
|
||||
private Disposable task;
|
||||
|
||||
public UnikeryDrawable(ObservedTextView textView) {
|
||||
mTextView = textView;
|
||||
mTextView.setOnWindowAttachListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
load(mUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
if (task != null && !task.isDisposed())
|
||||
task.dispose();
|
||||
clearDrawable();
|
||||
}
|
||||
|
||||
public void load(String url) {
|
||||
if (url != null) {
|
||||
mUrl = url;
|
||||
var imageLoader = Coil.imageLoader(mTextView.getContext());
|
||||
var request = new ImageRequest.Builder(mTextView.getContext()).data(url).memoryCacheKey(url).diskCacheKey(url).target(
|
||||
drawable -> null,
|
||||
drawable -> null,
|
||||
drawable -> {
|
||||
onGetValue(drawable);
|
||||
return null;
|
||||
}
|
||||
).build();
|
||||
task = imageLoader.enqueue(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDrawable() {
|
||||
setDrawable(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDrawable(Drawable drawable) {
|
||||
// Remove old callback
|
||||
Drawable oldDrawable = getDrawable();
|
||||
if (oldDrawable != null) {
|
||||
oldDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
super.setDrawable(drawable);
|
||||
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(mTextView);
|
||||
}
|
||||
|
||||
updateBounds();
|
||||
if (drawable != null) {
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateSelf() {
|
||||
CharSequence cs = mTextView.getText();
|
||||
mTextView.setText(cs);
|
||||
}
|
||||
|
||||
public void onGetValue(Drawable drawable) {
|
||||
clearDrawable();
|
||||
setDrawable(drawable);
|
||||
if (drawable instanceof AnimatedImageDrawable animatedImageDrawable)
|
||||
animatedImageDrawable.start();
|
||||
}
|
||||
}
|
||||
@@ -1,143 +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.drawable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class WrapDrawable extends Drawable {
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
public Drawable getDrawable() {
|
||||
return mDrawable;
|
||||
}
|
||||
|
||||
public void setDrawable(Drawable drawable) {
|
||||
mDrawable = drawable;
|
||||
}
|
||||
|
||||
public void updateBounds() {
|
||||
setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int left, int top, int right, int bottom) {
|
||||
super.setBounds(left, top, right, bottom);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setBounds(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Rect bounds) {
|
||||
super.setBounds(bounds);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getChangingConfigurations();
|
||||
} else {
|
||||
return super.getChangingConfigurations();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangingConfigurations(int configs) {
|
||||
super.setChangingConfigurations(configs);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDither(boolean dither) {
|
||||
super.setDither(dither);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setDither(dither);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
super.setFilterBitmap(filter);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setColorFilter(cf);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getOpacity();
|
||||
} else {
|
||||
return PixelFormat.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicWidth() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getIntrinsicWidth();
|
||||
} else {
|
||||
return super.getIntrinsicWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
if (mDrawable != null) {
|
||||
return mDrawable.getIntrinsicHeight();
|
||||
} else {
|
||||
return super.getIntrinsicHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,675 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.easyrecyclerview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Checkable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.LongSparseArray;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
/**
|
||||
* Add setChoiceMode for RecyclerView
|
||||
*/
|
||||
// Get some code from twoway-view and AbsListView.
|
||||
public class EasyRecyclerView extends RecyclerView {
|
||||
|
||||
/**
|
||||
* Normal list that does not indicate choices
|
||||
*/
|
||||
public static final int CHOICE_MODE_NONE = 0;
|
||||
|
||||
/**
|
||||
* The list allows up to one choice
|
||||
*/
|
||||
public static final int CHOICE_MODE_SINGLE = 1;
|
||||
|
||||
/**
|
||||
* The list allows multiple choices
|
||||
*/
|
||||
public static final int CHOICE_MODE_MULTIPLE = 2;
|
||||
|
||||
/**
|
||||
* The list allows multiple choices in a modal selection mode
|
||||
*/
|
||||
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
|
||||
|
||||
/**
|
||||
* The list allows multiple choices in custom action
|
||||
*/
|
||||
public static final int CHOICE_MODE_MULTIPLE_CUSTOM = 4;
|
||||
// Fling friction
|
||||
public float pageScrollThreshold;
|
||||
/**
|
||||
* Wrapper for the multiple choice mode callback; AbsListView needs to perform
|
||||
* a few extra actions around what application code does.
|
||||
*/
|
||||
MultiChoiceModeWrapper mMultiChoiceModeCallback;
|
||||
/**
|
||||
* Controls if/how the user may choose/check items in the list
|
||||
*/
|
||||
private int mChoiceMode = CHOICE_MODE_NONE;
|
||||
/**
|
||||
* Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
|
||||
*/
|
||||
private ActionMode mChoiceActionMode;
|
||||
/**
|
||||
* Listener for custom multiple choices
|
||||
*/
|
||||
private CustomChoiceListener mCustomChoiceListener;
|
||||
|
||||
private boolean mCustomChoice = false;
|
||||
|
||||
/**
|
||||
* A lock, avoid OutOfCustomChoiceMode when doing OutOfCustomChoiceMode
|
||||
*/
|
||||
private boolean mOutOfCustomChoiceModing = false;
|
||||
|
||||
private SparseBooleanArray mTempCheckStates;
|
||||
|
||||
/**
|
||||
* Running count of how many items are currently checked
|
||||
*/
|
||||
private int mCheckedItemCount;
|
||||
|
||||
/**
|
||||
* Running state of which positions are currently checked
|
||||
*/
|
||||
private SparseBooleanArray mCheckStates;
|
||||
|
||||
/**
|
||||
* Running state of which IDs are currently checked.
|
||||
* If there is a value for a given key, the checked state for that ID is true
|
||||
* and the value holds the last known position in the adapter for that id.
|
||||
*/
|
||||
private LongSparseArray<Integer> mCheckedIdStates;
|
||||
|
||||
private Adapter<?> mAdapter;
|
||||
|
||||
public EasyRecyclerView(Context context) {
|
||||
super(context);
|
||||
EasyRecyclerView.this.init(context);
|
||||
}
|
||||
|
||||
public EasyRecyclerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
EasyRecyclerView.this.init(context);
|
||||
}
|
||||
|
||||
public EasyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
EasyRecyclerView.this.init(context);
|
||||
}
|
||||
|
||||
public static void setViewChecked(View view, boolean checked) {
|
||||
if (view instanceof Checkable) {
|
||||
((Checkable) view).setChecked(checked);
|
||||
} else {
|
||||
view.setActivated(checked);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setAdapter(RecyclerView.Adapter adapter) {
|
||||
|
||||
super.setAdapter(adapter);
|
||||
|
||||
mAdapter = adapter;
|
||||
if (adapter != null) {
|
||||
if (mChoiceMode != CHOICE_MODE_NONE && adapter.hasStableIds() &&
|
||||
mCheckedIdStates == null) {
|
||||
mCheckedIdStates = new LongSparseArray<>();
|
||||
}
|
||||
}
|
||||
|
||||
if (mCheckStates != null) {
|
||||
mCheckStates.clear();
|
||||
}
|
||||
|
||||
if (mCheckedIdStates != null) {
|
||||
mCheckedIdStates.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildAttachedToWindow(@NonNull View child) {
|
||||
super.onChildAttachedToWindow(child);
|
||||
|
||||
if (mCheckStates != null) {
|
||||
int position = getChildAdapterPosition(child);
|
||||
if (position >= 0) {
|
||||
setViewChecked(child, mCheckStates.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getCheckedItemCount() {
|
||||
return mCheckedItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the checked state of the specified position. The result is only
|
||||
* valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
|
||||
* or {@link #CHOICE_MODE_MULTIPLE}.
|
||||
*
|
||||
* @param position The item whose checked state to return
|
||||
* @return The item's checked state or <code>false</code> if choice mode
|
||||
* is invalid
|
||||
* @see #setChoiceMode(int)
|
||||
*/
|
||||
public boolean isItemChecked(int position) {
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
|
||||
return mCheckStates.get(position);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of checked items in the list. The result is only valid if
|
||||
* the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
|
||||
*
|
||||
* @return A SparseBooleanArray which will return true for each call to
|
||||
* get(int position) where position is a checked position in the
|
||||
* list and false otherwise, or <code>null</code> if the choice
|
||||
* mode is set to {@link #CHOICE_MODE_NONE}.
|
||||
*/
|
||||
public SparseBooleanArray getCheckedItemPositions() {
|
||||
if (mChoiceMode != CHOICE_MODE_NONE) {
|
||||
return mCheckStates;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isInCustomChoice() {
|
||||
return mCustomChoice;
|
||||
}
|
||||
|
||||
public void intoCustomChoiceMode() {
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
|
||||
mCustomChoice = true;
|
||||
|
||||
mCustomChoiceListener.onIntoCustomChoice(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void outOfCustomChoiceMode() {
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && mCustomChoice && !mOutOfCustomChoiceModing) {
|
||||
mOutOfCustomChoiceModing = true;
|
||||
|
||||
// Copy mCheckStates
|
||||
mTempCheckStates.clear();
|
||||
for (int i = 0, n = mCheckStates.size(); i < n; i++) {
|
||||
mTempCheckStates.put(mCheckStates.keyAt(i), mCheckStates.valueAt(i));
|
||||
}
|
||||
// Uncheck remain checked items
|
||||
for (int i = 0, n = mTempCheckStates.size(); i < n; i++) {
|
||||
if (mTempCheckStates.valueAt(i)) {
|
||||
setItemChecked(mTempCheckStates.keyAt(i), false);
|
||||
}
|
||||
}
|
||||
|
||||
mCustomChoice = false;
|
||||
mCustomChoiceListener.onOutOfCustomChoice(this);
|
||||
|
||||
mOutOfCustomChoiceModing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear any choices previously set
|
||||
*/
|
||||
private void clearChoices() {
|
||||
if (mCheckStates != null) {
|
||||
mCheckStates.clear();
|
||||
}
|
||||
if (mCheckedIdStates != null) {
|
||||
mCheckedIdStates.clear();
|
||||
}
|
||||
mCheckedItemCount = 0;
|
||||
updateOnScreenCheckedViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all items checked.
|
||||
*/
|
||||
public void checkAll() {
|
||||
if (mChoiceMode == CHOICE_MODE_NONE || mChoiceMode == CHOICE_MODE_SINGLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check is intoCheckMode
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
|
||||
throw new IllegalStateException("Call intoCheckMode first");
|
||||
}
|
||||
|
||||
// Start selection mode if needed. We don't need to if we're unchecking something.
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
|
||||
if (mMultiChoiceModeCallback == null ||
|
||||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
|
||||
throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " +
|
||||
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
|
||||
"supplied. Call setMultiChoiceModeListener to set a callback.");
|
||||
}
|
||||
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
|
||||
}
|
||||
|
||||
for (int i = 0, n = mAdapter.getItemCount(); i < n; i++) {
|
||||
boolean oldValue = mCheckStates.get(i);
|
||||
mCheckStates.put(i, true);
|
||||
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
|
||||
mCheckedIdStates.put(mAdapter.getItemId(i), i);
|
||||
}
|
||||
if (!oldValue) {
|
||||
mCheckedItemCount++;
|
||||
}
|
||||
if (mChoiceActionMode != null) {
|
||||
final long id = mAdapter.getItemId(i);
|
||||
mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, i, id, true);
|
||||
}
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
|
||||
final long id = mAdapter.getItemId(i);
|
||||
mCustomChoiceListener.onItemCheckedStateChanged(this, i, id, true);
|
||||
}
|
||||
}
|
||||
|
||||
updateOnScreenCheckedViews();
|
||||
}
|
||||
|
||||
public void toggleItemChecked(int position) {
|
||||
if (mCheckStates != null) {
|
||||
setItemChecked(position, !mCheckStates.get(position));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the checked state of the specified position. The is only valid if
|
||||
* the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
|
||||
* {@link #CHOICE_MODE_MULTIPLE}.
|
||||
*
|
||||
* @param position The item whose checked state is to be checked
|
||||
* @param value The new checked state for the item
|
||||
*/
|
||||
public void setItemChecked(int position, boolean value) {
|
||||
if (mChoiceMode == CHOICE_MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check is intoCheckMode
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM && !mCustomChoice) {
|
||||
throw new IllegalStateException("Call intoCheckMode first");
|
||||
}
|
||||
|
||||
// Start selection mode if needed. We don't need to if we're unchecking something.
|
||||
if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
|
||||
if (mMultiChoiceModeCallback == null ||
|
||||
!mMultiChoiceModeCallback.hasWrappedCallback()) {
|
||||
throw new IllegalStateException("EasyRecyclerView: attempted to start selection mode " +
|
||||
"for CHOICE_MODE_MULTIPLE_MODAL but no choice mode callback was " +
|
||||
"supplied. Call setMultiChoiceModeListener to set a callback.");
|
||||
}
|
||||
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
|
||||
}
|
||||
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL || mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
|
||||
boolean oldValue = mCheckStates.get(position);
|
||||
mCheckStates.put(position, value);
|
||||
if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
|
||||
if (value) {
|
||||
mCheckedIdStates.put(mAdapter.getItemId(position), position);
|
||||
} else {
|
||||
mCheckedIdStates.remove(mAdapter.getItemId(position));
|
||||
}
|
||||
}
|
||||
if (oldValue != value) {
|
||||
if (value) {
|
||||
mCheckedItemCount++;
|
||||
} else {
|
||||
mCheckedItemCount--;
|
||||
}
|
||||
}
|
||||
if (mChoiceActionMode != null) {
|
||||
final long id = mAdapter.getItemId(position);
|
||||
mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
|
||||
position, id, value);
|
||||
}
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
|
||||
final long id = mAdapter.getItemId(position);
|
||||
mCustomChoiceListener.onItemCheckedStateChanged(this, position, id, value);
|
||||
}
|
||||
} else {
|
||||
boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
|
||||
// Clear all values if we're checking something, or unchecking the currently
|
||||
// selected item
|
||||
if (value || isItemChecked(position)) {
|
||||
mCheckStates.clear();
|
||||
if (updateIds) {
|
||||
mCheckedIdStates.clear();
|
||||
}
|
||||
}
|
||||
// this may end up selecting the value we just cleared but this way
|
||||
// we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
|
||||
if (value) {
|
||||
mCheckStates.put(position, true);
|
||||
if (updateIds) {
|
||||
mCheckedIdStates.put(mAdapter.getItemId(position), position);
|
||||
}
|
||||
mCheckedItemCount = 1;
|
||||
} else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
|
||||
mCheckedItemCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateOnScreenCheckedViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the choice behavior for the List. By default, Lists do not have any choice behavior
|
||||
* ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
|
||||
* List allows up to one item to be in a chosen state. By setting the choiceMode to
|
||||
* {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
|
||||
*
|
||||
* @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
|
||||
* {@link #CHOICE_MODE_MULTIPLE}
|
||||
*/
|
||||
public void setChoiceMode(int choiceMode) {
|
||||
mChoiceMode = choiceMode;
|
||||
if (mChoiceActionMode != null) {
|
||||
mChoiceActionMode.finish();
|
||||
mChoiceActionMode = null;
|
||||
}
|
||||
if (mChoiceMode != CHOICE_MODE_NONE) {
|
||||
if (mCheckStates == null) {
|
||||
mCheckStates = new SparseBooleanArray(0);
|
||||
}
|
||||
if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
|
||||
mCheckedIdStates = new LongSparseArray<>(0);
|
||||
}
|
||||
// Modal multi-choice mode only has choices when the mode is active. Clear them.
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
|
||||
clearChoices();
|
||||
setLongClickable(true);
|
||||
} else if (mChoiceMode == CHOICE_MODE_MULTIPLE_CUSTOM) {
|
||||
if (mTempCheckStates == null) {
|
||||
mTempCheckStates = new SparseBooleanArray(0);
|
||||
}
|
||||
clearChoices();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setCustomCheckedListener(CustomChoiceListener listener) {
|
||||
mCustomChoiceListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a quick, in-place update of the checked or activated state
|
||||
* on all visible item views. This should only be called when a valid
|
||||
* choice mode is active.
|
||||
*/
|
||||
private void updateOnScreenCheckedViews() {
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = getChildAt(i);
|
||||
final int position = getChildAdapterPosition(child);
|
||||
setViewChecked(child, mCheckStates.get(position));
|
||||
}
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
float scale = context.getResources().getDisplayMetrics().density;
|
||||
// g (m/s^2)
|
||||
// inch/meter
|
||||
// A context-specific coefficient adjusted to physical values.
|
||||
// look and feel tuning
|
||||
pageScrollThreshold = scale * 80;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
final SavedState ss = new SavedState(super.onSaveInstanceState());
|
||||
|
||||
ss.choiceMode = mChoiceMode;
|
||||
ss.customChoice = mCustomChoice;
|
||||
ss.checkedItemCount = mCheckedItemCount;
|
||||
ss.checkState = mCheckStates;
|
||||
ss.checkIdState = mCheckedIdStates;
|
||||
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
|
||||
setChoiceMode(ss.choiceMode);
|
||||
mCustomChoice = ss.customChoice;
|
||||
mCheckedItemCount = ss.checkedItemCount;
|
||||
if (ss.checkState != null) {
|
||||
mCheckStates = ss.checkState;
|
||||
}
|
||||
if (ss.checkIdState != null) {
|
||||
mCheckedIdStates = ss.checkIdState;
|
||||
}
|
||||
|
||||
if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mCheckedItemCount > 0) {
|
||||
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
|
||||
}
|
||||
updateOnScreenCheckedViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* A MultiChoiceModeListener receives events for {@link android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
|
||||
* It acts as the {@link android.view.ActionMode.Callback} for the selection mode and also receives
|
||||
* {@link #onItemCheckedStateChanged(android.view.ActionMode, int, long, boolean)} events when the user
|
||||
* selects and deselects list items.
|
||||
*/
|
||||
public interface MultiChoiceModeListener extends ActionMode.Callback {
|
||||
/**
|
||||
* Called when an item is checked or unchecked during selection mode.
|
||||
*
|
||||
* @param mode The {@link android.view.ActionMode} providing the selection mode
|
||||
* @param position Adapter position of the item that was checked or unchecked
|
||||
* @param id Adapter ID of the item that was checked or unchecked
|
||||
* @param checked <code>true</code> if the item is now checked, <code>false</code>
|
||||
* if the item is now unchecked.
|
||||
*/
|
||||
void onItemCheckedStateChanged(ActionMode mode,
|
||||
int position, long id, boolean checked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom checked
|
||||
*/
|
||||
public interface CustomChoiceListener {
|
||||
|
||||
void onIntoCustomChoice(EasyRecyclerView view);
|
||||
|
||||
void onOutOfCustomChoice(EasyRecyclerView view);
|
||||
|
||||
void onItemCheckedStateChanged(EasyRecyclerView view, int position, long id, boolean checked);
|
||||
}
|
||||
|
||||
/**
|
||||
* This saved state class is a Parcelable and should not extend
|
||||
* {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState}
|
||||
* because its super class AbsSavedState's constructor
|
||||
* currently passes null
|
||||
* as a class loader to read its superstate from Parcelable.
|
||||
* This causes {@link android.os.BadParcelableException} when restoring saved states.
|
||||
* <p/>
|
||||
* The super class "RecyclerView" is a part of the support library,
|
||||
* and restoring its saved state requires the class loader that loaded the RecyclerView.
|
||||
* It seems that the class loader is not required when restoring from RecyclerView itself,
|
||||
* but it is required when restoring from RecyclerView's subclasses.
|
||||
*/
|
||||
static class SavedState implements Parcelable {
|
||||
|
||||
public static final SavedState EMPTY_STATE = new SavedState() {
|
||||
};
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
int choiceMode;
|
||||
boolean customChoice;
|
||||
int checkedItemCount;
|
||||
SparseBooleanArray checkState;
|
||||
LongSparseArray<Integer> checkIdState;
|
||||
// This keeps the parent(RecyclerView)'s state
|
||||
Parcelable mSuperState;
|
||||
|
||||
SavedState() {
|
||||
mSuperState = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #onSaveInstanceState()}
|
||||
*/
|
||||
SavedState(Parcelable superState) {
|
||||
mSuperState = superState != EMPTY_STATE ? superState : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
private SavedState(Parcel in) {
|
||||
// Parcel 'in' has its parent(RecyclerView)'s saved state.
|
||||
// To restore it, class loader that loaded RecyclerView is required.
|
||||
Parcelable superState = in.readParcelable(RecyclerView.class.getClassLoader());
|
||||
mSuperState = superState != null ? superState : EMPTY_STATE;
|
||||
|
||||
choiceMode = in.readInt();
|
||||
customChoice = NumberUtils.int2boolean(in.readInt());
|
||||
checkedItemCount = in.readInt();
|
||||
checkState = in.readSparseBooleanArray();
|
||||
final int N = in.readInt();
|
||||
if (N > 0) {
|
||||
checkIdState = new LongSparseArray<>();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final long key = in.readLong();
|
||||
final int value = in.readInt();
|
||||
checkIdState.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeParcelable(mSuperState, flags);
|
||||
|
||||
out.writeInt(choiceMode);
|
||||
out.writeInt(NumberUtils.boolean2int(customChoice));
|
||||
out.writeInt(checkedItemCount);
|
||||
out.writeSparseBooleanArray(checkState);
|
||||
final int N = checkIdState != null ? checkIdState.size() : 0;
|
||||
out.writeInt(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
out.writeLong(checkIdState.keyAt(i));
|
||||
out.writeInt(checkIdState.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
public Parcelable getSuperState() {
|
||||
return mSuperState;
|
||||
}
|
||||
}
|
||||
|
||||
class MultiChoiceModeWrapper implements MultiChoiceModeListener {
|
||||
private MultiChoiceModeListener mWrapped;
|
||||
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean hasWrappedCallback() {
|
||||
return mWrapped != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
if (mWrapped.onCreateActionMode(mode, menu)) {
|
||||
// Initialize checked graphic state?
|
||||
setLongClickable(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return mWrapped.onPrepareActionMode(mode, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
return mWrapped.onActionItemClicked(mode, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
mWrapped.onDestroyActionMode(mode);
|
||||
mChoiceActionMode = null;
|
||||
|
||||
// Ending selection mode means deselecting everything.
|
||||
clearChoices();
|
||||
|
||||
requestLayout();
|
||||
|
||||
setLongClickable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode,
|
||||
int position, long id, boolean checked) {
|
||||
mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
|
||||
|
||||
// If there are no items selected we no longer need the selection mode.
|
||||
if (getCheckedItemCount() == 0) {
|
||||
mode.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,404 +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.easyrecyclerview;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.AnimationUtils;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.SimpleAnimatorListener;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
public class FastScroller extends View {
|
||||
|
||||
private static final int INVALID = -1;
|
||||
|
||||
private static final int SCROLL_BAR_FADE_DURATION = 500;
|
||||
private static final int SCROLL_BAR_DELAY = 1000;
|
||||
|
||||
private static final int MIN_HANDLER_HEIGHT_DP = 32;
|
||||
|
||||
private Handler mSimpleHandler;
|
||||
|
||||
private boolean mDraggable;
|
||||
|
||||
private int mMinHandlerHeight;
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private RecyclerView.OnScrollListener mOnScrollChangeListener;
|
||||
private RecyclerView.Adapter<?> mAdapter;
|
||||
private RecyclerView.AdapterDataObserver mAdapterDataObserver;
|
||||
|
||||
private Drawable mHandler;
|
||||
private int mHandlerOffset = INVALID;
|
||||
private int mHandlerHeight = INVALID;
|
||||
|
||||
private float mDownX = INVALID;
|
||||
private float mDownY = INVALID;
|
||||
|
||||
private float mLastMotionY = INVALID;
|
||||
|
||||
private boolean mDragged = false;
|
||||
|
||||
private boolean mCantDrag = false;
|
||||
|
||||
private int mTouchSlop;
|
||||
|
||||
private OnDragHandlerListener mListener;
|
||||
|
||||
private ObjectAnimator mShowAnimator;
|
||||
private ObjectAnimator mHideAnimator;
|
||||
|
||||
private final Runnable mHideRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mHideAnimator.start();
|
||||
}
|
||||
};
|
||||
|
||||
public FastScroller(Context context) {
|
||||
super(context);
|
||||
init(context, null, 0);
|
||||
}
|
||||
|
||||
public FastScroller(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
mSimpleHandler = SimpleHandler.getInstance();
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FastScroller, defStyleAttr, 0);
|
||||
mHandler = a.getDrawable(R.styleable.FastScroller_handler);
|
||||
mDraggable = a.getBoolean(R.styleable.FastScroller_draggable, true);
|
||||
a.recycle();
|
||||
|
||||
setAlpha(0.0f);
|
||||
setVisibility(INVISIBLE);
|
||||
|
||||
mMinHandlerHeight = LayoutUtils.dp2pix(context, MIN_HANDLER_HEIGHT_DP);
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
mShowAnimator = ObjectAnimator.ofFloat(this, "alpha", 1.0f);
|
||||
mShowAnimator.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
|
||||
mShowAnimator.setDuration(SCROLL_BAR_FADE_DURATION);
|
||||
|
||||
mHideAnimator = ObjectAnimator.ofFloat(this, "alpha", 0.0f);
|
||||
mHideAnimator.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
|
||||
mHideAnimator.setDuration(SCROLL_BAR_FADE_DURATION);
|
||||
mHideAnimator.addListener(new SimpleAnimatorListener() {
|
||||
|
||||
private boolean mCancel;
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCancel = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mCancel) {
|
||||
mCancel = false;
|
||||
} else {
|
||||
setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setOnDragHandlerListener(OnDragHandlerListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void updatePosition(boolean show) {
|
||||
if (mRecyclerView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int paddingTop = getPaddingTop();
|
||||
int paddingBottom = getPaddingBottom();
|
||||
int height = getHeight() - paddingTop - paddingBottom;
|
||||
int offset = mRecyclerView.computeVerticalScrollOffset();
|
||||
int extent = mRecyclerView.computeVerticalScrollExtent();
|
||||
int range = mRecyclerView.computeVerticalScrollRange();
|
||||
|
||||
if (height <= 0 || extent >= range || extent <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int endOffset = height * offset / range;
|
||||
int endHeight = height * extent / range;
|
||||
|
||||
endHeight = Math.max(endHeight, mMinHandlerHeight);
|
||||
endOffset = Math.min(endOffset, height - endHeight);
|
||||
|
||||
mHandlerOffset = endOffset + paddingTop;
|
||||
mHandlerHeight = endHeight;
|
||||
|
||||
if (show) {
|
||||
if (mHideAnimator.isRunning()) {
|
||||
mHideAnimator.cancel();
|
||||
mShowAnimator.start();
|
||||
} else if (getVisibility() != VISIBLE && !mShowAnimator.isRunning()) {
|
||||
setVisibility(VISIBLE);
|
||||
mShowAnimator.start();
|
||||
}
|
||||
|
||||
Handler handler = mSimpleHandler;
|
||||
handler.removeCallbacks(mHideRunnable);
|
||||
|
||||
if (!mDragged) {
|
||||
handler.postDelayed(mHideRunnable, SCROLL_BAR_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setHandlerDrawable(Drawable drawable) {
|
||||
mHandler = drawable;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public boolean isDraggable() {
|
||||
return mDraggable;
|
||||
}
|
||||
|
||||
public void setDraggable(boolean draggable) {
|
||||
mDraggable = draggable;
|
||||
if (mDragged) {
|
||||
mDragged = false;
|
||||
}
|
||||
mSimpleHandler.removeCallbacks(mHideRunnable);
|
||||
mHideRunnable.run();
|
||||
}
|
||||
|
||||
public boolean isAttached() {
|
||||
return mRecyclerView != null;
|
||||
}
|
||||
|
||||
public void attachToRecyclerView(RecyclerView recyclerView) {
|
||||
if (recyclerView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRecyclerView != null) {
|
||||
throw new IllegalStateException("The FastScroller is already attached to a RecyclerView, " +
|
||||
"call detachedFromRecyclerView first");
|
||||
}
|
||||
|
||||
mRecyclerView = recyclerView;
|
||||
mOnScrollChangeListener = new RecyclerView.OnScrollListener() {
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
updatePosition(true);
|
||||
invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
recyclerView.addOnScrollListener(mOnScrollChangeListener);
|
||||
|
||||
mAdapter = recyclerView.getAdapter();
|
||||
if (mAdapter != null) {
|
||||
mAdapterDataObserver = new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onItemRangeChanged(int positionStart, int itemCount) {
|
||||
super.onItemRangeChanged(positionStart, itemCount);
|
||||
updatePosition(false);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
|
||||
super.onItemRangeChanged(positionStart, itemCount, payload);
|
||||
updatePosition(false);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
super.onItemRangeInserted(positionStart, itemCount);
|
||||
updatePosition(false);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount);
|
||||
updatePosition(false);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
|
||||
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
|
||||
updatePosition(false);
|
||||
invalidate();
|
||||
}
|
||||
};
|
||||
mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public void detachedFromRecyclerView() {
|
||||
if (mRecyclerView != null && mOnScrollChangeListener != null) {
|
||||
mRecyclerView.removeOnScrollListener(mOnScrollChangeListener);
|
||||
}
|
||||
mRecyclerView = null;
|
||||
mOnScrollChangeListener = null;
|
||||
|
||||
if (mAdapter != null && mAdapterDataObserver != null) {
|
||||
mAdapter.unregisterAdapterDataObserver(mAdapterDataObserver);
|
||||
}
|
||||
mAdapter = null;
|
||||
mAdapterDataObserver = null;
|
||||
|
||||
setAlpha(0.0f);
|
||||
setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mRecyclerView == null || mHandler == null) {
|
||||
return;
|
||||
}
|
||||
if (mHandlerHeight == INVALID) {
|
||||
updatePosition(false);
|
||||
}
|
||||
if (mHandlerHeight == INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int saved = canvas.save();
|
||||
canvas.translate(paddingLeft, mHandlerOffset);
|
||||
mHandler.setBounds(0, 0, getWidth() - paddingLeft - getPaddingRight(), mHandlerHeight);
|
||||
mHandler.draw(canvas);
|
||||
canvas.restoreToCount(saved);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (!mDraggable || getVisibility() != VISIBLE || mRecyclerView == null || mHandlerHeight == INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int action = event.getAction();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mCantDrag = false;
|
||||
}
|
||||
|
||||
if (mCantDrag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
mDragged = false;
|
||||
mDownX = event.getX();
|
||||
mDownY = event.getY();
|
||||
if (mDownY < mHandlerOffset || mDownY > mHandlerOffset + mHandlerHeight) {
|
||||
mCantDrag = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (!mDragged) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
// Check touch slop
|
||||
if (MathUtils.dist(x, y, mDownX, mDownY) < mTouchSlop) {
|
||||
return true;
|
||||
}
|
||||
if (Math.abs(x - mDownX) > Math.abs(y - mDownY) ||
|
||||
y < mHandlerOffset || y > mHandlerOffset + mHandlerHeight) {
|
||||
mCantDrag = true;
|
||||
return false;
|
||||
} else {
|
||||
mDragged = true;
|
||||
mSimpleHandler.removeCallbacks(mHideRunnable);
|
||||
// Update mLastMotionY
|
||||
if (mDownY < mHandlerOffset || mDownY >= mHandlerOffset + mHandlerHeight) {
|
||||
// the point out of handler, make the point in handler center
|
||||
mLastMotionY = mHandlerOffset + mHandlerHeight / 2;
|
||||
} else {
|
||||
mLastMotionY = mDownY;
|
||||
}
|
||||
// Notify
|
||||
if (mListener != null) {
|
||||
mListener.onStartDragHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int range = mRecyclerView.computeVerticalScrollRange();
|
||||
if (range <= 0) {
|
||||
break;
|
||||
}
|
||||
float y = event.getY();
|
||||
int scroll = (int) (range * (y - mLastMotionY) / (getHeight() - getPaddingTop() - getPaddingBottom()));
|
||||
mRecyclerView.scrollBy(0, scroll);
|
||||
mLastMotionY = y;
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
// Notify
|
||||
if (mDragged && mListener != null) {
|
||||
mListener.onEndDragHandler();
|
||||
}
|
||||
mDragged = false;
|
||||
mSimpleHandler.postDelayed(mHideRunnable, SCROLL_BAR_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface OnDragHandlerListener {
|
||||
void onStartDragHandler();
|
||||
|
||||
void onEndDragHandler();
|
||||
}
|
||||
}
|
||||
@@ -1,86 +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.easyrecyclerview;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class HandlerDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint;
|
||||
private final RectF mTemp = new RectF();
|
||||
private int mColor = Color.BLACK;
|
||||
|
||||
public HandlerDrawable() {
|
||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mPaint.setColor(mColor);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
int width = getBounds().width();
|
||||
int height = getBounds().height();
|
||||
|
||||
if (width > height) {
|
||||
canvas.drawRect(0, 0, width, height, mPaint);
|
||||
} else {
|
||||
mTemp.set(0, 0, width, width);
|
||||
canvas.drawArc(mTemp, -180, 180, true, mPaint);
|
||||
mTemp.set(0, height - width, width, height);
|
||||
canvas.drawArc(mTemp, 0, 180, true, mPaint);
|
||||
float halfWidth = (float) width / 2.0f;
|
||||
canvas.drawRect(0, halfWidth, width, height - halfWidth, mPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
if (mColor != color) {
|
||||
mColor = color;
|
||||
mPaint.setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
int alpha = Color.alpha(mColor);
|
||||
if (alpha == 0xff) {
|
||||
return PixelFormat.OPAQUE;
|
||||
} else if (alpha == 0x00) {
|
||||
return PixelFormat.TRANSPARENT;
|
||||
} else {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,151 +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.easyrecyclerview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class LayoutManagerUtils {
|
||||
|
||||
private static Method sCsdfp = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
sCsdfp = StaggeredGridLayoutManager.class.getDeclaredMethod("calculateScrollDirectionForPosition", int.class);
|
||||
sCsdfp.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// Ignore
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void scrollToPositionWithOffset(
|
||||
RecyclerView.LayoutManager layoutManager, int position, int offset) {
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, offset);
|
||||
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
((StaggeredGridLayoutManager) layoutManager).scrollToPositionWithOffset(position, offset);
|
||||
} else {
|
||||
throw new IllegalStateException("Can't do scrollToPositionWithOffset for " +
|
||||
layoutManager.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void smoothScrollToPosition(
|
||||
RecyclerView.LayoutManager layoutManager, Context context, int position) {
|
||||
smoothScrollToPosition(layoutManager, context, position, -1);
|
||||
}
|
||||
|
||||
public static void smoothScrollToPosition(
|
||||
RecyclerView.LayoutManager layoutManager, Context context, int position,
|
||||
int millisecondsPerInch) {
|
||||
SimpleSmoothScroller smoothScroller;
|
||||
if (layoutManager instanceof final LinearLayoutManager linearLayoutManager) {
|
||||
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
|
||||
@Override
|
||||
public PointF computeScrollVectorForPosition(int targetPosition) {
|
||||
return linearLayoutManager.computeScrollVectorForPosition(targetPosition);
|
||||
}
|
||||
};
|
||||
|
||||
} else if (layoutManager instanceof final StaggeredGridLayoutManager staggeredGridLayoutManager) {
|
||||
smoothScroller = new SimpleSmoothScroller(context, millisecondsPerInch) {
|
||||
@Override
|
||||
public PointF computeScrollVectorForPosition(int targetPosition) {
|
||||
int direction = 0;
|
||||
try {
|
||||
direction = (Integer) sCsdfp.invoke(staggeredGridLayoutManager, targetPosition);
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (direction == 0) {
|
||||
return null;
|
||||
}
|
||||
if (staggeredGridLayoutManager.getOrientation() == StaggeredGridLayoutManager.HORIZONTAL) {
|
||||
return new PointF(direction, 0);
|
||||
} else {
|
||||
return new PointF(0, direction);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Can't do smoothScrollToPosition for " +
|
||||
layoutManager.getClass().getName());
|
||||
}
|
||||
smoothScroller.setTargetPosition(position);
|
||||
layoutManager.startSmoothScroll(smoothScroller);
|
||||
}
|
||||
|
||||
public static void scrollToPositionProperly(final RecyclerView.LayoutManager layoutManager,
|
||||
final Context context, final int position, final OnScrollToPositionListener listener) {
|
||||
SimpleHandler.getInstance().postDelayed(() -> {
|
||||
int first = getFirstVisibleItemPosition(layoutManager);
|
||||
int last = getLastVisibleItemPosition(layoutManager);
|
||||
int offset = Math.abs(position - first);
|
||||
int max = last - first;
|
||||
if (offset < max && max > 0) {
|
||||
smoothScrollToPosition(layoutManager, context, position,
|
||||
MathUtils.lerp(100, 25, (offset / max)));
|
||||
} else {
|
||||
scrollToPositionWithOffset(layoutManager, position, 0);
|
||||
if (listener != null) {
|
||||
listener.onScrollToPosition(position);
|
||||
}
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
public static int getFirstVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
return ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
|
||||
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
int[] positions = ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(null);
|
||||
return MathUtils.min(positions);
|
||||
} else {
|
||||
throw new IllegalStateException("Can't do getFirstVisibleItemPosition for " +
|
||||
layoutManager.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
return ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
|
||||
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
int[] positions = ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(null);
|
||||
return MathUtils.max(positions);
|
||||
} else {
|
||||
throw new IllegalStateException("Can't do getLastVisibleItemPosition for " +
|
||||
layoutManager.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnScrollToPositionListener {
|
||||
void onScrollToPosition(int position);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.easyrecyclerview;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class MarginItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private int mMargin;
|
||||
private int mPaddingLeft;
|
||||
private int mPaddingTop;
|
||||
private int mPaddingRight;
|
||||
private int mPaddingBottom;
|
||||
|
||||
public MarginItemDecoration(int margin, int paddingLeft, int paddingTop,
|
||||
int paddingRight, int paddingBottom) {
|
||||
setMargin(margin, paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view,
|
||||
RecyclerView parent, RecyclerView.State state) {
|
||||
outRect.set(mMargin, mMargin, mMargin, mMargin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param margin gap between two item
|
||||
* @param paddingLeft gap between RecyclerView left and left item left
|
||||
* @param paddingTop gap between RecyclerView top and top item top
|
||||
* @param paddingRight gap between RecyclerView right and right item right
|
||||
* @param paddingBottom gap between RecyclerView bottom and bottom item bottom
|
||||
*/
|
||||
public void setMargin(int margin, int paddingLeft, int paddingTop,
|
||||
int paddingRight, int paddingBottom) {
|
||||
int halfMargin = margin / 2;
|
||||
mMargin = halfMargin;
|
||||
mPaddingLeft = paddingLeft - halfMargin;
|
||||
mPaddingTop = paddingTop - halfMargin;
|
||||
mPaddingRight = paddingRight - halfMargin;
|
||||
mPaddingBottom = paddingBottom - halfMargin;
|
||||
}
|
||||
|
||||
public void applyPaddings(View view) {
|
||||
view.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.easyrecyclerview;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class SimpleHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public SimpleHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 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.easyrecyclerview;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
|
||||
public abstract class SimpleSmoothScroller extends LinearSmoothScroller {
|
||||
|
||||
private final float mMillisecondsPerPx;
|
||||
|
||||
public SimpleSmoothScroller(Context context, float millisecondsPerInch) {
|
||||
super(context);
|
||||
mMillisecondsPerPx = millisecondsPerInch / context.getResources().getDisplayMetrics().densityDpi;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateTimeForScrolling(int dx) {
|
||||
if (mMillisecondsPerPx <= 0) {
|
||||
return super.calculateTimeForScrolling(dx);
|
||||
} else {
|
||||
return (int) Math.ceil(Math.abs(dx) * mMillisecondsPerPx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,178 +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.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ReadableTime;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.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(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');
|
||||
}
|
||||
if (null != body) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,221 +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.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.util.PackageUtils;
|
||||
import com.hippo.util.ReadableTime;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.OSUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class Crash {
|
||||
private Crash() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String avoidNull(String str) {
|
||||
return null != str ? str : "null";
|
||||
}
|
||||
|
||||
private static void collectInfo(Context context, FileWriter fw) throws IOException {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("PackageName=");
|
||||
fw.write(pi.packageName);
|
||||
fw.write("\r\n");
|
||||
fw.write("VersionName=");
|
||||
fw.write(versionName);
|
||||
fw.write("\r\n");
|
||||
fw.write("VersionCode=");
|
||||
fw.write(versionCode);
|
||||
fw.write("\r\n");
|
||||
String signature = PackageUtils.getSignature(context, pi.packageName);
|
||||
fw.write("Signature=");
|
||||
fw.write(null != signature ? signature : "null");
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("Can't get package information\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
|
||||
// Runtime
|
||||
String topActivityClazzName = "null";
|
||||
String topSceneClazzName = "null";
|
||||
try {
|
||||
Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity();
|
||||
if (null != topActivity) {
|
||||
topActivityClazzName = topActivity.getClass().getName();
|
||||
if (topActivity instanceof StageActivity) {
|
||||
Class<?> clazz = ((StageActivity) topActivity).getTopSceneClass();
|
||||
if (clazz != null) {
|
||||
topSceneClazzName = clazz.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Ignore
|
||||
}
|
||||
fw.write("======== Runtime ========\r\n");
|
||||
fw.write("TopActivity=");
|
||||
fw.write(avoidNull(topActivityClazzName));
|
||||
fw.write("\r\n");
|
||||
fw.write("TopScene=");
|
||||
fw.write(avoidNull(topSceneClazzName));
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
|
||||
// Device info
|
||||
fw.write("======== DeviceInfo ========\r\n");
|
||||
fw.write("BOARD=");
|
||||
fw.write(Build.BOARD);
|
||||
fw.write("\r\n");
|
||||
fw.write("BOOTLOADER=");
|
||||
fw.write(Build.BOOTLOADER);
|
||||
fw.write("\r\n");
|
||||
fw.write("SUPPORTED_ABIS=");
|
||||
fw.write(Arrays.toString(Build.SUPPORTED_ABIS));
|
||||
fw.write("\r\n");
|
||||
fw.write("DEVICE=");
|
||||
fw.write(Build.DEVICE);
|
||||
fw.write("\r\n");
|
||||
fw.write("DISPLAY=");
|
||||
fw.write(Build.DISPLAY);
|
||||
fw.write("\r\n");
|
||||
fw.write("FINGERPRINT=");
|
||||
fw.write(Build.FINGERPRINT);
|
||||
fw.write("\r\n");
|
||||
fw.write("HARDWARE=");
|
||||
fw.write(Build.HARDWARE);
|
||||
fw.write("\r\n");
|
||||
fw.write("HOST=");
|
||||
fw.write(Build.HOST);
|
||||
fw.write("\r\n");
|
||||
fw.write("ID=");
|
||||
fw.write(Build.ID);
|
||||
fw.write("\r\n");
|
||||
fw.write("MANUFACTURER=");
|
||||
fw.write(Build.MANUFACTURER);
|
||||
fw.write("\r\n");
|
||||
fw.write("MODEL=");
|
||||
fw.write(Build.MODEL);
|
||||
fw.write("\r\n");
|
||||
fw.write("PRODUCT=");
|
||||
fw.write(Build.PRODUCT);
|
||||
fw.write("\r\n");
|
||||
fw.write("RADIO=");
|
||||
fw.write(Build.getRadioVersion());
|
||||
fw.write("\r\n");
|
||||
fw.write("TAGS=");
|
||||
fw.write(Build.TAGS);
|
||||
fw.write("\r\n");
|
||||
fw.write("TYPE=");
|
||||
fw.write(Build.TYPE);
|
||||
fw.write("\r\n");
|
||||
fw.write("USER=");
|
||||
fw.write(Build.USER);
|
||||
fw.write("\r\n");
|
||||
fw.write("CODENAME=");
|
||||
fw.write(Build.VERSION.CODENAME);
|
||||
fw.write("\r\n");
|
||||
fw.write("INCREMENTAL=");
|
||||
fw.write(Build.VERSION.INCREMENTAL);
|
||||
fw.write("\r\n");
|
||||
fw.write("RELEASE=");
|
||||
fw.write(Build.VERSION.RELEASE);
|
||||
fw.write("\r\n");
|
||||
fw.write("SDK=");
|
||||
fw.write(Integer.toString(Build.VERSION.SDK_INT));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY=");
|
||||
fw.write(
|
||||
FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_NATIVE=");
|
||||
fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_MAX=");
|
||||
fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_TOTAL=");
|
||||
fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
|
||||
private static void getThrowableInfo(Throwable t, FileWriter fw) {
|
||||
PrintWriter printWriter = new PrintWriter(fw);
|
||||
t.printStackTrace(printWriter);
|
||||
Throwable cause = t.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveCrashLog(Context context, Throwable t) {
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis());
|
||||
String fileName = "crash-" + nowString + ".log";
|
||||
File file = new File(dir, fileName);
|
||||
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = new FileWriter(file);
|
||||
fw.write("TIME=");
|
||||
fw.write(nowString);
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
collectInfo(context, fw);
|
||||
fw.write("======== CrashInfo ========\r\n");
|
||||
getThrowableInfo(t, fw);
|
||||
fw.write("\r\n");
|
||||
fw.flush();
|
||||
} catch (Exception e) {
|
||||
file.delete();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fw);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,200 +16,122 @@
|
||||
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.app.Application
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.text.Html
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.LruCache
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.disk.DiskCache
|
||||
import com.hippo.Native
|
||||
import com.hippo.app.BaseDialogBuilder
|
||||
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.EhRequestBuilder
|
||||
import com.hippo.ehviewer.client.EhSSLSocketFactory
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.EhX509TrustManager
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.parser.EventPaneParser
|
||||
import com.hippo.ehviewer.dao.buildMainDB
|
||||
import com.hippo.ehviewer.coil.MergeInterceptor
|
||||
import com.hippo.ehviewer.coil.installCronetHttpUriFetcher
|
||||
import com.hippo.ehviewer.dailycheck.checkDawn
|
||||
import com.hippo.ehviewer.dao.SearchDatabase
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.spider.SpiderDen
|
||||
import com.hippo.ehviewer.ui.CommonOperations
|
||||
import com.hippo.ehviewer.ui.EhActivity
|
||||
import com.hippo.scene.SceneApplication
|
||||
import com.hippo.util.BitmapUtils
|
||||
import com.hippo.util.ExceptionUtils
|
||||
import com.hippo.util.ReadableTime
|
||||
import com.hippo.yorozuya.FileUtils
|
||||
import com.hippo.yorozuya.IntIdGenerator
|
||||
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
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.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 kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import okhttp3.Cache
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
import java.net.Proxy
|
||||
import java.security.KeyStore
|
||||
import java.util.Arrays
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
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 splitties.arch.room.roomDb
|
||||
import splitties.init.appCtx
|
||||
|
||||
class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
private val mIdGenerator = IntIdGenerator()
|
||||
private val mGlobalStuffMap = HashMap<Int, Any>()
|
||||
private val mActivityList = ArrayList<Activity>()
|
||||
val topActivity: EhActivity?
|
||||
get() = if (mActivityList.isNotEmpty()) {
|
||||
mActivityList[mActivityList.size - 1] as EhActivity
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private val lifecycle = ProcessLifecycleOwner.get().lifecycle
|
||||
private val lifecycleScope = lifecycle.coroutineScope
|
||||
|
||||
fun recreateAllActivity() {
|
||||
mActivityList.forEach { it.recreate() }
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
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 {
|
||||
if (Settings.getSaveCrashLog()) {
|
||||
Crash.saveCrashLog(application, e)
|
||||
if (Settings.saveCrashLog) {
|
||||
Crash.saveCrashLog(e)
|
||||
}
|
||||
} catch (ignored: Throwable) {
|
||||
}
|
||||
handler?.uncaughtException(t, e)
|
||||
}
|
||||
super<SceneApplication>.onCreate()
|
||||
Native.initialize()
|
||||
GetText.initialize(this)
|
||||
Settings.initialize()
|
||||
super.onCreate()
|
||||
CronetProviderInstaller.installProvider(appCtx)
|
||||
System.loadLibrary("ehviewer")
|
||||
System.loadLibrary("ehviewer_rust")
|
||||
ReadableTime.initialize(this)
|
||||
AppConfig.initialize(this)
|
||||
SpiderDen.initialize(this)
|
||||
BitmapUtils.initialize(this)
|
||||
EhTagDatabase.update()
|
||||
AppCompatDelegate.setDefaultNightMode(Settings.getTheme())
|
||||
launchIO {
|
||||
lifecycleScope.launchIO {
|
||||
val theme = Settings.theme
|
||||
withUIContext {
|
||||
AppCompatDelegate.setDefaultNightMode(theme)
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchIO {
|
||||
launchIO {
|
||||
ehDatabase
|
||||
migrateCookies()
|
||||
}
|
||||
launchIO {
|
||||
downloadManager
|
||||
EhTagDatabase.update()
|
||||
}
|
||||
launchIO {
|
||||
EhDB
|
||||
}
|
||||
launchIO {
|
||||
DownloadManager.isIdle
|
||||
}
|
||||
launchIO {
|
||||
cleanupDownload()
|
||||
}
|
||||
launchIO {
|
||||
theDawnOfNewDay()
|
||||
if (Settings.requestNews) {
|
||||
launchIO {
|
||||
checkDawn()
|
||||
}
|
||||
}
|
||||
}
|
||||
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0))
|
||||
cleanObsoleteCache(this)
|
||||
}
|
||||
|
||||
private fun cleanupDownload() {
|
||||
try {
|
||||
val downloadLocation = Settings.getDownloadLocation()
|
||||
if (Settings.getMediaScan()) {
|
||||
CommonOperations.removeNoMediaFile(downloadLocation)
|
||||
} else {
|
||||
CommonOperations.ensureNoMediaFile(downloadLocation)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
ExceptionUtils.throwIfFatal(t)
|
||||
private suspend fun cleanupDownload() {
|
||||
runCatching {
|
||||
keepNoMediaFileStatus()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
|
||||
// Clear temp files
|
||||
try {
|
||||
runCatching {
|
||||
clearTempDir()
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
ExceptionUtils.throwIfFatal(t)
|
||||
}
|
||||
}
|
||||
|
||||
private fun theDawnOfNewDay() {
|
||||
if (!Settings.getRequestNews()) {
|
||||
return
|
||||
}
|
||||
val store = ehCookieStore
|
||||
val eh = EhUrl.HOST_E.toHttpUrl()
|
||||
|
||||
if (store.contains(eh, EhCookieStore.KEY_IPD_MEMBER_ID) || store.contains(
|
||||
eh,
|
||||
EhCookieStore.KEY_IPD_PASS_HASH
|
||||
)
|
||||
) {
|
||||
val referer = EhUrl.REFERER_E
|
||||
val request = EhRequestBuilder(EhUrl.HOST_E + "news.php", referer).build()
|
||||
val call = okHttpClient.newCall(request)
|
||||
try {
|
||||
call.execute().use { response ->
|
||||
val responseBody = response.body
|
||||
val body = responseBody.string()
|
||||
val html = EventPaneParser.parse(body)
|
||||
if (html != null) {
|
||||
showEventPane(html)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showEventPane(html: String) {
|
||||
if (Settings.getHideHvEvents() && html.contains("You have encountered a monster!")) {
|
||||
return
|
||||
}
|
||||
val activity = topActivity
|
||||
activity?.runOnUiThread {
|
||||
val dialog = BaseDialogBuilder(activity)
|
||||
.setMessage(Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create()
|
||||
dialog.setOnShowListener {
|
||||
val messageView = dialog.findViewById<View>(android.R.id.message)
|
||||
if (messageView is TextView) {
|
||||
messageView.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
}
|
||||
try {
|
||||
dialog.show()
|
||||
} catch (t: Throwable) {
|
||||
// ignore
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -222,131 +144,67 @@ class EhApplication : SceneApplication(), DefaultLifecycleObserver, ImageLoaderF
|
||||
}
|
||||
}
|
||||
|
||||
fun putGlobalStuff(o: Any): Int {
|
||||
val id = mIdGenerator.nextId()
|
||||
mGlobalStuffMap[id] = o
|
||||
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId())
|
||||
return id
|
||||
}
|
||||
|
||||
fun containGlobalStuff(id: Int): Boolean {
|
||||
return mGlobalStuffMap.containsKey(id)
|
||||
}
|
||||
|
||||
fun getGlobalStuff(id: Int): Any? {
|
||||
return mGlobalStuffMap[id]
|
||||
}
|
||||
|
||||
fun removeGlobalStuff(id: Int): Any? {
|
||||
return mGlobalStuffMap.remove(id)
|
||||
}
|
||||
|
||||
fun removeGlobalStuff(o: Any) {
|
||||
mGlobalStuffMap.values.removeAll(setOf(o))
|
||||
}
|
||||
|
||||
fun registerActivity(activity: Activity) {
|
||||
mActivityList.add(activity)
|
||||
}
|
||||
|
||||
fun unregisterActivity(activity: Activity) {
|
||||
mActivityList.remove(activity)
|
||||
}
|
||||
|
||||
override fun onPause(owner: LifecycleOwner) {
|
||||
if (!locked)
|
||||
locked_last_leave_time = System.currentTimeMillis() / 1000
|
||||
locked = true
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader.Builder(this).apply {
|
||||
okHttpClient(okHttpClient)
|
||||
allowRgb565(getSystemService<ActivityManager>()!!.isLowRamDevice)
|
||||
// Coil spawns a new thread for every image load by default
|
||||
fetcherDispatcher(Dispatchers.IO.limitedParallelism(8))
|
||||
decoderDispatcher(Dispatchers.IO.limitedParallelism(2))
|
||||
transformationDispatcher(Dispatchers.IO.limitedParallelism(2))
|
||||
}.build()
|
||||
override fun newImageLoader() = imageLoader {
|
||||
components {
|
||||
if (isCronetSupported) {
|
||||
callFactory { unreachable() }
|
||||
installCronetHttpUriFetcher()
|
||||
} else {
|
||||
callFactory(baseOkHttpClient)
|
||||
}
|
||||
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 {
|
||||
private const val KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id"
|
||||
var locked = true
|
||||
var locked_last_leave_time: Long = 0
|
||||
|
||||
@JvmStatic
|
||||
lateinit var application: EhApplication
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
val ehCookieStore by lazy { EhCookieStore(application) }
|
||||
|
||||
@JvmStatic
|
||||
val ehProxySelector by lazy { EhProxySelector() }
|
||||
|
||||
@JvmStatic
|
||||
val okHttpClient by lazy {
|
||||
val builder = OkHttpClient.Builder()
|
||||
.cookieJar(ehCookieStore)
|
||||
.cache(Cache(File(application.cacheDir, "http_cache"), 50L * 1024L * 1024L))
|
||||
.dns(EhDns)
|
||||
.proxySelector(ehProxySelector)
|
||||
|
||||
if (Settings.getDF()) {
|
||||
var trustManager: X509TrustManager
|
||||
try {
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(
|
||||
TrustManagerFactory.getDefaultAlgorithm()
|
||||
)
|
||||
trustManagerFactory.init(null as KeyStore?)
|
||||
val trustManagers = trustManagerFactory.trustManagers
|
||||
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
|
||||
"Unexpected default trust managers:" + Arrays.toString(
|
||||
trustManagers
|
||||
)
|
||||
}
|
||||
trustManager = trustManagers[0] as X509TrustManager
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
trustManager = EhX509TrustManager
|
||||
val baseOkHttpClient by lazy {
|
||||
httpClient {
|
||||
cookieJar(EhCookieStore)
|
||||
if (isAtLeastQ) {
|
||||
dns(AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6))
|
||||
}
|
||||
chunker { alwaysReadResponseBody(false) }
|
||||
addInterceptor(CloudflareInterceptor(appCtx))
|
||||
}
|
||||
}
|
||||
|
||||
builder.sslSocketFactory(EhSSLSocketFactory, trustManager)
|
||||
builder.proxy(Proxy.NO_PROXY)
|
||||
val noRedirectOkHttpClient by lazy {
|
||||
httpClient(baseOkHttpClient) {
|
||||
followRedirects(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Never use this okhttp client to download large blobs!!!
|
||||
val okHttpClient by lazy {
|
||||
httpClient(baseOkHttpClient) {
|
||||
cache(
|
||||
appCtx.cacheDir.toOkioPath() / "http_cache",
|
||||
20L * 1024L * 1024L,
|
||||
)
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val spiderInfoCache by lazy {
|
||||
DiskCache.Builder().directory(File(application.cacheDir, "spider_info"))
|
||||
.maxSizeBytes(20 * 1024 * 1024).build()
|
||||
val imageCache by lazy {
|
||||
diskCache {
|
||||
directory(appCtx.cacheDir.toOkioPath() / "image_cache")
|
||||
maxSizeBytes(Settings.readCacheSize.coerceIn(320, 5120).toLong() * 1024 * 1024)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val downloadManager by lazy { DownloadManager() }
|
||||
|
||||
@JvmStatic
|
||||
val hosts by lazy { Hosts(application, "hosts.db") }
|
||||
|
||||
@JvmStatic
|
||||
val favouriteStatusRouter by lazy { FavouriteStatusRouter() }
|
||||
|
||||
@JvmStatic
|
||||
val readerPreferences by lazy { ReaderPreferences(AndroidPreferenceStore(application)) }
|
||||
|
||||
@JvmStatic
|
||||
val ehDatabase by lazy { buildMainDB(application) }
|
||||
val searchDatabase by lazy { roomDb<SearchDatabase>("search_database.db") }
|
||||
}
|
||||
}
|
||||
@@ -1,571 +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.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.paging.PagingSource;
|
||||
import androidx.room.Room;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.dao.BasicDao;
|
||||
import com.hippo.ehviewer.dao.DownloadDirname;
|
||||
import com.hippo.ehviewer.dao.DownloadDirnameDao;
|
||||
import com.hippo.ehviewer.dao.DownloadInfo;
|
||||
import com.hippo.ehviewer.dao.DownloadLabel;
|
||||
import com.hippo.ehviewer.dao.DownloadLabelDao;
|
||||
import com.hippo.ehviewer.dao.DownloadsDao;
|
||||
import com.hippo.ehviewer.dao.EhDatabase;
|
||||
import com.hippo.ehviewer.dao.Filter;
|
||||
import com.hippo.ehviewer.dao.HistoryDao;
|
||||
import com.hippo.ehviewer.dao.HistoryInfo;
|
||||
import com.hippo.ehviewer.dao.LocalFavoriteInfo;
|
||||
import com.hippo.ehviewer.dao.LocalFavoritesDao;
|
||||
import com.hippo.ehviewer.dao.QuickSearch;
|
||||
import com.hippo.ehviewer.dao.QuickSearchDao;
|
||||
import com.hippo.ehviewer.download.DownloadManager;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.ObjectUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EhDB {
|
||||
private static final int CUR_DB_VER = 4;
|
||||
|
||||
private static final EhDatabase db = EhApplication.getEhDatabase();
|
||||
|
||||
private static void upgradeDB(SQLiteDatabase db, int oldVersion) {
|
||||
switch (oldVersion) {
|
||||
case 1: // 1 to 2, add FILTER
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS \"FILTER\" (" + //
|
||||
"\"_id\" INTEGER PRIMARY KEY ," + // 0: id
|
||||
"\"MODE\" INTEGER NOT NULL ," + // 1: mode
|
||||
"\"TEXT\" TEXT," + // 2: text
|
||||
"\"ENABLE\" INTEGER);"); // 3: enable
|
||||
case 2: // 2 to 3, add ENABLE column to table FILTER
|
||||
db.execSQL("CREATE TABLE " + "\"FILTER2\" (" +
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"MODE\" INTEGER NOT NULL ," +
|
||||
"\"TEXT\" TEXT," +
|
||||
"\"ENABLE\" INTEGER);");
|
||||
db.execSQL("INSERT INTO \"FILTER2\" (" +
|
||||
"_id, MODE, TEXT, ENABLE)" +
|
||||
"SELECT _id, MODE, TEXT, 1 FROM FILTER;");
|
||||
db.execSQL("DROP TABLE FILTER");
|
||||
db.execSQL("ALTER TABLE FILTER2 RENAME TO FILTER");
|
||||
case 3: // 3 to 4, add PAGE_FROM and PAGE_TO column to QUICK_SEARCH
|
||||
db.execSQL("CREATE TABLE " + "\"QUICK_SEARCH2\" (" +
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"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 );");
|
||||
db.execSQL("INSERT INTO \"QUICK_SEARCH2\" (" +
|
||||
"_id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, PAGE_FROM, PAGE_TO, TIME)" +
|
||||
"SELECT _id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, -1, -1, TIME FROM QUICK_SEARCH;");
|
||||
db.execSQL("DROP TABLE QUICK_SEARCH");
|
||||
db.execSQL("ALTER TABLE QUICK_SEARCH2 RENAME TO QUICK_SEARCH");
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized List<DownloadInfo> getAllDownloadInfo() {
|
||||
DownloadsDao dao = db.downloadsDao();
|
||||
List<DownloadInfo> list = dao.list();
|
||||
// Fix state
|
||||
for (DownloadInfo info : list) {
|
||||
if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
info.state = DownloadInfo.STATE_NONE;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// Insert or update
|
||||
public static synchronized void putDownloadInfo(DownloadInfo downloadInfo) {
|
||||
DownloadsDao dao = db.downloadsDao();
|
||||
if (null != dao.load(downloadInfo.getGid())) {
|
||||
// Update
|
||||
dao.update(downloadInfo);
|
||||
} else {
|
||||
// Insert
|
||||
dao.insert(downloadInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void removeDownloadInfo(DownloadInfo downloadInfo) {
|
||||
db.downloadsDao().delete(downloadInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static synchronized String getDownloadDirname(long gid) {
|
||||
DownloadDirnameDao dao = db.downloadDirnameDao();
|
||||
DownloadDirname raw = dao.load(gid);
|
||||
if (raw != null) {
|
||||
return raw.getDirname();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update
|
||||
*/
|
||||
public static synchronized void putDownloadDirname(long gid, String dirname) {
|
||||
DownloadDirnameDao dao = db.downloadDirnameDao();
|
||||
DownloadDirname raw = dao.load(gid);
|
||||
if (raw != null) { // Update
|
||||
raw.setDirname(dirname);
|
||||
dao.update(raw);
|
||||
} else { // Insert
|
||||
raw = new DownloadDirname();
|
||||
raw.setGid(gid);
|
||||
raw.setDirname(dirname);
|
||||
dao.insert(raw);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void removeDownloadDirname(long gid) {
|
||||
DownloadDirnameDao dao = db.downloadDirnameDao();
|
||||
dao.deleteByKey(gid);
|
||||
}
|
||||
|
||||
public static synchronized void clearDownloadDirname() {
|
||||
DownloadDirnameDao dao = db.downloadDirnameDao();
|
||||
dao.deleteAll();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static synchronized List<DownloadLabel> getAllDownloadLabelList() {
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
return dao.list();
|
||||
}
|
||||
|
||||
public static synchronized DownloadLabel addDownloadLabel(String label) {
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
DownloadLabel raw = new DownloadLabel();
|
||||
raw.setLabel(label);
|
||||
raw.setTime(System.currentTimeMillis());
|
||||
raw.setId(dao.insert(raw));
|
||||
return raw;
|
||||
}
|
||||
|
||||
public static synchronized DownloadLabel addDownloadLabel(DownloadLabel raw) {
|
||||
// Reset id
|
||||
raw.setId(null);
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
raw.setId(dao.insert(raw));
|
||||
return raw;
|
||||
}
|
||||
|
||||
public static synchronized void updateDownloadLabel(DownloadLabel raw) {
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
dao.update(raw);
|
||||
}
|
||||
|
||||
public static synchronized void moveDownloadLabel(int fromPosition, int toPosition) {
|
||||
if (fromPosition == toPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean reverse = fromPosition > toPosition;
|
||||
int offset = reverse ? toPosition : fromPosition;
|
||||
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
|
||||
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
List<DownloadLabel> list = dao.list(offset, limit);
|
||||
|
||||
int step = reverse ? 1 : -1;
|
||||
int start = reverse ? limit - 1 : 0;
|
||||
int end = reverse ? 0 : limit - 1;
|
||||
long toTime = list.get(end).getTime();
|
||||
for (int i = end; reverse ? i < start : i > start; i += step) {
|
||||
list.get(i).setTime(list.get(i + step).getTime());
|
||||
}
|
||||
list.get(start).setTime(toTime);
|
||||
|
||||
dao.update(list);
|
||||
}
|
||||
|
||||
public static synchronized void removeDownloadLabel(DownloadLabel raw) {
|
||||
DownloadLabelDao dao = db.downloadLabelDao();
|
||||
dao.delete(raw);
|
||||
}
|
||||
|
||||
public static synchronized List<GalleryInfo> getAllLocalFavorites() {
|
||||
LocalFavoritesDao dao = db.localFavoritesDao();
|
||||
List<LocalFavoriteInfo> list = dao.list();
|
||||
return new ArrayList<>(list);
|
||||
}
|
||||
|
||||
public static synchronized List<GalleryInfo> searchLocalFavorites(String query) {
|
||||
LocalFavoritesDao dao = db.localFavoritesDao();
|
||||
List<LocalFavoriteInfo> list = dao.list("%" + query + "%");
|
||||
return new ArrayList<>(list);
|
||||
}
|
||||
|
||||
public static synchronized void removeLocalFavorites(long gid) {
|
||||
db.localFavoritesDao().deleteByKey(gid);
|
||||
}
|
||||
|
||||
public static synchronized void removeLocalFavorites(long[] gidArray) {
|
||||
LocalFavoritesDao dao = db.localFavoritesDao();
|
||||
for (long gid : gidArray) {
|
||||
dao.deleteByKey(gid);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized boolean containLocalFavorites(long gid) {
|
||||
LocalFavoritesDao dao = db.localFavoritesDao();
|
||||
return null != dao.load(gid);
|
||||
}
|
||||
|
||||
public static synchronized void putLocalFavorites(GalleryInfo galleryInfo) {
|
||||
LocalFavoritesDao dao = db.localFavoritesDao();
|
||||
if (null == dao.load(galleryInfo.getGid())) {
|
||||
LocalFavoriteInfo info;
|
||||
if (galleryInfo instanceof LocalFavoriteInfo) {
|
||||
info = (LocalFavoriteInfo) galleryInfo;
|
||||
} else {
|
||||
info = new LocalFavoriteInfo(galleryInfo);
|
||||
info.time = System.currentTimeMillis();
|
||||
}
|
||||
dao.insert(info);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void putLocalFavorites(List<GalleryInfo> galleryInfoList) {
|
||||
for (GalleryInfo gi : galleryInfoList) {
|
||||
putLocalFavorites(gi);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized List<QuickSearch> getAllQuickSearch() {
|
||||
QuickSearchDao dao = db.quickSearchDao();
|
||||
return dao.list();
|
||||
}
|
||||
|
||||
public static synchronized void insertQuickSearch(QuickSearch quickSearch) {
|
||||
QuickSearchDao dao = db.quickSearchDao();
|
||||
quickSearch.setId(null);
|
||||
quickSearch.setTime(System.currentTimeMillis());
|
||||
quickSearch.setId(dao.insert(quickSearch));
|
||||
}
|
||||
|
||||
public static synchronized void importQuickSearch(List<QuickSearch> quickSearchList) {
|
||||
QuickSearchDao dao = db.quickSearchDao();
|
||||
for (QuickSearch quickSearch : quickSearchList) {
|
||||
dao.insert(quickSearch);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void deleteQuickSearch(QuickSearch quickSearch) {
|
||||
QuickSearchDao dao = db.quickSearchDao();
|
||||
dao.delete(quickSearch);
|
||||
}
|
||||
|
||||
public static synchronized void moveQuickSearch(int fromPosition, int toPosition) {
|
||||
if (fromPosition == toPosition) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean reverse = fromPosition > toPosition;
|
||||
int offset = reverse ? toPosition : fromPosition;
|
||||
int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1;
|
||||
|
||||
QuickSearchDao dao = db.quickSearchDao();
|
||||
List<QuickSearch> list = dao.list(offset, limit);
|
||||
|
||||
int step = reverse ? 1 : -1;
|
||||
int start = reverse ? limit - 1 : 0;
|
||||
int end = reverse ? 0 : limit - 1;
|
||||
long toTime = list.get(end).getTime();
|
||||
for (int i = end; reverse ? i < start : i > start; i += step) {
|
||||
list.get(i).setTime(list.get(i + step).getTime());
|
||||
}
|
||||
list.get(start).setTime(toTime);
|
||||
|
||||
dao.update(list);
|
||||
}
|
||||
|
||||
public static synchronized PagingSource<Integer, HistoryInfo> getHistoryLazyList() {
|
||||
return db.historyDao().listLazy();
|
||||
}
|
||||
|
||||
public static synchronized void putHistoryInfo(GalleryInfo galleryInfo) {
|
||||
HistoryDao dao = db.historyDao();
|
||||
HistoryInfo info;
|
||||
if (galleryInfo instanceof HistoryInfo) {
|
||||
info = (HistoryInfo) galleryInfo;
|
||||
} else {
|
||||
info = new HistoryInfo(galleryInfo);
|
||||
}
|
||||
info.time = System.currentTimeMillis();
|
||||
if (null != dao.load(info.getGid())) {
|
||||
dao.update(info);
|
||||
} else {
|
||||
dao.insert(info);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void putHistoryInfo(List<HistoryInfo> historyInfoList) {
|
||||
HistoryDao dao = db.historyDao();
|
||||
for (HistoryInfo info : historyInfoList) {
|
||||
if (null == dao.load(info.getGid())) {
|
||||
dao.insert(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void deleteHistoryInfo(HistoryInfo info) {
|
||||
HistoryDao dao = db.historyDao();
|
||||
dao.delete(info);
|
||||
}
|
||||
|
||||
public static synchronized void clearHistoryInfo() {
|
||||
HistoryDao dao = db.historyDao();
|
||||
dao.deleteAll();
|
||||
}
|
||||
|
||||
public static synchronized List<Filter> getAllFilter() {
|
||||
return db.filterDao().list();
|
||||
}
|
||||
|
||||
public static synchronized boolean addFilter(Filter filter) {
|
||||
Filter existFilter;
|
||||
try {
|
||||
existFilter = db.filterDao().load(filter.text, filter.mode);
|
||||
} catch (Exception e) {
|
||||
existFilter = null;
|
||||
}
|
||||
if (existFilter == null) {
|
||||
filter.setId(null);
|
||||
filter.setId(db.filterDao().insert(filter));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void deleteFilter(Filter filter) {
|
||||
db.filterDao().delete(filter);
|
||||
}
|
||||
|
||||
public static synchronized void triggerFilter(Filter filter) {
|
||||
filter.enable = !filter.enable;
|
||||
db.filterDao().update(filter);
|
||||
}
|
||||
|
||||
private static <T> void copyDao(BasicDao<T> from, BasicDao<T> to) {
|
||||
List<T> list = from.list();
|
||||
for (T item : list)
|
||||
to.insert(item);
|
||||
}
|
||||
|
||||
public static synchronized boolean exportDB(Context context, Uri uri) {
|
||||
final String ehExportName = "eh.export.db";
|
||||
|
||||
// Delete old export db
|
||||
context.deleteDatabase(ehExportName);
|
||||
EhDatabase newDb = Room.databaseBuilder(context, EhDatabase.class, ehExportName).allowMainThreadQueries().build();
|
||||
|
||||
try {
|
||||
// Copy data to a export db
|
||||
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());
|
||||
|
||||
// Close export db so we can copy it
|
||||
if (newDb.isOpen()) {
|
||||
newDb.close();
|
||||
}
|
||||
|
||||
// Copy export db to data dir
|
||||
File dbFile = context.getDatabasePath(ehExportName);
|
||||
if (dbFile == null || !dbFile.isFile()) {
|
||||
return false;
|
||||
}
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = new FileInputStream(dbFile);
|
||||
os = context.getContentResolver().openOutputStream(uri);
|
||||
IOUtils.copy(is, os);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
|
||||
// Delete failed file
|
||||
return false;
|
||||
} finally {
|
||||
context.deleteDatabase(ehExportName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return error string, null for no error
|
||||
*/
|
||||
public static synchronized String importDB(Context context, Uri uri) {
|
||||
try {
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(uri);
|
||||
|
||||
// Copy file to cache dir
|
||||
File file = File.createTempFile("importDatabase", "");
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
byte[] buff = new byte[1024];
|
||||
int read;
|
||||
if (inputStream != null) {
|
||||
while ((read = inputStream.read(buff, 0, buff.length)) > 0) {
|
||||
outputStream.write(buff, 0, read);
|
||||
}
|
||||
} else {
|
||||
return context.getString(R.string.cant_read_the_file);
|
||||
}
|
||||
inputStream.close();
|
||||
outputStream.close();
|
||||
|
||||
// Check database version
|
||||
SQLiteDatabase oldDB = SQLiteDatabase.openDatabase(
|
||||
file.getPath(), null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
|
||||
int newVersion = CUR_DB_VER;
|
||||
int oldVersion = oldDB.getVersion();
|
||||
if (oldVersion < newVersion) {
|
||||
upgradeDB(oldDB, oldVersion);
|
||||
oldDB.setVersion(newVersion);
|
||||
} else if (oldVersion > newVersion) {
|
||||
return context.getString(R.string.db_version_too_high);
|
||||
}
|
||||
|
||||
// Crete temp room database from cache file
|
||||
String tmpDBName = "tmp.db";
|
||||
context.deleteDatabase(tmpDBName);
|
||||
EhDatabase oldRoomDatabase = Room.databaseBuilder(context, EhDatabase.class, tmpDBName)
|
||||
.createFromFile(file).allowMainThreadQueries().build();
|
||||
|
||||
// Download label
|
||||
DownloadManager manager = EhApplication.getDownloadManager();
|
||||
try {
|
||||
List<DownloadLabel> downloadLabelList = oldRoomDatabase.downloadLabelDao().list();
|
||||
manager.addDownloadLabel(downloadLabelList);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// Downloads
|
||||
try {
|
||||
List<DownloadInfo> downloadInfoList = oldRoomDatabase.downloadsDao().list();
|
||||
manager.addDownload(downloadInfoList, false);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// Download dirname
|
||||
try {
|
||||
List<DownloadDirname> downloadDirnameList = oldRoomDatabase.downloadDirnameDao().list();
|
||||
for (DownloadDirname dirname : downloadDirnameList) {
|
||||
putDownloadDirname(dirname.getGid(), dirname.getDirname());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// History
|
||||
try {
|
||||
List<HistoryInfo> historyInfoList = oldRoomDatabase.historyDao().list();
|
||||
putHistoryInfo(historyInfoList);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// QuickSearch
|
||||
try {
|
||||
List<QuickSearch> quickSearchList = oldRoomDatabase.quickSearchDao().list();
|
||||
List<QuickSearch> currentQuickSearchList = db.quickSearchDao().list();
|
||||
List<QuickSearch> importList = new ArrayList<>();
|
||||
for (QuickSearch quickSearch : quickSearchList) {
|
||||
String name = quickSearch.getName();
|
||||
for (QuickSearch q : currentQuickSearchList) {
|
||||
if (ObjectUtils.equal(q.getName(), name)) {
|
||||
// The same name
|
||||
name = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == name) {
|
||||
continue;
|
||||
}
|
||||
importList.add(quickSearch);
|
||||
}
|
||||
importQuickSearch(importList);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// LocalFavorites
|
||||
try {
|
||||
List<LocalFavoriteInfo> localFavoriteInfoList = oldRoomDatabase.localFavoritesDao().list();
|
||||
for (LocalFavoriteInfo info : localFavoriteInfoList) {
|
||||
putLocalFavorites(info);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// Filter
|
||||
try {
|
||||
List<Filter> filterList = oldRoomDatabase.filterDao().list();
|
||||
List<Filter> currentFilterList = db.filterDao().list();
|
||||
for (Filter filter : filterList) {
|
||||
if (!currentFilterList.contains(filter)) {
|
||||
addFilter(filter);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// Delete temp database
|
||||
if (oldRoomDatabase.isOpen()) {
|
||||
oldRoomDatabase.close();
|
||||
}
|
||||
context.deleteDatabase(tmpDBName);
|
||||
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
// Ignore
|
||||
return context.getString(R.string.cant_read_the_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.ParcelFileDescriptor.MODE_READ_ONLY
|
||||
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.EhDatabase
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.dao.HistoryInfo
|
||||
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 val db = roomDb<EhDatabase>("eh.db")
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateDownloadInfo(downloadInfo: List<DownloadInfo>) {
|
||||
val dao = db.downloadsDao()
|
||||
dao.update(downloadInfo.map(DownloadInfo::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)
|
||||
}
|
||||
|
||||
suspend fun getDownloadDirname(gid: Long): String? {
|
||||
val dao = db.downloadDirnameDao()
|
||||
val raw = dao.load(gid)
|
||||
return raw?.dirname
|
||||
}
|
||||
|
||||
suspend fun putDownloadDirname(gid: Long, dirname: String) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
dao.upsert(DownloadDirname(gid, dirname))
|
||||
}
|
||||
|
||||
suspend fun removeDownloadDirname(gid: Long) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
dao.deleteByKey(gid)
|
||||
}
|
||||
|
||||
private suspend fun importDownloadDirname(downloadDirnameList: List<DownloadDirname>) {
|
||||
val dao = db.downloadDirnameDao()
|
||||
dao.insertOrIgnore(downloadDirnameList)
|
||||
}
|
||||
|
||||
suspend fun getAllDownloadLabelList() = db.downloadLabelDao().list()
|
||||
|
||||
suspend fun addDownloadLabel(raw: DownloadLabel): DownloadLabel {
|
||||
// Reset id
|
||||
raw.id = null
|
||||
val dao = db.downloadLabelDao()
|
||||
raw.id = dao.insert(raw)
|
||||
return raw
|
||||
}
|
||||
|
||||
suspend fun updateDownloadLabel(raw: DownloadLabel) {
|
||||
val dao = db.downloadLabelDao()
|
||||
dao.update(raw)
|
||||
}
|
||||
|
||||
suspend fun updateDownloadLabel(downloadLabels: List<DownloadLabel>) {
|
||||
val dao = db.downloadLabelDao()
|
||||
dao.update(downloadLabels)
|
||||
}
|
||||
|
||||
suspend fun removeDownloadLabel(raw: DownloadLabel) {
|
||||
val dao = db.downloadLabelDao()
|
||||
dao.delete(raw)
|
||||
dao.fill(raw.position)
|
||||
}
|
||||
|
||||
suspend fun removeLocalFavorites(galleryInfo: BaseGalleryInfo) {
|
||||
db.localFavoritesDao().deleteByKey(galleryInfo.gid)
|
||||
deleteGalleryInfo(galleryInfo)
|
||||
}
|
||||
|
||||
suspend fun removeLocalFavorites(galleryInfoList: List<BaseGalleryInfo>) {
|
||||
galleryInfoList.forEach {
|
||||
removeLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun containLocalFavorites(gid: Long): Boolean {
|
||||
val dao = db.localFavoritesDao()
|
||||
return dao.contains(gid)
|
||||
}
|
||||
|
||||
suspend fun putLocalFavorites(galleryInfo: BaseGalleryInfo) {
|
||||
putGalleryInfo(galleryInfo)
|
||||
db.localFavoritesDao().upsert(LocalFavoriteInfo(galleryInfo.gid))
|
||||
}
|
||||
|
||||
suspend fun putLocalFavorites(galleryInfoList: List<BaseGalleryInfo>) {
|
||||
galleryInfoList.forEach {
|
||||
putLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun importLocalFavorites(localFavorites: List<LocalFavoriteInfo>) {
|
||||
db.localFavoritesDao().insertOrIgnore(localFavorites)
|
||||
}
|
||||
|
||||
suspend fun getAllQuickSearch() = db.quickSearchDao().list()
|
||||
|
||||
suspend fun insertQuickSearch(quickSearch: QuickSearch) {
|
||||
val dao = db.quickSearchDao()
|
||||
quickSearch.id = dao.insert(quickSearch)
|
||||
}
|
||||
|
||||
private suspend fun importQuickSearch(quickSearchList: List<QuickSearch>) {
|
||||
val dao = db.quickSearchDao()
|
||||
dao.insert(quickSearchList)
|
||||
}
|
||||
|
||||
suspend fun deleteQuickSearch(quickSearch: QuickSearch) {
|
||||
val dao = db.quickSearchDao()
|
||||
dao.delete(quickSearch)
|
||||
dao.fill(quickSearch.position)
|
||||
}
|
||||
|
||||
suspend fun updateQuickSearch(quickSearchList: List<QuickSearch>) {
|
||||
val dao = db.quickSearchDao()
|
||||
dao.update(quickSearchList)
|
||||
}
|
||||
|
||||
val historyLazyList
|
||||
get() = db.historyDao().joinListLazy()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun importHistoryInfo(historyInfoList: List<HistoryInfo>) {
|
||||
val dao = db.historyDao()
|
||||
dao.insertOrIgnore(historyInfoList)
|
||||
}
|
||||
|
||||
suspend fun deleteHistoryInfo(galleryInfo: BaseGalleryInfo) {
|
||||
val dao = db.historyDao()
|
||||
dao.deleteByKey(galleryInfo.gid)
|
||||
deleteGalleryInfo(galleryInfo)
|
||||
}
|
||||
|
||||
suspend fun clearHistoryInfo() {
|
||||
val dao = db.historyDao()
|
||||
val historyList = dao.list()
|
||||
dao.deleteAll()
|
||||
historyList.forEach { runCatching { db.galleryDao().deleteByKey(it.gid) } }
|
||||
}
|
||||
|
||||
suspend fun getAllFilter() = db.filterDao().list()
|
||||
|
||||
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)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteFilter(filter: Filter) {
|
||||
db.filterDao().delete(filter)
|
||||
}
|
||||
|
||||
suspend fun updateFilter(filter: Filter) {
|
||||
db.filterDao().update(filter)
|
||||
}
|
||||
|
||||
suspend fun exportDB(context: Context, uri: Uri) {
|
||||
val ehExportName = "eh.export.db"
|
||||
resource {
|
||||
context.deleteDatabase(ehExportName)
|
||||
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 ->
|
||||
ParcelFileDescriptor.open(dbFile, MODE_READ_ONLY).use { fromFd ->
|
||||
fromFd sendTo toFd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
DownloadManager.addDownloadLabel(downloadLabelList)
|
||||
}
|
||||
runCatching {
|
||||
oldDB.downloadDirnameDao().list().let {
|
||||
importDownloadDirname(it)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val downloadInfoList = oldDB.downloadsDao().joinList().asReversed()
|
||||
DownloadManager.addDownload(downloadInfoList, false)
|
||||
}
|
||||
runCatching {
|
||||
val historyInfoList = oldDB.historyDao().list()
|
||||
importHistoryInfo(historyInfoList)
|
||||
}
|
||||
runCatching {
|
||||
val quickSearchList = oldDB.quickSearchDao().list()
|
||||
val currentQuickSearchList = db.quickSearchDao().list()
|
||||
val offset = currentQuickSearchList.size
|
||||
val importList = quickSearchList.filter { newQS ->
|
||||
currentQuickSearchList.none { it.name == newQS.name }
|
||||
}.onEachIndexed { index, q -> q.position = index + offset }
|
||||
importQuickSearch(importList)
|
||||
}
|
||||
runCatching {
|
||||
oldDB.localFavoritesDao().list().let {
|
||||
importLocalFavorites(it)
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val filterList = oldDB.filterDao().list()
|
||||
val currentFilterList = db.filterDao().list()
|
||||
filterList.forEach {
|
||||
if (it !in currentFilterList) addFilter(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +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.network.InetValidator;
|
||||
import com.hippo.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;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class EhProxySelector extends ProxySelector {
|
||||
|
||||
public static final int TYPE_DIRECT = 0;
|
||||
public static final int TYPE_SYSTEM = 1;
|
||||
public static final int TYPE_HTTP = 2;
|
||||
public static final int TYPE_SOCKS = 3;
|
||||
|
||||
private ProxySelector delegation;
|
||||
private ProxySelector alternative;
|
||||
|
||||
EhProxySelector() {
|
||||
alternative = ProxySelector.getDefault();
|
||||
if (alternative == null) {
|
||||
alternative = new NullProxySelector();
|
||||
}
|
||||
|
||||
updateProxy();
|
||||
}
|
||||
|
||||
public void updateProxy() {
|
||||
switch (Settings.getProxyType()) {
|
||||
case TYPE_DIRECT:
|
||||
delegation = new NullProxySelector();
|
||||
break;
|
||||
default:
|
||||
case TYPE_SYSTEM:
|
||||
delegation = alternative;
|
||||
break;
|
||||
case TYPE_HTTP:
|
||||
case TYPE_SOCKS:
|
||||
delegation = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
int type = Settings.getProxyType();
|
||||
if (type == TYPE_HTTP || type == TYPE_SOCKS) {
|
||||
try {
|
||||
String ip = Settings.getProxyIp();
|
||||
int port = Settings.getProxyPort();
|
||||
if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) {
|
||||
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||
SocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
|
||||
return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (delegation != null) {
|
||||
return delegation.select(uri);
|
||||
}
|
||||
|
||||
return alternative.select(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
if (delegation != null) {
|
||||
delegation.select(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullProxySelector extends ProxySelector {
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
return Collections.singletonList(Proxy.NO_PROXY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.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,42 +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.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
public class GetText {
|
||||
|
||||
private static Resources sResources;
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sResources = context.getApplicationContext().getResources();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getString(@StringRes int id) {
|
||||
return sResources.getString(id);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getString(@StringRes int id, Object... formatArgs) {
|
||||
return sResources.getString(id, formatArgs);
|
||||
}
|
||||
}
|
||||
@@ -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.database.MSQLiteBuilder;
|
||||
import com.hippo.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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,739 +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.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.hippo.ehviewer.client.EhConfig;
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
|
||||
import com.hippo.ehviewer.ui.CommonOperations;
|
||||
import com.hippo.ehviewer.ui.scene.GalleryListScene;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.AssertUtils;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class Settings {
|
||||
|
||||
/********************
|
||||
****** Eh
|
||||
********************/
|
||||
|
||||
public static final String KEY_THEME = "theme";
|
||||
|
||||
public static final String KEY_ACCOUNT = "account";
|
||||
public static final String KEY_BLACK_DARK_THEME = "black_dark_theme";
|
||||
public static final int THEME_SYSTEM = -1;
|
||||
public static final String KEY_GALLERY_SITE = "gallery_site";
|
||||
public static final String KEY_LIST_MODE = "list_mode";
|
||||
public static final String KEY_DETAIL_SIZE = "detail_size";
|
||||
public static final String KEY_THUMB_SIZE = "thumb_size_";
|
||||
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
|
||||
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
|
||||
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
|
||||
public static final int DEFAULT_DEFAULT_CATEGORIES = EhConfig.ALL_CATEGORY;
|
||||
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
|
||||
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
|
||||
/********************
|
||||
****** Privacy and Security
|
||||
********************/
|
||||
public static final String KEY_SEC_SECURITY = "enable_secure";
|
||||
public static final boolean VALUE_SEC_SECURITY = false;
|
||||
/********************
|
||||
****** Download
|
||||
********************/
|
||||
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
|
||||
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
|
||||
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
|
||||
public static final String KEY_NOTIFICATION_REQUIRED = "notification_required";
|
||||
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
|
||||
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
|
||||
public static final String KEY_MEDIA_SCAN = "media_scan";
|
||||
public static final String KEY_IMAGE_RESOLUTION = "image_size";
|
||||
public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO;
|
||||
public static final int INVALID_DEFAULT_FAV_SLOT = -2;
|
||||
/********************
|
||||
****** Advanced
|
||||
********************/
|
||||
public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body";
|
||||
public static final String KEY_SECURITY = "require_unlock";
|
||||
public static final String KEY_SECURITY_DELAY = "require_unlock_delay";
|
||||
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
|
||||
public static final int DEFAULT_READ_CACHE_SIZE = 320;
|
||||
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts_2";
|
||||
public static final String KEY_DOMAIN_FRONTING = "domain_fronting";
|
||||
public static final String KEY_LIST_THUMB_SIZE = "list_tile_size";
|
||||
private static final String KEY_HIDE_HV_EVENTS = "hide_hv_events";
|
||||
private static final boolean DEFAULT_HIDE_HV_EVENTS = true;
|
||||
private static final String KEY_SHOW_COMMENTS = "show_gallery_comments";
|
||||
private static final boolean DEFAULT_SHOW_COMMENTS = true;
|
||||
private static final String KEY_PREVIEW_NUM = "preview_num";
|
||||
private static final int DEFAULT_PREVIEW_NUM = 60;
|
||||
private static final String TAG = Settings.class.getSimpleName();
|
||||
private static final String KEY_DISPLAY_NAME = "display_name";
|
||||
private static final String DEFAULT_DISPLAY_NAME = null;
|
||||
private static final int DEFAULT_LIST_THUMB_SIZE = 40;
|
||||
private static final String KEY_AVATAR = "avatar";
|
||||
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
|
||||
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
|
||||
private static final String KEY_NEED_SIGN_IN = "need_sign_in";
|
||||
private static final boolean DEFAULT_NEED_SIGN_IN = true;
|
||||
private static final String KEY_SELECT_SITE = "select_site";
|
||||
private static final boolean DEFAULT_SELECT_SITE = true;
|
||||
private static final int DEFAULT_THEME = THEME_SYSTEM;
|
||||
private static final int DEFAULT_GALLERY_SITE = 1;
|
||||
private static final String KEY_LAUNCH_PAGE = "launch_page";
|
||||
private static final int DEFAULT_LAUNCH_PAGE = 0;
|
||||
private static final int DEFAULT_LIST_MODE = 0;
|
||||
private static final int DEFAULT_DETAIL_SIZE = 0;
|
||||
private static final int DEFAULT_THUMB_SIZE = 120;
|
||||
private static final int DEFAULT_THUMB_RESOLUTION = 0;
|
||||
private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title";
|
||||
private static final boolean DEFAULT_SHOW_JPN_TITLE = false;
|
||||
private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages";
|
||||
private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false;
|
||||
private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false;
|
||||
private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0;
|
||||
private static final String DEFAULT_EXCLUDED_LANGUAGES = null;
|
||||
private static final String KEY_METERED_NETWORK_WARNING = "cellular_network_warning";
|
||||
private static final boolean DEFAULT_METERED_NETWORK_WARNING = false;
|
||||
private static final String KEY_APP_LINK_VERIFY_TIP = "app_link_verify_tip";
|
||||
private static final boolean DEFAULT_APP_LINK_VERIFY_TIP = false;
|
||||
private static final boolean DEFAULT_MEDIA_SCAN = false;
|
||||
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
|
||||
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
|
||||
private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label";
|
||||
private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false;
|
||||
private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label";
|
||||
private static final String DEFAULT_DOWNLOAD_LABEL = null;
|
||||
private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread";
|
||||
private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3;
|
||||
private static final String KEY_PRELOAD_IMAGE = "preload_image";
|
||||
private static final int DEFAULT_PRELOAD_IMAGE = 5;
|
||||
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
|
||||
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
|
||||
/********************
|
||||
****** Favorites
|
||||
********************/
|
||||
private static final String KEY_FAV_CAT_0 = "fav_cat_0";
|
||||
private static final String KEY_FAV_CAT_1 = "fav_cat_1";
|
||||
private static final String KEY_FAV_CAT_2 = "fav_cat_2";
|
||||
private static final String KEY_FAV_CAT_3 = "fav_cat_3";
|
||||
private static final String KEY_FAV_CAT_4 = "fav_cat_4";
|
||||
private static final String KEY_FAV_CAT_5 = "fav_cat_5";
|
||||
private static final String KEY_FAV_CAT_6 = "fav_cat_6";
|
||||
private static final String KEY_FAV_CAT_7 = "fav_cat_7";
|
||||
private static final String KEY_FAV_CAT_8 = "fav_cat_8";
|
||||
private static final String KEY_FAV_CAT_9 = "fav_cat_9";
|
||||
private static final String DEFAULT_FAV_CAT_0 = "Favorites 0";
|
||||
private static final String DEFAULT_FAV_CAT_1 = "Favorites 1";
|
||||
private static final String DEFAULT_FAV_CAT_2 = "Favorites 2";
|
||||
private static final String DEFAULT_FAV_CAT_3 = "Favorites 3";
|
||||
private static final String DEFAULT_FAV_CAT_4 = "Favorites 4";
|
||||
private static final String DEFAULT_FAV_CAT_5 = "Favorites 5";
|
||||
private static final String DEFAULT_FAV_CAT_6 = "Favorites 6";
|
||||
private static final String DEFAULT_FAV_CAT_7 = "Favorites 7";
|
||||
private static final String DEFAULT_FAV_CAT_8 = "Favorites 8";
|
||||
private static final String DEFAULT_FAV_CAT_9 = "Favorites 9";
|
||||
private static final String KEY_FAV_COUNT_0 = "fav_count_0";
|
||||
private static final String KEY_FAV_COUNT_1 = "fav_count_1";
|
||||
private static final String KEY_FAV_COUNT_2 = "fav_count_2";
|
||||
private static final String KEY_FAV_COUNT_3 = "fav_count_3";
|
||||
private static final String KEY_FAV_COUNT_4 = "fav_count_4";
|
||||
private static final String KEY_FAV_COUNT_5 = "fav_count_5";
|
||||
private static final String KEY_FAV_COUNT_6 = "fav_count_6";
|
||||
private static final String KEY_FAV_COUNT_7 = "fav_count_7";
|
||||
private static final String KEY_FAV_COUNT_8 = "fav_count_8";
|
||||
private static final String KEY_FAV_COUNT_9 = "fav_count_9";
|
||||
private static final String KEY_FAV_LOCAL = "fav_local";
|
||||
private static final String KEY_FAV_CLOUD = "fav_cloud";
|
||||
private static final int DEFAULT_FAV_COUNT = 0;
|
||||
private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat";
|
||||
private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL;
|
||||
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
|
||||
private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2";
|
||||
private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT;
|
||||
private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = true;
|
||||
private static final String KEY_SAVE_CRASH_LOG = "save_crash_log";
|
||||
private static final boolean DEFAULT_SAVE_CRASH_LOG = true;
|
||||
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
|
||||
private static final boolean DEFAULT_FRONTING = false;
|
||||
private static final String KEY_PROXY_TYPE = "proxy_type";
|
||||
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
|
||||
private static final String KEY_PROXY_IP = "proxy_ip";
|
||||
private static final String DEFAULT_PROXY_IP = null;
|
||||
private static final String KEY_PROXY_PORT = "proxy_port";
|
||||
private static final int DEFAULT_PROXY_PORT = -1;
|
||||
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
|
||||
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
|
||||
private static final String KEY_DOWNLOAD_DELAY = "download_delay";
|
||||
private static final int DEFAULT_DOWNLOAD_DELAY = 0;
|
||||
private static final String KEY_REQUEST_NEWS = "request_news";
|
||||
private static final boolean DEFAULT_REQUEST_NEWS = true;
|
||||
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
|
||||
private static final String KEY_QS_SAVE_PROGRESS = "qs_save_progress";
|
||||
private static final boolean DEFAULT_QS_SAVE_PROGRESS = true;
|
||||
public static boolean LIST_THUMB_SIZE_INITED = false;
|
||||
public static SharedPreferences sSettingsPre;
|
||||
private static int LIST_THUMB_SIZE = 40;
|
||||
private static EhConfig sEhConfig;
|
||||
|
||||
public static void initialize() {
|
||||
sSettingsPre = PreferenceManager.getDefaultSharedPreferences(EhApplication.getApplication());
|
||||
sEhConfig = loadEhConfig();
|
||||
fixDefaultValue();
|
||||
}
|
||||
|
||||
private static void fixDefaultValue() {
|
||||
if ("CN".equals(Locale.getDefault().getCountry())) {
|
||||
// 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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static EhConfig loadEhConfig() {
|
||||
EhConfig ehConfig = new EhConfig();
|
||||
ehConfig.imageSize = getImageResolution();
|
||||
ehConfig.excludedLanguages = getExcludedLanguages();
|
||||
ehConfig.defaultCategories = getDefaultCategories();
|
||||
ehConfig.excludedNamespaces = getExcludedTagNamespaces();
|
||||
ehConfig.setDirty();
|
||||
return ehConfig;
|
||||
}
|
||||
|
||||
public static boolean getBoolean(String key, boolean defValue) {
|
||||
try {
|
||||
return sSettingsPre.getBoolean(key, defValue);
|
||||
} catch (ClassCastException e) {
|
||||
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void putBoolean(String key, boolean value) {
|
||||
sSettingsPre.edit().putBoolean(key, value).apply();
|
||||
}
|
||||
|
||||
public static int getInt(String key, int defValue) {
|
||||
try {
|
||||
return sSettingsPre.getInt(key, defValue);
|
||||
} catch (ClassCastException e) {
|
||||
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void putInt(String key, int value) {
|
||||
sSettingsPre.edit().putInt(key, value).apply();
|
||||
}
|
||||
|
||||
public static String getString(String key, String defValue) {
|
||||
try {
|
||||
return sSettingsPre.getString(key, defValue);
|
||||
} catch (ClassCastException e) {
|
||||
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void putString(String key, String value) {
|
||||
sSettingsPre.edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public static Set<String> getStringSet(String key) {
|
||||
return sSettingsPre.getStringSet(key, null);
|
||||
}
|
||||
|
||||
public static void putStringToStringSet(String key, String value) {
|
||||
Set<String> set = getStringSet(key);
|
||||
if (set == null)
|
||||
set = Set.of(value);
|
||||
else if (set.contains(value))
|
||||
return;
|
||||
else
|
||||
set.add(value);
|
||||
sSettingsPre.edit().putStringSet(key, set).apply();
|
||||
}
|
||||
|
||||
public static int getIntFromStr(String key, int defValue) {
|
||||
try {
|
||||
return NumberUtils.parseIntSafely(sSettingsPre.getString(key, Integer.toString(defValue)), defValue);
|
||||
} catch (ClassCastException e) {
|
||||
Log.d(TAG, "Get ClassCastException when get " + key + " value", e);
|
||||
return defValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void putIntToStr(String key, int value) {
|
||||
sSettingsPre.edit().putString(key, Integer.toString(value)).apply();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getDisplayName() {
|
||||
return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
public static void putDisplayName(String value) {
|
||||
putString(KEY_DISPLAY_NAME, value);
|
||||
}
|
||||
|
||||
public static void putAvatar(String value) {
|
||||
putString(KEY_AVATAR, value);
|
||||
}
|
||||
|
||||
public static boolean getRemoveImageFiles() {
|
||||
return getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES);
|
||||
}
|
||||
|
||||
public static void putRemoveImageFiles(boolean value) {
|
||||
putBoolean(KEY_REMOVE_IMAGE_FILES, value);
|
||||
}
|
||||
|
||||
public static EhConfig getEhConfig() {
|
||||
return sEhConfig;
|
||||
}
|
||||
|
||||
public static boolean getNeedSignIn() {
|
||||
return getBoolean(KEY_NEED_SIGN_IN, DEFAULT_NEED_SIGN_IN);
|
||||
}
|
||||
|
||||
public static void putNeedSignIn(boolean value) {
|
||||
putBoolean(KEY_NEED_SIGN_IN, value);
|
||||
}
|
||||
|
||||
public static boolean getSelectSite() {
|
||||
return getBoolean(KEY_SELECT_SITE, DEFAULT_SELECT_SITE);
|
||||
}
|
||||
|
||||
public static void putSelectSite(boolean value) {
|
||||
putBoolean(KEY_SELECT_SITE, value);
|
||||
}
|
||||
|
||||
public static int getTheme() {
|
||||
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
|
||||
}
|
||||
|
||||
public static int getGallerySite() {
|
||||
return getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE);
|
||||
}
|
||||
|
||||
public static void putGallerySite(int value) {
|
||||
putIntToStr(KEY_GALLERY_SITE, value);
|
||||
}
|
||||
|
||||
public static String getLaunchPageGalleryListSceneAction() {
|
||||
int value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE);
|
||||
return switch (value) {
|
||||
case 0 -> GalleryListScene.ACTION_HOMEPAGE;
|
||||
case 1 -> GalleryListScene.ACTION_SUBSCRIPTION;
|
||||
case 2 -> GalleryListScene.ACTION_WHATS_HOT;
|
||||
case 3 -> GalleryListScene.ACTION_TOP_LIST;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + value);
|
||||
};
|
||||
}
|
||||
|
||||
public static int getListMode() {
|
||||
return getIntFromStr(KEY_LIST_MODE, DEFAULT_LIST_MODE);
|
||||
}
|
||||
|
||||
public static int getDetailSize() {
|
||||
return getIntFromStr(KEY_DETAIL_SIZE, DEFAULT_DETAIL_SIZE);
|
||||
}
|
||||
|
||||
@DimenRes
|
||||
public static int getDetailSizeResId() {
|
||||
return switch (getDetailSize()) {
|
||||
case 0 -> R.dimen.gallery_list_column_width_long;
|
||||
case 1 -> R.dimen.gallery_list_column_width_short;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + getDetailSize());
|
||||
};
|
||||
}
|
||||
|
||||
public static int getThumbSize() {
|
||||
return dip2px(getInt(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE));
|
||||
}
|
||||
|
||||
public static int dip2px(int dpValue) {
|
||||
final float scale = EhApplication.getApplication().getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static int getThumbResolution() {
|
||||
return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION);
|
||||
}
|
||||
|
||||
public static boolean getShowComments() {
|
||||
return getBoolean(KEY_SHOW_COMMENTS, DEFAULT_SHOW_COMMENTS);
|
||||
}
|
||||
|
||||
public static boolean getRequestNews() {
|
||||
return getBoolean(KEY_REQUEST_NEWS, DEFAULT_REQUEST_NEWS);
|
||||
}
|
||||
|
||||
public static boolean getHideHvEvents() {
|
||||
return getBoolean(KEY_HIDE_HV_EVENTS, DEFAULT_HIDE_HV_EVENTS);
|
||||
}
|
||||
|
||||
public static boolean getShowJpnTitle() {
|
||||
return getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE);
|
||||
}
|
||||
|
||||
public static boolean getShowGalleryPages() {
|
||||
return getBoolean(KEY_SHOW_GALLERY_PAGES, DEFAULT_SHOW_GALLERY_PAGES);
|
||||
}
|
||||
|
||||
public static boolean getShowTagTranslations() {
|
||||
return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS);
|
||||
}
|
||||
|
||||
public static void putShowTagTranslations(boolean value) {
|
||||
putBoolean(KEY_SHOW_TAG_TRANSLATIONS, value);
|
||||
}
|
||||
|
||||
public static int getPreviewNum() {
|
||||
return getInt(KEY_PREVIEW_NUM, DEFAULT_PREVIEW_NUM);
|
||||
}
|
||||
|
||||
public static int getDefaultCategories() {
|
||||
return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES);
|
||||
}
|
||||
|
||||
public static int getExcludedTagNamespaces() {
|
||||
return getInt(KEY_EXCLUDED_TAG_NAMESPACES, DEFAULT_EXCLUDED_TAG_NAMESPACES);
|
||||
}
|
||||
|
||||
public static String getExcludedLanguages() {
|
||||
return getString(KEY_EXCLUDED_LANGUAGES, DEFAULT_EXCLUDED_LANGUAGES);
|
||||
}
|
||||
|
||||
public static boolean getMeteredNetworkWarning() {
|
||||
return getBoolean(KEY_METERED_NETWORK_WARNING, DEFAULT_METERED_NETWORK_WARNING);
|
||||
}
|
||||
|
||||
public static boolean getAppLinkVerifyTip() {
|
||||
return getBoolean(KEY_APP_LINK_VERIFY_TIP, DEFAULT_APP_LINK_VERIFY_TIP);
|
||||
}
|
||||
|
||||
public static void putAppLinkVerifyTip(boolean value) {
|
||||
putBoolean(KEY_APP_LINK_VERIFY_TIP, value);
|
||||
}
|
||||
|
||||
public static boolean getEnabledSecurity() {
|
||||
return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static UniFile getDownloadLocation() {
|
||||
UniFile dir;
|
||||
Uri.Builder builder = new 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(EhApplication.getApplication(), builder.build());
|
||||
return dir != null ? dir : UniFile.fromFile(AppConfig.getDefaultDownloadDir());
|
||||
}
|
||||
|
||||
public static void putDownloadLocation(@NonNull UniFile location) {
|
||||
Uri uri = location.getUri();
|
||||
putString(KEY_DOWNLOAD_SAVE_SCHEME, uri.getScheme());
|
||||
putString(KEY_DOWNLOAD_SAVE_AUTHORITY, uri.getEncodedAuthority());
|
||||
putString(KEY_DOWNLOAD_SAVE_PATH, uri.getEncodedPath());
|
||||
putString(KEY_DOWNLOAD_SAVE_QUERY, uri.getEncodedQuery());
|
||||
putString(KEY_DOWNLOAD_SAVE_FRAGMENT, uri.getEncodedFragment());
|
||||
|
||||
if (getMediaScan()) {
|
||||
CommonOperations.removeNoMediaFile(location);
|
||||
} else {
|
||||
CommonOperations.ensureNoMediaFile(location);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getMediaScan() {
|
||||
return getBoolean(KEY_MEDIA_SCAN, DEFAULT_MEDIA_SCAN);
|
||||
}
|
||||
|
||||
public static String getRecentDownloadLabel() {
|
||||
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
|
||||
}
|
||||
|
||||
public static void putRecentDownloadLabel(String value) {
|
||||
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
|
||||
}
|
||||
|
||||
public static boolean getHasDefaultDownloadLabel() {
|
||||
return getBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, DEFAULT_HAS_DOWNLOAD_LABEL);
|
||||
}
|
||||
|
||||
public static void putHasDefaultDownloadLabel(boolean hasDefaultDownloadLabel) {
|
||||
putBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, hasDefaultDownloadLabel);
|
||||
}
|
||||
|
||||
public static String getDefaultDownloadLabel() {
|
||||
return getString(KEY_DEFAULT_DOWNLOAD_LABEL, DEFAULT_DOWNLOAD_LABEL);
|
||||
}
|
||||
|
||||
public static void putDefaultDownloadLabel(String value) {
|
||||
putString(KEY_DEFAULT_DOWNLOAD_LABEL, value);
|
||||
}
|
||||
|
||||
public static int getMultiThreadDownload() {
|
||||
return getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD);
|
||||
}
|
||||
|
||||
public static int getDownloadDelay() {
|
||||
return getIntFromStr(KEY_DOWNLOAD_DELAY, DEFAULT_DOWNLOAD_DELAY);
|
||||
}
|
||||
|
||||
public static int getPreloadImage() {
|
||||
return getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE);
|
||||
}
|
||||
|
||||
public static String getImageResolution() {
|
||||
return getString(KEY_IMAGE_RESOLUTION, DEFAULT_IMAGE_RESOLUTION);
|
||||
}
|
||||
|
||||
public static void putImageResolution(String value) {
|
||||
sEhConfig.imageSize = value;
|
||||
sEhConfig.setDirty();
|
||||
putString(KEY_IMAGE_RESOLUTION, value);
|
||||
}
|
||||
|
||||
public static boolean getDownloadOriginImage() {
|
||||
return getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE);
|
||||
}
|
||||
|
||||
public static String[] getFavCat() {
|
||||
String[] favCat = new String[10];
|
||||
favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0);
|
||||
favCat[1] = sSettingsPre.getString(KEY_FAV_CAT_1, DEFAULT_FAV_CAT_1);
|
||||
favCat[2] = sSettingsPre.getString(KEY_FAV_CAT_2, DEFAULT_FAV_CAT_2);
|
||||
favCat[3] = sSettingsPre.getString(KEY_FAV_CAT_3, DEFAULT_FAV_CAT_3);
|
||||
favCat[4] = sSettingsPre.getString(KEY_FAV_CAT_4, DEFAULT_FAV_CAT_4);
|
||||
favCat[5] = sSettingsPre.getString(KEY_FAV_CAT_5, DEFAULT_FAV_CAT_5);
|
||||
favCat[6] = sSettingsPre.getString(KEY_FAV_CAT_6, DEFAULT_FAV_CAT_6);
|
||||
favCat[7] = sSettingsPre.getString(KEY_FAV_CAT_7, DEFAULT_FAV_CAT_7);
|
||||
favCat[8] = sSettingsPre.getString(KEY_FAV_CAT_8, DEFAULT_FAV_CAT_8);
|
||||
favCat[9] = sSettingsPre.getString(KEY_FAV_CAT_9, DEFAULT_FAV_CAT_9);
|
||||
return favCat;
|
||||
}
|
||||
|
||||
public static void putFavCat(String[] value) {
|
||||
AssertUtils.assertEquals(10, value.length);
|
||||
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();
|
||||
}
|
||||
|
||||
public static int[] getFavCount() {
|
||||
int[] favCount = new int[10];
|
||||
favCount[0] = sSettingsPre.getInt(KEY_FAV_COUNT_0, DEFAULT_FAV_COUNT);
|
||||
favCount[1] = sSettingsPre.getInt(KEY_FAV_COUNT_1, DEFAULT_FAV_COUNT);
|
||||
favCount[2] = sSettingsPre.getInt(KEY_FAV_COUNT_2, DEFAULT_FAV_COUNT);
|
||||
favCount[3] = sSettingsPre.getInt(KEY_FAV_COUNT_3, DEFAULT_FAV_COUNT);
|
||||
favCount[4] = sSettingsPre.getInt(KEY_FAV_COUNT_4, DEFAULT_FAV_COUNT);
|
||||
favCount[5] = sSettingsPre.getInt(KEY_FAV_COUNT_5, DEFAULT_FAV_COUNT);
|
||||
favCount[6] = sSettingsPre.getInt(KEY_FAV_COUNT_6, DEFAULT_FAV_COUNT);
|
||||
favCount[7] = sSettingsPre.getInt(KEY_FAV_COUNT_7, DEFAULT_FAV_COUNT);
|
||||
favCount[8] = sSettingsPre.getInt(KEY_FAV_COUNT_8, DEFAULT_FAV_COUNT);
|
||||
favCount[9] = sSettingsPre.getInt(KEY_FAV_COUNT_9, DEFAULT_FAV_COUNT);
|
||||
return favCount;
|
||||
}
|
||||
|
||||
public static void putFavCount(int[] count) {
|
||||
AssertUtils.assertEquals(10, count.length);
|
||||
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();
|
||||
}
|
||||
|
||||
public static int getFavLocalCount() {
|
||||
return sSettingsPre.getInt(KEY_FAV_LOCAL, DEFAULT_FAV_COUNT);
|
||||
}
|
||||
|
||||
public static void putFavLocalCount(int count) {
|
||||
sSettingsPre.edit().putInt(KEY_FAV_LOCAL, count).apply();
|
||||
}
|
||||
|
||||
public static int getFavCloudCount() {
|
||||
return sSettingsPre.getInt(KEY_FAV_CLOUD, DEFAULT_FAV_COUNT);
|
||||
}
|
||||
|
||||
public static void putFavCloudCount(int count) {
|
||||
sSettingsPre.edit().putInt(KEY_FAV_CLOUD, count).apply();
|
||||
}
|
||||
|
||||
public static int getRecentFavCat() {
|
||||
return getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT);
|
||||
}
|
||||
|
||||
public static void putRecentFavCat(int value) {
|
||||
putInt(KEY_RECENT_FAV_CAT, value);
|
||||
}
|
||||
|
||||
public static int getDefaultFavSlot() {
|
||||
return getInt(KEY_DEFAULT_FAV_SLOT, DEFAULT_DEFAULT_FAV_SLOT);
|
||||
}
|
||||
|
||||
public static void putDefaultFavSlot(int value) {
|
||||
putInt(KEY_DEFAULT_FAV_SLOT, value);
|
||||
}
|
||||
|
||||
public static boolean getQSSaveProgress() {
|
||||
return getBoolean(KEY_QS_SAVE_PROGRESS, DEFAULT_QS_SAVE_PROGRESS);
|
||||
}
|
||||
|
||||
public static void putQSSaveProgress(boolean value) {
|
||||
putBoolean(KEY_QS_SAVE_PROGRESS, value);
|
||||
}
|
||||
|
||||
public static boolean getSaveParseErrorBody() {
|
||||
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
|
||||
}
|
||||
|
||||
public static boolean getSaveCrashLog() {
|
||||
return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG);
|
||||
}
|
||||
|
||||
public static boolean getSecurity() {
|
||||
return getBoolean(KEY_SECURITY, false);
|
||||
}
|
||||
|
||||
public static void putSecurity(boolean value) {
|
||||
putBoolean(KEY_SECURITY, value);
|
||||
}
|
||||
|
||||
public static int getSecurityDelay() {
|
||||
return getInt(KEY_SECURITY_DELAY, 0);
|
||||
}
|
||||
|
||||
public static int getReadCacheSize() {
|
||||
return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE);
|
||||
}
|
||||
|
||||
public static boolean getBuiltInHosts() {
|
||||
return getBoolean(KEY_BUILT_IN_HOSTS, DEFAULT_BUILT_IN_HOSTS);
|
||||
}
|
||||
|
||||
public static void putBuiltInHosts(boolean value) {
|
||||
putBoolean(KEY_BUILT_IN_HOSTS, value);
|
||||
}
|
||||
|
||||
public static boolean getDF() {
|
||||
return getBoolean(KEY_DOMAIN_FRONTING, DEFAULT_FRONTING);
|
||||
}
|
||||
|
||||
public static void putDF(boolean value) {
|
||||
putBoolean(KEY_DOMAIN_FRONTING, value);
|
||||
}
|
||||
|
||||
public static int getProxyType() {
|
||||
return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE);
|
||||
}
|
||||
|
||||
public static void putProxyType(int value) {
|
||||
putInt(KEY_PROXY_TYPE, value);
|
||||
}
|
||||
|
||||
public static String getProxyIp() {
|
||||
return getString(KEY_PROXY_IP, DEFAULT_PROXY_IP);
|
||||
}
|
||||
|
||||
public static void putProxyIp(String value) {
|
||||
putString(KEY_PROXY_IP, value);
|
||||
}
|
||||
|
||||
public static int getProxyPort() {
|
||||
return getInt(KEY_PROXY_PORT, DEFAULT_PROXY_PORT);
|
||||
}
|
||||
|
||||
public static void putProxyPort(int value) {
|
||||
putInt(KEY_PROXY_PORT, value);
|
||||
}
|
||||
|
||||
public static int getClipboardTextHashCode() {
|
||||
return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE);
|
||||
}
|
||||
|
||||
public static void putClipboardTextHashCode(int value) {
|
||||
putInt(KEY_CLIPBOARD_TEXT_HASH_CODE, value);
|
||||
}
|
||||
|
||||
public static Set<String> getArchivePasswds() {
|
||||
return getStringSet(KEY_ARCHIVE_PASSWDS);
|
||||
}
|
||||
|
||||
public static void putPasswdToArchivePasswds(String value) {
|
||||
putStringToStringSet(KEY_ARCHIVE_PASSWDS, value);
|
||||
}
|
||||
|
||||
public static int getListThumbSize() {
|
||||
if (LIST_THUMB_SIZE_INITED) {
|
||||
return LIST_THUMB_SIZE;
|
||||
}
|
||||
int size = 3 * getInt(KEY_LIST_THUMB_SIZE, DEFAULT_LIST_THUMB_SIZE);
|
||||
LIST_THUMB_SIZE = size;
|
||||
LIST_THUMB_SIZE_INITED = true;
|
||||
return size;
|
||||
}
|
||||
|
||||
public static boolean getNotificationRequired() {
|
||||
return getBoolean(KEY_NOTIFICATION_REQUIRED, false);
|
||||
}
|
||||
|
||||
public static void putNotificationRequired() {
|
||||
putBoolean(KEY_NOTIFICATION_REQUIRED, true);
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
@file:Suppress("SameParameterValue")
|
||||
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
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 : DataStorePreferences(null) {
|
||||
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||
|
||||
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)
|
||||
|
||||
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 intPref("download_delay_2", 0)
|
||||
var gallerySite by intPref("gallery_site_2", 0).observed { updateWhenGallerySiteChanges() }
|
||||
var multiThreadDownload by intPref("download_thread_2", 3)
|
||||
var preloadImage by intPref("preload_image_2", 5)
|
||||
var theme by intPref("theme_2", -1).observed { updateWhenThemeChanges() }
|
||||
var listMode by intPref("list_mode_2", 0)
|
||||
var detailSize by intPref("detail_size_2", 0)
|
||||
var thumbResolution by intPref("thumb_resolution_2", 0)
|
||||
var readCacheSize by intPref("read_cache_size_2", 640)
|
||||
var launchPage by intPref("launch_page_2", 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 harmonizeCategoryColor by boolPref("harmonize_category_color", true)
|
||||
var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false)
|
||||
var downloadOriginImage by boolPref("download_origin_image", false)
|
||||
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)
|
||||
|
||||
init {
|
||||
if ("CN" == Locale.getDefault().country) {
|
||||
edit {
|
||||
if (KEY_SHOW_TAG_TRANSLATIONS !in prefs) showTagTranslations = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +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.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrlOpener;
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.client.parser.GalleryPageUrlParser;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.scene.Announcer;
|
||||
import com.hippo.scene.StageActivity;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
|
||||
|
||||
public final class UrlOpener {
|
||||
|
||||
private UrlOpener() {
|
||||
}
|
||||
|
||||
public static void openUrl(@NonNull Context context, String url, boolean ehUrl) {
|
||||
openUrl(context, url, ehUrl, null);
|
||||
}
|
||||
|
||||
public static void openUrl(@NonNull Context context, String url, boolean ehUrl, GalleryDetail galleryDetail) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent;
|
||||
Uri uri = Uri.parse(url);
|
||||
|
||||
if (ehUrl) {
|
||||
if (galleryDetail != null) {
|
||||
GalleryPageUrlParser.Result result = GalleryPageUrlParser.parse(url);
|
||||
if (result != null) {
|
||||
if (result.gid == galleryDetail.getGid()) {
|
||||
intent = new Intent(context, ReaderActivity.class);
|
||||
intent.setAction(ReaderActivity.ACTION_EH);
|
||||
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
|
||||
intent.putExtra(ReaderActivity.KEY_PAGE, result.page);
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
} else if (url.startsWith("#c")) {
|
||||
try {
|
||||
intent = new Intent(context, ReaderActivity.class);
|
||||
intent.setAction(ReaderActivity.ACTION_EH);
|
||||
intent.putExtra(ReaderActivity.KEY_GALLERY_INFO, galleryDetail);
|
||||
intent.putExtra(ReaderActivity.KEY_PAGE, Integer.parseInt(url.replace("#c", "")) - 1);
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
} catch (NumberFormatException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
Announcer announcer = EhUrlOpener.parseUrl(url);
|
||||
if (null != announcer) {
|
||||
intent = new Intent(context, MainActivity.class);
|
||||
intent.setAction(StageActivity.ACTION_START_SCENE);
|
||||
intent.putExtra(StageActivity.KEY_SCENE_NAME, announcer.getClazz().getName());
|
||||
intent.putExtra(StageActivity.KEY_SCENE_ARGS, announcer.getArgs());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
|
||||
customTabsIntent.setShowTitle(true);
|
||||
try {
|
||||
customTabsIntent.build().launchUrl(context, uri);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(context, R.string.no_browser_installed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +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;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.WindowInsetsAnimationCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class WindowInsetsAnimationHelper extends WindowInsetsAnimationCompat.Callback {
|
||||
private final View[] views;
|
||||
private final HashMap<View, Integer> startPaddings = new HashMap<>();
|
||||
private final HashMap<View, Integer> endPaddings = new HashMap<>();
|
||||
WindowInsetsAnimationCompat animation;
|
||||
|
||||
public WindowInsetsAnimationHelper(int dispatchMode, View... views) {
|
||||
super(dispatchMode);
|
||||
this.views = views;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
|
||||
super.onPrepare(animation);
|
||||
this.animation = animation;
|
||||
for (View view : views) {
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
startPaddings.put(view, view.getPaddingBottom());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WindowInsetsAnimationCompat.BoundsCompat onStart(@NonNull WindowInsetsAnimationCompat animation, @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds) {
|
||||
this.animation = animation;
|
||||
for (View view : views) {
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
endPaddings.put(view, view.getPaddingBottom());
|
||||
int startPadding = startPaddings.containsKey(view) ? startPaddings.get(view) : 0;
|
||||
view.setTranslationY(-(startPadding - view.getPaddingBottom()));
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
|
||||
if (animation == null) {
|
||||
return insets;
|
||||
}
|
||||
WindowInsetsAnimationCompat imeAnimation = null;
|
||||
for (WindowInsetsAnimationCompat animation : runningAnimations) {
|
||||
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
|
||||
imeAnimation = animation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (imeAnimation != null) {
|
||||
for (View view : views) {
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
int startPadding = startPaddings.containsKey(view) ? startPaddings.get(view) : 0;
|
||||
int endPadding = endPaddings.containsKey(view) ? endPaddings.get(view) : 0;
|
||||
view.setTranslationY(MathUtils.lerp(endPadding - startPadding, 0, animation.getInterpolatedFraction()));
|
||||
}
|
||||
}
|
||||
return insets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
|
||||
super.onEnd(animation);
|
||||
startPaddings.clear();
|
||||
endPaddings.clear();
|
||||
this.animation = null;
|
||||
for (View view : views) {
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
view.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +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;
|
||||
|
||||
public class EhCacheKeyFactory {
|
||||
|
||||
public static String getThumbKey(long gid) {
|
||||
return "preview:large:" + gid + ":" + 0; // "thumb:" + gid;
|
||||
}
|
||||
|
||||
public static String getNormalPreviewKey(long gid, int index) {
|
||||
return "preview:normal:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getLargePreviewKey(long gid, int index) {
|
||||
return "preview:large:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getLargePreviewSetKey(long gid, int index) {
|
||||
return "large_preview_set:" + gid + ":" + index;
|
||||
}
|
||||
|
||||
public static String getImageKey(long gid, int index) {
|
||||
return "image:" + gid + ":" + index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
// ExHentai Large Preview: https://s.exhentai.org/t/***
|
||||
// ExHentai Normal Preview: https://s.exhentai.org/m/***
|
||||
// EHentai Normal Preview: https://ehgt.org/m/***
|
||||
// EHentai Large Preview: https://ehgt.org/***
|
||||
|
||||
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_LARGE_THUMB_EX).removePrefix(URL_PREFIX_THUMB_EX)
|
||||
}
|
||||
|
||||
val GalleryPreview.url
|
||||
get() = if (exThumb) {
|
||||
if (this is NormalGalleryPreview) URL_PREFIX_THUMB_EX else URL_PREFIX_LARGE_THUMB_EX
|
||||
} else {
|
||||
URL_PREFIX_THUMB_E
|
||||
} + imageKey
|
||||
|
||||
val GalleryInfo.thumbUrl
|
||||
get() = (if (exThumb) URL_PREFIX_LARGE_THUMB_EX else URL_PREFIX_THUMB_E) + thumbKey!!
|
||||
|
||||
val exThumb get() = EhUtils.isExHentai && !Settings.forceEhThumb
|
||||
@@ -1,196 +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
|
||||
|
||||
import com.hippo.ehviewer.EhApplication.Companion.okHttpClient
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.io.File
|
||||
|
||||
object EhClient {
|
||||
internal fun enqueue(request: EhRequest, scope: CoroutineScope) {
|
||||
check(!request.isActive) // Abort if attempt to execute an active request
|
||||
request.job = scope.launchIO {
|
||||
val method = request.method
|
||||
val params = request.args
|
||||
val callback: Callback<Any?>? = request.callback
|
||||
try {
|
||||
val result: Any? = when (method) {
|
||||
METHOD_SIGN_IN -> EhEngine.signIn(
|
||||
mOkHttpClient,
|
||||
params!![0] as String,
|
||||
params[1] as String
|
||||
)
|
||||
|
||||
METHOD_GET_GALLERY_LIST -> EhEngine.getGalleryList(
|
||||
mOkHttpClient,
|
||||
params!![0] as String
|
||||
)
|
||||
|
||||
METHOD_GET_GALLERY_DETAIL -> EhEngine.getGalleryDetail(
|
||||
mOkHttpClient,
|
||||
params!![0] as String?
|
||||
)
|
||||
|
||||
METHOD_GET_PREVIEW_SET -> EhEngine.getPreviewSet(
|
||||
mOkHttpClient,
|
||||
params!![0] as String?
|
||||
)
|
||||
|
||||
METHOD_GET_RATE_GALLERY -> EhEngine.rateGallery(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as Long,
|
||||
params[3] as String?,
|
||||
params[4] as Float
|
||||
)
|
||||
|
||||
METHOD_GET_COMMENT_GALLERY -> EhEngine.commentGallery(
|
||||
mOkHttpClient,
|
||||
params!![0] as String?,
|
||||
params[1] as String,
|
||||
params[2] as String?
|
||||
)
|
||||
|
||||
METHOD_GET_GALLERY_TOKEN -> EhEngine.getGalleryToken(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as Int
|
||||
)
|
||||
|
||||
METHOD_GET_FAVORITES -> EhEngine.getFavorites(
|
||||
mOkHttpClient,
|
||||
params!![0] as String
|
||||
)
|
||||
|
||||
METHOD_ADD_FAVORITES -> EhEngine.addFavorites(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as Int,
|
||||
params[3] as String?
|
||||
)
|
||||
|
||||
METHOD_ADD_FAVORITES_RANGE -> @Suppress("UNCHECKED_CAST") EhEngine.addFavoritesRange(
|
||||
mOkHttpClient,
|
||||
params!![0] as LongArray,
|
||||
params[1] as Array<String?>,
|
||||
params[2] as Int
|
||||
)
|
||||
|
||||
METHOD_MODIFY_FAVORITES -> EhEngine.modifyFavorites(
|
||||
mOkHttpClient,
|
||||
params!![0] as String,
|
||||
params[1] as LongArray,
|
||||
params[2] as Int
|
||||
)
|
||||
|
||||
METHOD_GET_TORRENT_LIST -> EhEngine.getTorrentList(
|
||||
mOkHttpClient,
|
||||
params!![0] as String?,
|
||||
params[1] as Long,
|
||||
params[2] as String?
|
||||
)
|
||||
|
||||
METHOD_GET_PROFILE -> EhEngine.getProfile(mOkHttpClient)
|
||||
METHOD_VOTE_COMMENT -> EhEngine.voteComment(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as Long,
|
||||
params[3] as String?,
|
||||
params[4] as Long,
|
||||
params[5] as Int
|
||||
)
|
||||
|
||||
METHOD_IMAGE_SEARCH -> EhEngine.imageSearch(
|
||||
mOkHttpClient,
|
||||
params!![0] as File,
|
||||
params[1] as Boolean,
|
||||
params[2] as Boolean,
|
||||
params[3] as Boolean
|
||||
)
|
||||
|
||||
METHOD_ARCHIVE_LIST -> EhEngine.getArchiveList(
|
||||
mOkHttpClient,
|
||||
params!![0] as String?,
|
||||
params[1] as Long,
|
||||
params[2] as String?
|
||||
)
|
||||
|
||||
METHOD_DOWNLOAD_ARCHIVE -> EhEngine.downloadArchive(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as String?,
|
||||
params[3] as String?,
|
||||
params[4] as Boolean
|
||||
)
|
||||
|
||||
METHOD_VOTE_TAG -> EhEngine.voteTag(
|
||||
mOkHttpClient,
|
||||
params!![0] as Long,
|
||||
params[1] as String?,
|
||||
params[2] as Long,
|
||||
params[3] as String?,
|
||||
params[4] as String?,
|
||||
params[5] as Int
|
||||
)
|
||||
|
||||
METHOD_GET_UCONFIG -> EhEngine.getUConfig(mOkHttpClient)
|
||||
else -> throw IllegalStateException("Can't detect method $method")
|
||||
}
|
||||
withUIContext { callback?.onSuccess(result) }
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException)
|
||||
throw e // Don't catch coroutine CancellationException
|
||||
e.printStackTrace()
|
||||
withUIContext { callback?.onFailure(e) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback<E> {
|
||||
fun onSuccess(result: E)
|
||||
fun onFailure(e: Exception)
|
||||
fun onCancel()
|
||||
}
|
||||
|
||||
const val METHOD_SIGN_IN = 0
|
||||
const val METHOD_GET_GALLERY_LIST = 1
|
||||
const val METHOD_GET_GALLERY_DETAIL = 3
|
||||
const val METHOD_GET_PREVIEW_SET = 4
|
||||
const val METHOD_GET_RATE_GALLERY = 5
|
||||
const val METHOD_GET_COMMENT_GALLERY = 6
|
||||
const val METHOD_GET_GALLERY_TOKEN = 7
|
||||
const val METHOD_GET_FAVORITES = 8
|
||||
const val METHOD_ADD_FAVORITES = 9
|
||||
const val METHOD_ADD_FAVORITES_RANGE = 10
|
||||
const val METHOD_MODIFY_FAVORITES = 11
|
||||
const val METHOD_GET_TORRENT_LIST = 12
|
||||
const val METHOD_GET_PROFILE = 14
|
||||
const val METHOD_VOTE_COMMENT = 15
|
||||
const val METHOD_IMAGE_SEARCH = 16
|
||||
const val METHOD_ARCHIVE_LIST = 17
|
||||
const val METHOD_DOWNLOAD_ARCHIVE = 18
|
||||
const val METHOD_VOTE_TAG = 19
|
||||
const val METHOD_GET_UCONFIG = 20
|
||||
private val mOkHttpClient = okHttpClient
|
||||
}
|
||||
@@ -1,806 +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;
|
||||
|
||||
import com.hippo.network.InetValidator;
|
||||
|
||||
/**
|
||||
* Some configurable stuff about EH. It ends up cookie.
|
||||
*/
|
||||
public class EhConfig implements Cloneable {
|
||||
|
||||
/**
|
||||
* The Cookie key of uconfig
|
||||
*/
|
||||
public static final String KEY_UCONFIG = "uconfig";
|
||||
/**
|
||||
* The Cookie key of lofi resolution
|
||||
*/
|
||||
public static final String KEY_LOFI_RESOLUTION = "xres";
|
||||
/**
|
||||
* The Cookie key of show warning
|
||||
*/
|
||||
public static final String KEY_CONTENT_WARNING = "nw";
|
||||
/**
|
||||
* load images through the Hentai@Home Network
|
||||
*/
|
||||
public static final String LOAD_FROM_HAH_YES = "y";
|
||||
/**
|
||||
* do not load images through the Hentai@Home Network
|
||||
*/
|
||||
public static final String LOAD_FROM_HAH_NO = "n";
|
||||
/**
|
||||
* Image Size Auto
|
||||
*/
|
||||
public static final String IMAGE_SIZE_AUTO = "a";
|
||||
/**
|
||||
* Image Size 780x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_780X = "780";
|
||||
/**
|
||||
* Image Size 980x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_980X = "980";
|
||||
/**
|
||||
* Image Size 1280x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_1280X = "1280";
|
||||
/**
|
||||
* Image Size 1600x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_1600X = "1600";
|
||||
/**
|
||||
* Image Size 2400x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_2400X = "2400";
|
||||
/**
|
||||
* Manual Accept, Manual Start
|
||||
*/
|
||||
public static final String ARCHIVER_DOWNLOAD_MAMS = "0";
|
||||
/**
|
||||
* >Manual Accept, Auto Start
|
||||
*/
|
||||
public static final String ARCHIVER_DOWNLOAD_AAMS = "1";
|
||||
/**
|
||||
* Auto Accept, Manual Start
|
||||
*/
|
||||
public static final String ARCHIVER_DOWNLOAD_MAAS = "2";
|
||||
/**
|
||||
* Auto Accept, Auto Start
|
||||
*/
|
||||
public static final String ARCHIVER_DOWNLOAD_AAAS = "3";
|
||||
/**
|
||||
* List View on the front and search pages
|
||||
*/
|
||||
public static final String LAYOUT_MODE_LIST = "l";
|
||||
/**
|
||||
* Thumbnail View on the front and search pages
|
||||
*/
|
||||
public static final String LAYOUT_MODE_THUMB = "t";
|
||||
public static final int MISC = 0x1;
|
||||
public static final int DOUJINSHI = 0x2;
|
||||
public static final int MANGA = 0x4;
|
||||
public static final int ARTIST_CG = 0x8;
|
||||
public static final int GAME_CG = 0x10;
|
||||
public static final int IMAGE_SET = 0x20;
|
||||
public static final int COSPLAY = 0x40;
|
||||
public static final int ASIAN_PORN = 0x80;
|
||||
public static final int NON_H = 0x100;
|
||||
public static final int WESTERN = 0x200;
|
||||
public static final int ALL_CATEGORY = 0x3ff;
|
||||
public static final int NAMESPACES_RECLASS = 0x1;
|
||||
public static final int NAMESPACES_LANGUAGE = 0x2;
|
||||
public static final int NAMESPACES_PARODY = 0x4;
|
||||
public static final int NAMESPACES_CHARACTER = 0x8;
|
||||
public static final int NAMESPACES_GROUP = 0x10;
|
||||
public static final int NAMESPACES_ARTIST = 0x20;
|
||||
public static final int NAMESPACES_MALE = 0x40;
|
||||
public static final int NAMESPACES_FEMALE = 0x80;
|
||||
public static final String JAPANESE_ORIGINAL = "0";
|
||||
public static final String JAPANESE_TRANSLATED = "1024";
|
||||
public static final String JAPANESE_REWRITE = "2048";
|
||||
public static final String ENGLISH_ORIGINAL = "1";
|
||||
public static final String ENGLISH_TRANSLATED = "1025";
|
||||
public static final String ENGLISH_REWRITE = "2049";
|
||||
public static final String CHINESE_ORIGINAL = "10";
|
||||
public static final String CHINESE_TRANSLATED = "1034";
|
||||
public static final String CHINESE_REWRITE = "2058";
|
||||
public static final String DUTCH_ORIGINAL = "20";
|
||||
public static final String DUTCH_TRANSLATED = "1044";
|
||||
public static final String DUTCH_REWRITE = "2068";
|
||||
public static final String FRENCH_ORIGINAL = "30";
|
||||
public static final String FRENCH_TRANSLATED = "1054";
|
||||
public static final String FRENCH_REWRITE = "2078";
|
||||
public static final String GERMAN_ORIGINAL = "40";
|
||||
public static final String GERMAN_TRANSLATED = "1064";
|
||||
public static final String GERMAN_REWRITE = "2088";
|
||||
public static final String HUNGARIAN_ORIGINAL = "50";
|
||||
public static final String HUNGARIAN_TRANSLATED = "1074";
|
||||
public static final String HUNGARIAN_REWRITE = "2098";
|
||||
public static final String ITALIAN_ORIGINAL = "60";
|
||||
public static final String ITALIAN_TRANSLATED = "1084";
|
||||
public static final String ITALIAN_REWRITE = "2108";
|
||||
public static final String KOREAN_ORIGINAL = "70";
|
||||
public static final String KOREAN_TRANSLATED = "1094";
|
||||
public static final String KOREAN_REWRITE = "2118";
|
||||
public static final String POLISH_ORIGINAL = "80";
|
||||
public static final String POLISH_TRANSLATED = "1104";
|
||||
public static final String POLISH_REWRITE = "2128";
|
||||
public static final String PORTUGUESE_ORIGINAL = "90";
|
||||
public static final String PORTUGUESE_TRANSLATED = "1114";
|
||||
public static final String PORTUGUESE_REWRITE = "2138";
|
||||
public static final String RUSSIAN_ORIGINAL = "100";
|
||||
public static final String RUSSIAN_TRANSLATED = "1124";
|
||||
public static final String RUSSIAN_REWRITE = "2148";
|
||||
public static final String SPANISH_ORIGINAL = "110";
|
||||
public static final String SPANISH_TRANSLATED = "1134";
|
||||
public static final String SPANISH_REWRITE = "2158";
|
||||
public static final String THAI_ORIGINAL = "120";
|
||||
public static final String THAI_TRANSLATED = "1144";
|
||||
public static final String THAI_REWRITE = "2168";
|
||||
public static final String VIETNAMESE_ORIGINAL = "130";
|
||||
public static final String VIETNAMESE_TRANSLATED = "1154";
|
||||
public static final String VIETNAMESE_REWRITE = "2178";
|
||||
public static final String NA_ORIGINAL = "254";
|
||||
public static final String NA_TRANSLATED = "1278";
|
||||
public static final String NA_REWRITE = "2302";
|
||||
public static final String OTHER_ORIGINAL = "255";
|
||||
public static final String OTHER_TRANSLATED = "1279";
|
||||
public static final String OTHER_REWRITE = "2303";
|
||||
/**
|
||||
* 25 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
public static final String RESULT_COUNT_25 = "0";
|
||||
/**
|
||||
* 50 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
public static final String RESULT_COUNT_50 = "1";
|
||||
/**
|
||||
* 100 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
public static final String RESULT_COUNT_100 = "2";
|
||||
/**
|
||||
* 200 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
public static final String RESULT_COUNT_200 = "3";
|
||||
/**
|
||||
* On mouse-over
|
||||
*/
|
||||
public static final String MOUSE_OVER_YES = "m";
|
||||
/**
|
||||
* On page load
|
||||
*/
|
||||
public static final String MOUSE_OVER_NO = "p";
|
||||
/**
|
||||
* Preview normal size
|
||||
*/
|
||||
public static final String PREVIEW_SIZE_NORMAL = "m";
|
||||
/**
|
||||
* Preview large size
|
||||
*/
|
||||
public static final String PREVIEW_SIZE_LARGE = "l";
|
||||
/**
|
||||
* 4 row preview per page
|
||||
*/
|
||||
public static final String PREVIEW_ROW_4 = "2";
|
||||
/**
|
||||
* 10 row preview per page
|
||||
*/
|
||||
public static final String PREVIEW_ROW_10 = "5";
|
||||
/**
|
||||
* 20 row preview per page
|
||||
*/
|
||||
public static final String PREVIEW_ROW_20 = "10";
|
||||
/**
|
||||
* 40 row preview per page
|
||||
*/
|
||||
public static final String PREVIEW_ROW_40 = "20";
|
||||
/**
|
||||
* Oldest comments first
|
||||
*/
|
||||
public static final String COMMENTS_SORT_OLDEST_FIRST = "a";
|
||||
/**
|
||||
* Recent comments first
|
||||
*/
|
||||
public static final String COMMENTS_SORT_RECENT_FIRST = "d";
|
||||
/**
|
||||
* By highest score
|
||||
*/
|
||||
public static final String COMMENTS_SORT_HIGHEST_SCORE_FIRST = "s";
|
||||
/**
|
||||
* Show gallery comment votes On score hover or click
|
||||
*/
|
||||
public static final String COMMENTS_VOTES_POP = "0";
|
||||
/**
|
||||
* Always show gallery comment votes
|
||||
*/
|
||||
public static final String COMMENTS_VOTES_ALWAYS = "1";
|
||||
/**
|
||||
* Sort order for gallery tags alphabetically
|
||||
*/
|
||||
public static final String TAGS_SORT_ALPHABETICAL = "a";
|
||||
/**
|
||||
* Sort order for gallery tags by tag power
|
||||
*/
|
||||
public static final String TAGS_SORT_POWER = "p";
|
||||
/**
|
||||
* Show gallery page numbers
|
||||
*/
|
||||
public static final String SHOW_GALLERY_INDEX_YES = "1";
|
||||
/**
|
||||
* Do not show gallery page numbers
|
||||
*/
|
||||
public static final String SHOW_GALLERY_INDEX_NO = "0";
|
||||
/**
|
||||
* Enable Tag Flagging
|
||||
*/
|
||||
public static final String ENABLE_TAG_FLAGGING_YES = "y";
|
||||
/**
|
||||
* Do not enable Tag Flagging
|
||||
*/
|
||||
public static final String ENABLE_TAG_FLAGGING_NO = "n";
|
||||
/**
|
||||
* Always display the original images
|
||||
*/
|
||||
public static final String ALWAYS_ORIGINAL_YES = "y";
|
||||
/**
|
||||
* Do not Always display the original images
|
||||
*/
|
||||
public static final String ALWAYS_ORIGINAL_NO = "n";
|
||||
/**
|
||||
* Enable the Multi-Page Viewe
|
||||
*/
|
||||
public static final String MULTI_PAGE_YES = "y";
|
||||
/**
|
||||
* Do not enable the Multi-Page Viewe
|
||||
*/
|
||||
public static final String MULTI_PAGE_NO = "n";
|
||||
/**
|
||||
* Align left, only scale if image is larger than browser width
|
||||
*/
|
||||
public static final String MULTI_PAGE_STYLE_N = "n";
|
||||
/**
|
||||
* Align center, only scale if image is larger than browser width
|
||||
*/
|
||||
public static final String MULTI_PAGE_STYLE_C = "c";
|
||||
/**
|
||||
* Align center, Always scale images to fit browser width
|
||||
*/
|
||||
public static final String MULTI_PAGE_STYLE_Y = "y";
|
||||
/**
|
||||
* Show Multi-Page Viewer Thumbnail Pane
|
||||
*/
|
||||
public static final String MULTI_PAGE_THUMB_SHOW = "n";
|
||||
/**
|
||||
* Hide Multi-Page Viewer Thumbnail Pane
|
||||
*/
|
||||
public static final String MULTI_PAGE_THUMB_HIDE = "y";
|
||||
/**
|
||||
* 460x for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_460X = "1";
|
||||
/**
|
||||
* 780X for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_780X = "2";
|
||||
/**
|
||||
* 980X for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_980X = "3";
|
||||
/**
|
||||
* show warning
|
||||
*/
|
||||
public static final String CONTENT_WARNING_SHOW = "0";
|
||||
/**
|
||||
* not show warning
|
||||
*/
|
||||
public static final String CONTENT_WARNING_NOT_SHOW = "1";
|
||||
/**
|
||||
* The key of load images through the Hentai@Home Network
|
||||
*
|
||||
* @see #LOAD_FROM_HAH_YES
|
||||
* @see #LOAD_FROM_HAH_NO
|
||||
*/
|
||||
private static final String KEY_LOAD_FROM_HAH = "uh";
|
||||
/**
|
||||
* The key of Image Size Settings
|
||||
*
|
||||
* @see #IMAGE_SIZE_AUTO
|
||||
* @see #IMAGE_SIZE_780X
|
||||
* @see #IMAGE_SIZE_980X
|
||||
* @see #IMAGE_SIZE_1280X
|
||||
* @see #IMAGE_SIZE_1600X
|
||||
* @see #IMAGE_SIZE_2400X
|
||||
*/
|
||||
private static final String KEY_IMAGE_SIZE = "xr";
|
||||
/**
|
||||
* The key of scale images width
|
||||
*/
|
||||
private static final String KEY_SCALE_WIDTH = "rx";
|
||||
/**
|
||||
* The key of scale images height
|
||||
*/
|
||||
private static final String KEY_SCALE_HEIGHT = "ry";
|
||||
/**
|
||||
* The key of Gallery Name Display
|
||||
*
|
||||
* @see #GALLERY_TITLE_DEFAULT
|
||||
* @see #GALLERY_TITLE_JAPANESE
|
||||
*/
|
||||
private static final String KEY_GALLERY_TITLE = "tl";
|
||||
/**
|
||||
* The key of the behavior for downloading archiver
|
||||
*
|
||||
* @see #ARCHIVER_DOWNLOAD_MAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_MAAS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAAS
|
||||
*/
|
||||
private static final String KEY_ARCHIVER_DOWNLOAD = "ar";
|
||||
/**
|
||||
* The key of display mode would you like to use on the front and search pages
|
||||
*
|
||||
* @see #LAYOUT_MODE_LIST
|
||||
* @see #LAYOUT_MODE_THUMB
|
||||
*/
|
||||
private static final String KEY_LAYOUT_MODE = "dm";
|
||||
/**
|
||||
* The key for show popular
|
||||
*
|
||||
* @see #POPULAR_YES
|
||||
* @see #POPULAR_NO
|
||||
*/
|
||||
private static final String KEY_POPULAR = "prn";
|
||||
/**
|
||||
* The key of categories would you like to view as default on the front page
|
||||
*/
|
||||
private static final String KEY_DEFAULT_CATEGORIES = "cats";
|
||||
/**
|
||||
* The key for favorites sort
|
||||
*
|
||||
* @see #FAVORITES_SORT_GALLERY_UPDATE_TIME
|
||||
* @see #FAVORITES_SORT_FAVORITED_TIME
|
||||
*/
|
||||
private static final String KEY_FAVORITES_SORT = "fs";
|
||||
/**
|
||||
* The key of exclude certain namespaces from a default tag search
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_NAMESPACES = "xns";
|
||||
/**
|
||||
* The key of hide galleries in certain languages from the gallery list and searches
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_LANGUAGES = "xl";
|
||||
/**
|
||||
* The key of how many results would you like per page for the index/search page and torrent search pages
|
||||
*
|
||||
* @see #RESULT_COUNT_25
|
||||
* @see #RESULT_COUNT_50
|
||||
* @see #RESULT_COUNT_100
|
||||
* @see #RESULT_COUNT_200
|
||||
*/
|
||||
private static final String KEY_RESULT_COUNT = "rc";
|
||||
/**
|
||||
* The key of mouse-over thumb
|
||||
*
|
||||
* @see #MOUSE_OVER_YES
|
||||
* @see #MOUSE_OVER_NO
|
||||
*/
|
||||
private static final String KEY_MOUSE_OVER = "lt";
|
||||
/**
|
||||
* The key of preview size
|
||||
*
|
||||
* @see #PREVIEW_SIZE_NORMAL
|
||||
* @see #PREVIEW_SIZE_LARGE
|
||||
*/
|
||||
private static final String KEY_PREVIEW_SIZE = "ts";
|
||||
/**
|
||||
* The key of preview row per page
|
||||
*
|
||||
* @see #PREVIEW_ROW_4
|
||||
* @see #PREVIEW_ROW_10
|
||||
* @see #PREVIEW_ROW_20
|
||||
* @see #PREVIEW_ROW_40
|
||||
*/
|
||||
private static final String KEY_PREVIEW_ROW = "tr";
|
||||
/**
|
||||
* The key of sort order for gallery comments
|
||||
*
|
||||
* @see #COMMENTS_SORT_OLDEST_FIRST
|
||||
* @see #COMMENTS_SORT_RECENT_FIRST
|
||||
* @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST
|
||||
*/
|
||||
private static final String KEY_COMMENTS_SORT = "cs";
|
||||
/**
|
||||
* The key of show gallery comment votes
|
||||
*
|
||||
* @see #COMMENTS_VOTES_POP
|
||||
* @see #COMMENTS_VOTES_ALWAYS
|
||||
*/
|
||||
private static final String KEY_COMMENTS_VOTES = "sc";
|
||||
/**
|
||||
* The key of sort order for gallery tags
|
||||
*
|
||||
* @see #TAGS_SORT_ALPHABETICAL
|
||||
* @see #TAGS_SORT_POWER
|
||||
*/
|
||||
private static final String KEY_TAGS_SORT = "to";
|
||||
/**
|
||||
* The key of show gallery page numbers
|
||||
*
|
||||
* @see #SHOW_GALLERY_INDEX_NO
|
||||
* @see #SHOW_GALLERY_INDEX_YES
|
||||
*/
|
||||
private static final String KEY_SHOW_GALLERY_INDEX = "pn";
|
||||
/**
|
||||
* The key of the IP:Port of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_IP_PORT = "hp";
|
||||
/**
|
||||
* The key of the passkey of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_PASSKEY = "hk";
|
||||
/**
|
||||
* The key of enable Tag Flagging
|
||||
*
|
||||
* @see #ENABLE_TAG_FLAGGING_NO
|
||||
* @see #ENABLE_TAG_FLAGGING_YES
|
||||
*/
|
||||
private static final String KEY_ENABLE_TAG_FLAGGING = "tf";
|
||||
/**
|
||||
* The key of always display the original images instead of the resampled versions
|
||||
*
|
||||
* @see #ALWAYS_ORIGINAL_NO
|
||||
* @see #ALWAYS_ORIGINAL_YES
|
||||
*/
|
||||
private static final String KEY_ALWAYS_ORIGINAL = "oi";
|
||||
/**
|
||||
* The key of enable the multi-Page Viewer
|
||||
*
|
||||
* @see #MULTI_PAGE_NO
|
||||
* @see #MULTI_PAGE_YES
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE = "qb";
|
||||
/**
|
||||
* The key of multi-Page Viewer Display Style
|
||||
*
|
||||
* @see #MULTI_PAGE_STYLE_C
|
||||
* @see #MULTI_PAGE_STYLE_N
|
||||
* @see #MULTI_PAGE_STYLE_Y
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_STYLE = "ms";
|
||||
/**
|
||||
* The key of multi-Page Viewer Thumbnail Pane
|
||||
*
|
||||
* @see #MULTI_PAGE_THUMB_HIDE
|
||||
* @see #MULTI_PAGE_THUMB_SHOW
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_THUMB = "mt";
|
||||
/**
|
||||
* Default gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_DEFAULT = "r";
|
||||
/**
|
||||
* Japanese gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_JAPANESE = "j";
|
||||
/**
|
||||
* Show popular
|
||||
*/
|
||||
private static final String POPULAR_YES = "y";
|
||||
/**
|
||||
* Don't show popular
|
||||
*/
|
||||
private static final String POPULAR_NO = "n";
|
||||
/**
|
||||
* Sort favorites by last gallery update time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p";
|
||||
/**
|
||||
* Sort favorites by favorited time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_FAVORITED_TIME = "f";
|
||||
/**
|
||||
* Load images through the Hentai@Home Network<br/>
|
||||
* key: {@link #KEY_LOAD_FROM_HAH}<br/>
|
||||
* value: {@link #LOAD_FROM_HAH_YES}, {@link #LOAD_FROM_HAH_NO}
|
||||
*/
|
||||
public String loadFromHAH = LOAD_FROM_HAH_YES;
|
||||
|
||||
/**
|
||||
* Image Size<br/>
|
||||
* key: {@link #KEY_IMAGE_SIZE}<br/>
|
||||
* value: {@link #IMAGE_SIZE_AUTO}, {@link #IMAGE_SIZE_780X}, {@link #IMAGE_SIZE_980X},
|
||||
* {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X}
|
||||
*/
|
||||
public String imageSize = IMAGE_SIZE_AUTO;
|
||||
|
||||
/**
|
||||
* Scale width<br/>
|
||||
* key: {@link #KEY_SCALE_WIDTH}<br/>
|
||||
* value: 0 for no limit
|
||||
*/
|
||||
public int scaleWidth = 0;
|
||||
|
||||
/**
|
||||
* Scale height<br/>
|
||||
* key: {@link #KEY_SCALE_HEIGHT}<br/>
|
||||
* value: 0 for no limit
|
||||
*/
|
||||
public int scaleHeight = 0;
|
||||
|
||||
/**
|
||||
* Gallery title<br/>
|
||||
* key: {@link #KEY_GALLERY_TITLE}<br/>
|
||||
* value: {@link #GALLERY_TITLE_DEFAULT}, {@link #GALLERY_TITLE_JAPANESE}
|
||||
*/
|
||||
public String galleryTitle = GALLERY_TITLE_DEFAULT;
|
||||
|
||||
/**
|
||||
* The default behavior for downloading an archiver<br/>
|
||||
* key: {@link #KEY_ARCHIVER_DOWNLOAD}<br/>
|
||||
* value: {@link #ARCHIVER_DOWNLOAD_MAMS}, {@link #ARCHIVER_DOWNLOAD_AAMS},
|
||||
* {@link #ARCHIVER_DOWNLOAD_MAAS}, {@link #ARCHIVER_DOWNLOAD_AAAS}
|
||||
*/
|
||||
public String archiverDownload = ARCHIVER_DOWNLOAD_MAMS;
|
||||
|
||||
/**
|
||||
* Display mode used on the front and search pages<br/>
|
||||
* false for list, true for thumb<br/>
|
||||
* key: {@link #KEY_LAYOUT_MODE}<br/>
|
||||
* value: {@link #LAYOUT_MODE_LIST}, {@link #LAYOUT_MODE_THUMB}
|
||||
*/
|
||||
public String layoutMode = LAYOUT_MODE_LIST;
|
||||
|
||||
/**
|
||||
* Show popular or not<br/>
|
||||
* key: {@link #KEY_POPULAR}<br/>
|
||||
* value: {@link #POPULAR_YES}, {@link #POPULAR_NO}
|
||||
*/
|
||||
public String popular = POPULAR_YES;
|
||||
|
||||
/**
|
||||
* Default categories on the front page<br/>
|
||||
* key: {@link #KEY_DEFAULT_CATEGORIES}<br/>
|
||||
* value: the value of categories, for multiple use & operation,
|
||||
* 0 for none
|
||||
*/
|
||||
public int defaultCategories = 0;
|
||||
|
||||
/**
|
||||
* <br/>
|
||||
* key: {@link #KEY_FAVORITES_SORT}<br/>
|
||||
* value: {@link #FAVORITES_SORT_GALLERY_UPDATE_TIME}, {@link #FAVORITES_SORT_FAVORITED_TIME}
|
||||
*/
|
||||
public String favoritesSort = FAVORITES_SORT_FAVORITED_TIME;
|
||||
|
||||
/**
|
||||
* Certain namespaces excluded from a default tag search<br/>
|
||||
* key: {@link #KEY_EXCLUDED_NAMESPACES}<br/>
|
||||
* value: the value of namespaces, for multiple use & operation,
|
||||
* 0 for none
|
||||
*/
|
||||
public int excludedNamespaces = 0;
|
||||
|
||||
/**
|
||||
* Certain languages excluded from list and searches<br/>
|
||||
* key: {@link #KEY_EXCLUDED_LANGUAGES}<br/>
|
||||
* value: {@link #JAPANESE_TRANSLATED}, {@link #JAPANESE_REWRITE} ...
|
||||
* For multiple languages, use <code>x<code/> to combine them, like 1x1024x2048
|
||||
*/
|
||||
public String excludedLanguages = "";
|
||||
|
||||
/**
|
||||
* How many results would you like per page for the index/search page
|
||||
* and torrent search pages<br/>
|
||||
* key: {@link #KEY_RESULT_COUNT}<br/>
|
||||
* value: {@link #RESULT_COUNT_25}, {@link #RESULT_COUNT_50},
|
||||
* {@link #RESULT_COUNT_100}, {@link #RESULT_COUNT_200}<br/>
|
||||
* Require <code>Hath Perk:Paging Enlargement</code>
|
||||
*/
|
||||
public String resultCount = RESULT_COUNT_25;
|
||||
|
||||
/**
|
||||
* mouse-over thumb<br/>
|
||||
* key: {@link #KEY_MOUSE_OVER}<br/>
|
||||
* value: {@link #MOUSE_OVER_YES}, {@link #MOUSE_OVER_NO}
|
||||
*/
|
||||
public String mouseOver = MOUSE_OVER_YES;
|
||||
|
||||
/**
|
||||
* Default preview mode<br/>
|
||||
* key: {@link #KEY_PREVIEW_SIZE}<br/>
|
||||
* value: {@link #PREVIEW_SIZE_NORMAL}, {@link #PREVIEW_SIZE_LARGE}
|
||||
*/
|
||||
public String previewSize = PREVIEW_SIZE_LARGE;
|
||||
|
||||
/**
|
||||
* Preview row<br/>
|
||||
* key: {@link #KEY_PREVIEW_ROW}<br/>
|
||||
* value: {@link #PREVIEW_ROW_4}, {@link #PREVIEW_ROW_10},
|
||||
* {@link #PREVIEW_ROW_20}, {@link #PREVIEW_ROW_40}
|
||||
*/
|
||||
public String previewRow = PREVIEW_ROW_4;
|
||||
|
||||
/**
|
||||
* Sort order for gallery comments<br/>
|
||||
* key: {@link #KEY_COMMENTS_SORT}<br/>
|
||||
* value: {@link #COMMENTS_SORT_OLDEST_FIRST}, {@link #COMMENTS_SORT_RECENT_FIRST},
|
||||
* {@link #COMMENTS_SORT_HIGHEST_SCORE_FIRST}
|
||||
*/
|
||||
public String commentSort = COMMENTS_SORT_OLDEST_FIRST;
|
||||
|
||||
/**
|
||||
* Show gallery comment votes mode<br/>
|
||||
* key: {@link #KEY_COMMENTS_VOTES}<br/>
|
||||
* value: {@link #COMMENTS_VOTES_POP}, {@link #COMMENTS_VOTES_ALWAYS}
|
||||
*/
|
||||
public String commentVotes = COMMENTS_VOTES_POP;
|
||||
|
||||
|
||||
/**
|
||||
* Sort order for gallery tags<br/>
|
||||
* key: {@link #KEY_TAGS_SORT}<br/>
|
||||
* value: {@link #TAGS_SORT_ALPHABETICAL}, {@link #TAGS_SORT_POWER}
|
||||
*/
|
||||
public String tagSort = TAGS_SORT_ALPHABETICAL;
|
||||
|
||||
/**
|
||||
* Show gallery page numbers<br/>
|
||||
* key: {@link #KEY_SHOW_GALLERY_INDEX}<br/>
|
||||
* value: {@link #SHOW_GALLERY_INDEX_YES}, {@link #SHOW_GALLERY_INDEX_NO}
|
||||
*/
|
||||
public String showGalleryIndex = SHOW_GALLERY_INDEX_YES;
|
||||
|
||||
/**
|
||||
* The IP of a proxy-enabled Hentai@Home Client
|
||||
* to load all images<br/>
|
||||
* key: {@link #KEY_HAH_CLIENT_IP_PORT}<br/>
|
||||
*/
|
||||
public String hahClientIp = "";
|
||||
|
||||
/**
|
||||
* The PORT of a proxy-enabled Hentai@Home Client
|
||||
* to load all images<br/>
|
||||
* key: {@link #KEY_HAH_CLIENT_IP_PORT}<br/>
|
||||
*/
|
||||
public int hahClientPort = -1;
|
||||
|
||||
/**
|
||||
* The passkey of a proxy-enabled Hentai@Home Client
|
||||
* to load all images<br/>
|
||||
* key: {@link #KEY_HAH_CLIENT_PASSKEY}<br/>
|
||||
*/
|
||||
public String hahClientPasskey = "";
|
||||
|
||||
/**
|
||||
* Enable tag flagging
|
||||
* key: {@link #KEY_ENABLE_TAG_FLAGGING}<br/>
|
||||
* value: {@link #ENABLE_TAG_FLAGGING_YES}, {@link #ENABLE_TAG_FLAGGING_NO}<br/>
|
||||
* <code>Bronze Star</code> or <code>Hath Perk: Tag Flagging</code> Required
|
||||
*/
|
||||
public String enableTagFlagging = ENABLE_TAG_FLAGGING_NO;
|
||||
|
||||
/**
|
||||
* Always display the original images instead of the resampled versions<br/>
|
||||
* key: {@link #KEY_ALWAYS_ORIGINAL}<br/>
|
||||
* value: {@link #ALWAYS_ORIGINAL_YES}, {@link #ALWAYS_ORIGINAL_NO}<br/>
|
||||
* <code>Silver Star</code> or <code>Hath Perk: Source Nexus</code> Required
|
||||
*/
|
||||
public String alwaysOriginal = ALWAYS_ORIGINAL_NO;
|
||||
|
||||
/**
|
||||
* Enable the multi-Page Viewer<br/>
|
||||
* key: {@link #KEY_MULTI_PAGE}<br/>
|
||||
* value: {@link #MULTI_PAGE_YES}, {@link #MULTI_PAGE_NO}<br/>
|
||||
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
|
||||
*/
|
||||
public String multiPage = MULTI_PAGE_NO;
|
||||
|
||||
/**
|
||||
* Multi-Page Viewer Display Style<br/>
|
||||
* key: {@link #KEY_MULTI_PAGE_STYLE}<br/>
|
||||
* value: {@link #MULTI_PAGE_STYLE_N}, {@link #MULTI_PAGE_STYLE_C},
|
||||
* {@link #MULTI_PAGE_STYLE_Y}<br/>
|
||||
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
|
||||
*/
|
||||
public String multiPageStyle = MULTI_PAGE_STYLE_N;
|
||||
|
||||
/**
|
||||
* Multi-Page Viewer Thumbnail Pane<br/>
|
||||
* key: {@link #KEY_MULTI_PAGE_THUMB}<br/>
|
||||
* value: {@link #MULTI_PAGE_THUMB_SHOW}, {@link #MULTI_PAGE_THUMB_HIDE}<br/>
|
||||
* <code>Gold Star</code> or <code>Hath Perk: Multi-Page Viewer</code> Required
|
||||
*/
|
||||
public String multiPageThumb = MULTI_PAGE_THUMB_SHOW;
|
||||
|
||||
/**
|
||||
* Lofi resolution
|
||||
* key: {@link #KEY_LOFI_RESOLUTION}<br/>
|
||||
* value: {@link #LOFI_RESOLUTION_460X}, {@link #LOFI_RESOLUTION_780X},
|
||||
* {@link #LOFI_RESOLUTION_980X}
|
||||
*/
|
||||
public String lofiResolution = LOFI_RESOLUTION_980X;
|
||||
|
||||
/**
|
||||
* Show content warning
|
||||
* key: {@link #KEY_CONTENT_WARNING}<br/>
|
||||
* value: {@link #CONTENT_WARNING_SHOW}, {@link #CONTENT_WARNING_NOT_SHOW}
|
||||
*/
|
||||
public String contentWarning = CONTENT_WARNING_NOT_SHOW;
|
||||
|
||||
private String mUconfig;
|
||||
|
||||
private boolean mDirty = true;
|
||||
|
||||
@Override
|
||||
public EhConfig clone() {
|
||||
try {
|
||||
return (EhConfig) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setDirty() {
|
||||
mDirty = true;
|
||||
}
|
||||
|
||||
private void updateUconfig() {
|
||||
String hahClientIpPort = (InetValidator.isValidInet4Address(hahClientIp) &&
|
||||
InetValidator.isValidInetPort(hahClientPort)) ?
|
||||
hahClientIp + "%3A" + hahClientPort : "";
|
||||
String hahClientPasskey = null == this.hahClientPasskey ? "" : this.hahClientPasskey;
|
||||
mUconfig = KEY_LOAD_FROM_HAH + "_" + loadFromHAH + "-" +
|
||||
KEY_IMAGE_SIZE + "_" + imageSize + "-" +
|
||||
KEY_SCALE_WIDTH + "_" + scaleWidth + "-" +
|
||||
KEY_SCALE_HEIGHT + "_" + scaleHeight + "-" +
|
||||
KEY_GALLERY_TITLE + "_" + galleryTitle + "-" +
|
||||
KEY_ARCHIVER_DOWNLOAD + "_" + archiverDownload + "-" +
|
||||
KEY_LAYOUT_MODE + "_" + layoutMode + "-" +
|
||||
KEY_POPULAR + "_" + popular + "-" +
|
||||
KEY_DEFAULT_CATEGORIES + "_" + defaultCategories + "-" +
|
||||
KEY_FAVORITES_SORT + "_" + favoritesSort + "-" +
|
||||
KEY_EXCLUDED_NAMESPACES + "_" + excludedNamespaces + "-" +
|
||||
KEY_EXCLUDED_LANGUAGES + "_" + excludedLanguages + "-" +
|
||||
KEY_RESULT_COUNT + "_" + resultCount + "-" +
|
||||
KEY_MOUSE_OVER + "_" + mouseOver + "-" +
|
||||
KEY_PREVIEW_SIZE + "_" + previewSize + "-" +
|
||||
KEY_PREVIEW_ROW + "_" + previewRow + "-" +
|
||||
KEY_COMMENTS_SORT + "_" + commentSort + "-" +
|
||||
KEY_COMMENTS_VOTES + "_" + commentVotes + "-" +
|
||||
KEY_TAGS_SORT + "_" + tagSort + "-" +
|
||||
KEY_SHOW_GALLERY_INDEX + "_" + showGalleryIndex + "-" +
|
||||
KEY_HAH_CLIENT_IP_PORT + "_" + hahClientIpPort + "-" +
|
||||
KEY_HAH_CLIENT_PASSKEY + "_" + hahClientPasskey + "-" +
|
||||
KEY_ENABLE_TAG_FLAGGING + "_" + enableTagFlagging + "-" +
|
||||
KEY_ALWAYS_ORIGINAL + "_" + alwaysOriginal + "-" +
|
||||
KEY_MULTI_PAGE + "_" + multiPage + "-" +
|
||||
KEY_MULTI_PAGE_STYLE + "_" + multiPageStyle + "-" +
|
||||
KEY_MULTI_PAGE_THUMB + "_" + multiPageThumb;
|
||||
}
|
||||
|
||||
public synchronized String uconfig() {
|
||||
if (mDirty) {
|
||||
mDirty = false;
|
||||
updateUconfig();
|
||||
}
|
||||
|
||||
return mUconfig;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +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;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.network.CookieRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class EhCookieStore extends CookieRepository {
|
||||
|
||||
public static final String KEY_IPD_MEMBER_ID = "ipb_member_id";
|
||||
public static final String KEY_IPD_PASS_HASH = "ipb_pass_hash";
|
||||
public static final String KEY_IGNEOUS = "igneous";
|
||||
|
||||
public static final Cookie sTipsCookie =
|
||||
new Cookie.Builder()
|
||||
.name(EhConfig.KEY_CONTENT_WARNING)
|
||||
.value(EhConfig.CONTENT_WARNING_NOT_SHOW)
|
||||
.domain(EhUrl.DOMAIN_E)
|
||||
.path("/")
|
||||
.expiresAt(Long.MAX_VALUE)
|
||||
.build();
|
||||
|
||||
public EhCookieStore(Context context) {
|
||||
super(context, "okhttp3-cookie.db");
|
||||
}
|
||||
|
||||
public static Cookie newCookie(Cookie cookie, String newDomain, boolean forcePersistent,
|
||||
boolean forceLongLive, boolean forceNotHostOnly) {
|
||||
Cookie.Builder builder = new 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();
|
||||
}
|
||||
|
||||
public void signOut() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public boolean hasSignedIn() {
|
||||
HttpUrl url = HttpUrl.parse(EhUrl.HOST_E);
|
||||
return contains(url, KEY_IPD_MEMBER_ID) &&
|
||||
contains(url, KEY_IPD_PASS_HASH);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(@NonNull HttpUrl url) {
|
||||
List<Cookie> cookies = super.loadForRequest(url);
|
||||
|
||||
boolean checkTips = domainMatch(url, EhUrl.DOMAIN_E);
|
||||
|
||||
if (checkTips) {
|
||||
List<Cookie> result = new ArrayList<>(cookies.size() + 1);
|
||||
// Add all but skip some
|
||||
for (Cookie cookie : cookies) {
|
||||
String name = cookie.name();
|
||||
if (EhConfig.KEY_CONTENT_WARNING.equals(name)) {
|
||||
continue;
|
||||
}
|
||||
if (EhConfig.KEY_UCONFIG.equals(name)) {
|
||||
continue;
|
||||
}
|
||||
result.add(cookie);
|
||||
}
|
||||
// Add some
|
||||
result.add(sTipsCookie);
|
||||
return Collections.unmodifiableList(result);
|
||||
} else {
|
||||
return cookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.webkit.CookieManager
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
object EhCookieStore : CookieJar {
|
||||
private val manager = CookieManager.getInstance()
|
||||
fun signOut() = manager.removeAllCookies(null)
|
||||
fun contains(url: HttpUrl, name: String) = get(url).any { it.name == name }
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
const val KEY_IPB_MEMBER_ID = "ipb_member_id"
|
||||
const val KEY_IPB_PASS_HASH = "ipb_pass_hash"
|
||||
const val KEY_IGNEOUS = "igneous"
|
||||
private const val KEY_STAR = "star"
|
||||
private const val KEY_CONTENT_WARNING = "nw"
|
||||
private const val CONTENT_WARNING_NOT_SHOW = "1"
|
||||
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 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) {
|
||||
manager.setCookie(url.toString(), "$name=;Max-Age=0")
|
||||
}
|
||||
|
||||
fun addCookie(cookie: Cookie) {
|
||||
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 = loadForRequest(url)
|
||||
val cookieHeader = StringBuilder()
|
||||
var i = 0
|
||||
val size = cookies.size
|
||||
while (i < size) {
|
||||
if (i > 0) {
|
||||
cookieHeader.append("; ")
|
||||
}
|
||||
val cookie = cookies[i]
|
||||
cookieHeader.append(cookie.name).append('=').append(cookie.value)
|
||||
i++
|
||||
}
|
||||
return cookieHeader.toString()
|
||||
}
|
||||
|
||||
// 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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +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,
|
||||
"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.getDF() && pair.second }
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(UnknownHostException::class)
|
||||
override fun lookup(hostname: String): List<InetAddress> {
|
||||
return hosts[hostname] ?: builtInHosts[hostname].takeIf { Settings.getBuiltInHosts() }
|
||||
?: Dns.SYSTEM.lookup(hostname)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 766 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user