Compare commits
1530
Commits
theming
...
v0.4.16-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d4be416d8 | ||
|
|
11d12fc397 | ||
|
|
caf70b72b5 | ||
|
|
f6d860c439 | ||
|
|
7d99f06053 | ||
|
|
b4e260d7cb | ||
|
|
46281102cb | ||
|
|
b237ec034a | ||
|
|
bfae3b5a7d | ||
|
|
5311aa0fb9 | ||
|
|
2dd61e4cce | ||
|
|
63b97d280b | ||
|
|
529d4afb3b | ||
|
|
d2de67bcc3 | ||
|
|
d02d6ceac1 | ||
|
|
4ac4ae03f1 | ||
|
|
4534ed1156 | ||
|
|
c18bdb3abb | ||
|
|
fb524186d1 | ||
|
|
827f10392f | ||
|
|
105f2c76c0 | ||
|
|
2e6617ec83 | ||
|
|
8806f36182 | ||
|
|
07ca91d8a2 | ||
|
|
4443f69056 | ||
|
|
4cc0b04b68 | ||
|
|
89f2e7cf51 | ||
|
|
8b36fea7a5 | ||
|
|
e5848f2f91 | ||
|
|
5448b2f27e | ||
|
|
253b3bdcb9 | ||
|
|
c3a17ca4ca | ||
|
|
4cf7422997 | ||
|
|
270fca127b | ||
|
|
8b614f786d | ||
|
|
415d3fb3a7 | ||
|
|
593086d3b1 | ||
|
|
f3fda343d2 | ||
|
|
6633eb8f60 | ||
|
|
65d66007e0 | ||
|
|
9763f54bf5 | ||
|
|
6be6b5c714 | ||
|
|
f7c4663329 | ||
|
|
03c19bc9cf | ||
|
|
c27410d359 | ||
|
|
00dda2141b | ||
|
|
68c3a1d137 | ||
|
|
e60ab4a54e | ||
|
|
d9d57428ce | ||
|
|
78d30f9546 | ||
|
|
db1b95b15a | ||
|
|
d27c191800 | ||
|
|
682d9bdef1 | ||
|
|
6f3565a4e3 | ||
|
|
a7e70dabe5 | ||
|
|
d602766fc4 | ||
|
|
5c95ae3dd0 | ||
|
|
0e354123fc | ||
|
|
db51fb01c0 | ||
|
|
cfdf283457 | ||
|
|
724e8b9308 | ||
|
|
46a05c916c | ||
|
|
c814a1b384 | ||
|
|
d1eed0fbd5 | ||
|
|
6faa7dbb74 | ||
|
|
57e8c32629 | ||
|
|
9b6425442a | ||
|
|
025f582f6a | ||
|
|
2dec7b28f4 | ||
|
|
30886adc62 | ||
|
|
761d32dab6 | ||
|
|
7dbebc1cd1 | ||
|
|
dd6c793979 | ||
|
|
c441823d94 | ||
|
|
090c82dd40 | ||
|
|
a7a5a3cd4c | ||
|
|
0e36807bd6 | ||
|
|
24c41e5a6d | ||
|
|
22e12b6422 | ||
|
|
0efdefb207 | ||
|
|
80d2045fbb | ||
|
|
7bb0d69a96 | ||
|
|
96d71394fd | ||
|
|
371f9ae4a6 | ||
|
|
b439165246 | ||
|
|
07eefdcea9 | ||
|
|
9d8533e066 | ||
|
|
37f39f29ab | ||
|
|
84d2d583a3 | ||
|
|
f2c73c6ede | ||
|
|
59e92e30f2 | ||
|
|
be1bc8efbb | ||
|
|
ac0206f5ab | ||
|
|
e576fa0be5 | ||
|
|
1e0e432d57 | ||
|
|
3a1c93d4d0 | ||
|
|
378e6192c7 | ||
|
|
67d0a4c88d | ||
|
|
d59297d348 | ||
|
|
1da12a02b6 | ||
|
|
9ceee0b3d9 | ||
|
|
281b4d9773 | ||
|
|
18714288d7 | ||
|
|
8ac098e4f8 | ||
|
|
eda5ab139e | ||
|
|
ab3221cc01 | ||
|
|
92d4ff2027 | ||
|
|
cedf5d263a | ||
|
|
08909baf2f | ||
|
|
383bafae92 | ||
|
|
c2661e11ec | ||
|
|
9c4ccce409 | ||
|
|
c1b2409baa | ||
|
|
54ee9f6c5b | ||
|
|
d83033e983 | ||
|
|
92c0369332 | ||
|
|
d796455bbe | ||
|
|
dc4743f2a3 | ||
|
|
94c8736bc1 | ||
|
|
e05255263b | ||
|
|
e916b5fcc8 | ||
|
|
09e0dbd01d | ||
|
|
84e5ce6dc2 | ||
|
|
2520b709bd | ||
|
|
0ed16b0836 | ||
|
|
6e55514edf | ||
|
|
93d515b423 | ||
|
|
b88ecd97fc | ||
|
|
5c1b39c79f | ||
|
|
bae7263554 | ||
|
|
63f4ed3885 | ||
|
|
ad90c522ac | ||
|
|
374b221f0d | ||
|
|
99adb95816 | ||
|
|
e96a3b304f | ||
|
|
cf4855bdd1 | ||
|
|
8c8c574a4e | ||
|
|
653afd69b6 | ||
|
|
0660c9664d | ||
|
|
a72ef8570a | ||
|
|
4ce55a22f9 | ||
|
|
910c991adb | ||
|
|
b746594b1d | ||
|
|
0698985623 | ||
|
|
859d9dd2b8 | ||
|
|
341d1b6e70 | ||
|
|
cbdf33f253 | ||
|
|
536dd9ea51 | ||
|
|
624b4d4724 | ||
|
|
0e604c81c7 | ||
|
|
3175fe3af2 | ||
|
|
9f37390d59 | ||
|
|
2dfcc9096f | ||
|
|
00d8e60d1e | ||
|
|
010a71ecca | ||
|
|
e82547ddbb | ||
|
|
484b0f5850 | ||
|
|
fd9837aee4 | ||
|
|
255e5e62db | ||
|
|
4da1ec3b76 | ||
|
|
296b717891 | ||
|
|
e709f6ac68 | ||
|
|
e231e4a2b4 | ||
|
|
2e8f168d9f | ||
|
|
e75eb4caa5 | ||
|
|
58905bbc59 | ||
|
|
ae9d89c6c6 | ||
|
|
41ac03a7e1 | ||
|
|
4bded5c565 | ||
|
|
652f5e665f | ||
|
|
05e118ff31 | ||
|
|
c63457674d | ||
|
|
c9b8dbc8dd | ||
|
|
34c6a7f8b1 | ||
|
|
74d4a5368a | ||
|
|
b2256d20e0 | ||
|
|
f887648f48 | ||
|
|
8ee29b4b83 | ||
|
|
a4c6af8ace | ||
|
|
9ec4e52eba | ||
|
|
b756bfa622 | ||
|
|
f96baa58ac | ||
|
|
d9f03a5d0e | ||
|
|
15d2daa887 | ||
|
|
8e12f50a7f | ||
|
|
9ff220475d | ||
|
|
2c6619a512 | ||
|
|
5ea599b61e | ||
|
|
4eea4e2011 | ||
|
|
f1a7816e69 | ||
|
|
12c01133e1 | ||
|
|
3856932a3f | ||
|
|
a10c333cc6 | ||
|
|
4d950e5c68 | ||
|
|
c72e102076 | ||
|
|
43f3df487b | ||
|
|
ca48f97d83 | ||
|
|
b69beff4d2 | ||
|
|
67401c6868 | ||
|
|
9fa8df30eb | ||
|
|
ecf896e44e | ||
|
|
58a9b69dd9 | ||
|
|
26a64545ff | ||
|
|
05c557a8f7 | ||
|
|
c8c8678785 | ||
|
|
4e6eb74a8e | ||
|
|
52cb007a67 | ||
|
|
657d038d9c | ||
|
|
de4d283f75 | ||
|
|
3b37412a58 | ||
|
|
dd6b9b32b8 | ||
|
|
31a456582b | ||
|
|
6ab317cbf5 | ||
|
|
32c469d870 | ||
|
|
e0f09cea24 | ||
|
|
e668cfb2a3 | ||
|
|
857b63a356 | ||
|
|
0c03406774 | ||
|
|
ac3cb847f4 | ||
|
|
05ed60b86d | ||
|
|
020ec70221 | ||
|
|
478825e708 | ||
|
|
6ce0e630db | ||
|
|
6e538f44b5 | ||
|
|
bd3bec2a31 | ||
|
|
cf25e39485 | ||
|
|
1f87d7a403 | ||
|
|
10da74285c | ||
|
|
9fc5aa4fc3 | ||
|
|
3a2607274f | ||
|
|
6cbe7caec1 | ||
|
|
452b4aa667 | ||
|
|
c4d77e40f4 | ||
|
|
3c9a787a77 | ||
|
|
d400da3ab9 | ||
|
|
ded67ac699 | ||
|
|
2c3d78b0b4 | ||
|
|
ed9701a04b | ||
|
|
9af33a4a01 | ||
|
|
481abbc3d8 | ||
|
|
d9065c3073 | ||
|
|
46295e5216 | ||
|
|
5eeff2f08d | ||
|
|
341a368656 | ||
|
|
f9657af36c | ||
|
|
5bead1b88f | ||
|
|
c4d915e60d | ||
|
|
834f70b585 | ||
|
|
a5243e6839 | ||
|
|
03487678ac | ||
|
|
c482e82ebe | ||
|
|
14ecc21937 | ||
|
|
65aa04a7e3 | ||
|
|
d8e1ce12f7 | ||
|
|
9a84b5b87d | ||
|
|
0a42040188 | ||
|
|
8e66f75baf | ||
|
|
72e1371fe0 | ||
|
|
e9c8170ee4 | ||
|
|
330793f8bc | ||
|
|
45015c571f | ||
|
|
b6fcf0330d | ||
|
|
cc95856e47 | ||
|
|
cbab131610 | ||
|
|
879c218f0c | ||
|
|
ef0e2caabf | ||
|
|
d4ab0a1366 | ||
|
|
9cbc45dea3 | ||
|
|
b6133da808 | ||
|
|
59fb11dbaa | ||
|
|
3de88254f6 | ||
|
|
306338992e | ||
|
|
370ee53bde | ||
|
|
ccc61ebb92 | ||
|
|
522cac34b0 | ||
|
|
282c2e3ed7 | ||
|
|
97453703e1 | ||
|
|
dfeff9903d | ||
|
|
208e601f08 | ||
|
|
e9d63463c5 | ||
|
|
c86a1533e4 | ||
|
|
a1332043a1 | ||
|
|
86e41aef7f | ||
|
|
a1937420e8 | ||
|
|
f5383a58e1 | ||
|
|
3476e4eed4 | ||
|
|
9f01927bd1 | ||
|
|
be2b7c99fc | ||
|
|
d4410a1b85 | ||
|
|
1e16a43b1e | ||
|
|
5b70beceeb | ||
|
|
e8dea04d5d | ||
|
|
1dc000f8f5 | ||
|
|
7d3c4ce4a6 | ||
|
|
92159a9242 | ||
|
|
b49813e2b1 | ||
|
|
c977cd5bdf | ||
|
|
9bd5cafc2d | ||
|
|
b96fbc5a5f | ||
|
|
433801a9d0 | ||
|
|
ed115442dc | ||
|
|
3dd4e4df38 | ||
|
|
8925e85bb4 | ||
|
|
3c90940695 | ||
|
|
d2e9968829 | ||
|
|
c7e5c57248 | ||
|
|
7c820b7207 | ||
|
|
0fbdccbecc | ||
|
|
61d63d574b | ||
|
|
c5a0d7d05c | ||
|
|
d07bf40dc3 | ||
|
|
d365cd9068 | ||
|
|
d8a7b451c0 | ||
|
|
6ea4822f41 | ||
|
|
28ab01cbff | ||
|
|
cc16f4727f | ||
|
|
244bae71e2 | ||
|
|
79c2fc9010 | ||
|
|
66951fa826 | ||
|
|
e9047274b3 | ||
|
|
ac364f1ae2 | ||
|
|
5f57558c52 | ||
|
|
01f956477b | ||
|
|
3c8b394a4b | ||
|
|
c525427594 | ||
|
|
f5037dced3 | ||
|
|
8a83052941 | ||
|
|
274e1bd0df | ||
|
|
0bcbf2b4f8 | ||
|
|
32daac436e | ||
|
|
a7d8e7294f | ||
|
|
820ca12c3e | ||
|
|
38aa6c1905 | ||
|
|
f3cda292e0 | ||
|
|
91bd29cf93 | ||
|
|
75c98b98dd | ||
|
|
186a807d54 | ||
|
|
cc6e3c2dd8 | ||
|
|
48ba199613 | ||
|
|
9a9a73e89a | ||
|
|
df3b374f3a | ||
|
|
a6f52af5d1 | ||
|
|
5c26dd8ea4 | ||
|
|
fa12e8f7e0 | ||
|
|
14cf129435 | ||
|
|
3a7a05e630 | ||
|
|
073c80d1b0 | ||
|
|
e05fe77fb4 | ||
|
|
b0856d6232 | ||
|
|
b86128ac66 | ||
|
|
0cdccd6a54 | ||
|
|
3e9058002b | ||
|
|
829a333d7f | ||
|
|
76775f0ad4 | ||
|
|
42984efe25 | ||
|
|
086210718c | ||
|
|
7656468636 | ||
|
|
bc1f11da61 | ||
|
|
448a169bc3 | ||
|
|
63075ca216 | ||
|
|
27cacc03a2 | ||
|
|
4876a881d2 | ||
|
|
2002db57d9 | ||
|
|
fef7082e66 | ||
|
|
0aad1ef358 | ||
|
|
4dc7223629 | ||
|
|
d8f5b5e910 | ||
|
|
9fbb2fada4 | ||
|
|
3d5c832cec | ||
|
|
a31d75ea41 | ||
|
|
bc467ac335 | ||
|
|
efa28aa3d6 | ||
|
|
aee5f38fb5 | ||
|
|
9dd3a26bab | ||
|
|
a4612a127d | ||
|
|
f49bc4d05d | ||
|
|
7a21f11b64 | ||
|
|
0ca4ba2697 | ||
|
|
4775715b66 | ||
|
|
09333dc64d | ||
|
|
c481d5fca9 | ||
|
|
91bfea9917 | ||
|
|
be65e6816d | ||
|
|
042595f238 | ||
|
|
1f2200c584 | ||
|
|
174151b52b | ||
|
|
52b58dc79b | ||
|
|
7fed263a0e | ||
|
|
de92e4670c | ||
|
|
79fd0350e4 | ||
|
|
4806bb89eb | ||
|
|
4f760b5a4d | ||
|
|
0cdbf9ce99 | ||
|
|
f19a4275de | ||
|
|
a7bde644a4 | ||
|
|
7e36bbf83e | ||
|
|
e461b4654e | ||
|
|
0a7338bf89 | ||
|
|
8d7f2dfee8 | ||
|
|
c1197e461b | ||
|
|
bf5bc08671 | ||
|
|
7baaf08167 | ||
|
|
437bef3d03 | ||
|
|
0d75bec938 | ||
|
|
024079c4bc | ||
|
|
0af00ab7d1 | ||
|
|
ed53f509ee | ||
|
|
2011901666 | ||
|
|
6b90aedae8 | ||
|
|
b75ce047df | ||
|
|
4db73ff3d1 | ||
|
|
bfd258ba54 | ||
|
|
4a092f0920 | ||
|
|
186c1f80e6 | ||
|
|
59c8be9ac2 | ||
|
|
eb2c4c02d2 | ||
|
|
fe2fd354fd | ||
|
|
16fd8925ff | ||
|
|
661302d90f | ||
|
|
2c8dd6e89a | ||
|
|
91fe8a8fb4 | ||
|
|
cc3456758e | ||
|
|
c2dc772905 | ||
|
|
9915e8d2df | ||
|
|
5c79dbe1c2 | ||
|
|
f6555f49d8 | ||
|
|
f83e97404a | ||
|
|
f704d54178 | ||
|
|
3577e3b70c | ||
|
|
000bd5e7d3 | ||
|
|
4d3e8d4354 | ||
|
|
0d51b40676 | ||
|
|
054f03480e | ||
|
|
114b46ccee | ||
|
|
0715c0cfcf | ||
|
|
a51a146780 | ||
|
|
c2c3ea68fa | ||
|
|
8e40da9fb4 | ||
|
|
d6b744533b | ||
|
|
df1d411ae0 | ||
|
|
2904616d67 | ||
|
|
61eac6feca | ||
|
|
1f29f2ddc5 | ||
|
|
fbff3eadde | ||
|
|
7c8a76616c | ||
|
|
465d52df67 | ||
|
|
57e69b0d98 | ||
|
|
a799828de0 | ||
|
|
e6ad306e24 | ||
|
|
5ff1e0a0a6 | ||
|
|
09048a7a21 | ||
|
|
2e2e890875 | ||
|
|
271b1cdfee | ||
|
|
bc06512250 | ||
|
|
a94af3a4db | ||
|
|
924a34b458 | ||
|
|
f73b880b6d | ||
|
|
a53a9eaeaa | ||
|
|
88390f0fc9 | ||
|
|
c6721841ac | ||
|
|
dfa77311f2 | ||
|
|
57e241ab7d | ||
|
|
50b97aac8a | ||
|
|
6166b29b29 | ||
|
|
7ee1331263 | ||
|
|
0cc0299215 | ||
|
|
a7e0f2ae2b | ||
|
|
c38e0aa61e | ||
|
|
9496da151a | ||
|
|
5cec11c6dd | ||
|
|
a285bf4270 | ||
|
|
344c418412 | ||
|
|
5464053fa0 | ||
|
|
8036fc332b | ||
|
|
c1cb5c23ae | ||
|
|
dbb25fbabd | ||
|
|
d75a339a02 | ||
|
|
6fd15a05ff | ||
|
|
0d991138c0 | ||
|
|
5264a397a0 | ||
|
|
c2fe5e6c3c | ||
|
|
7c232b70ed | ||
|
|
8761d23bda | ||
|
|
81203ef531 | ||
|
|
743356af01 | ||
|
|
036cf4846c | ||
|
|
7f97394861 | ||
|
|
d439469f39 | ||
|
|
8ec891d0bc | ||
|
|
bb318aeeb1 | ||
|
|
6621f7c2a2 | ||
|
|
108e212c56 | ||
|
|
f923db90ea | ||
|
|
e5a0e9617b | ||
|
|
e0d27dd97c | ||
|
|
5d49fc586b | ||
|
|
e1beef2e29 | ||
|
|
0803e73949 | ||
|
|
e7b8dd56fe | ||
|
|
add56248e1 | ||
|
|
90a539d361 | ||
|
|
ed2645e3ff | ||
|
|
cdc8d2eaf2 | ||
|
|
bdb9dc670c | ||
|
|
32b3a035a8 | ||
|
|
c44aea13d6 | ||
|
|
ce4bb21782 | ||
|
|
fa5716d651 | ||
|
|
c7971c9860 | ||
|
|
9d3e7c53de | ||
|
|
81c4e1c019 | ||
|
|
a4125653cf | ||
|
|
3c3b51d6ab | ||
|
|
9e21dee290 | ||
|
|
c341e493f8 | ||
|
|
e6325c74d8 | ||
|
|
e703adb168 | ||
|
|
ae2b9e0cce | ||
|
|
6db84cbc12 | ||
|
|
db41faee3e | ||
|
|
905158bb07 | ||
|
|
a08782de4e | ||
|
|
0fcbffab26 | ||
|
|
30d6ccbd9a | ||
|
|
cd7f3f8b52 | ||
|
|
4b57fa9414 | ||
|
|
9d3099f816 | ||
|
|
a0c02b2fd1 | ||
|
|
df8c86fe4c | ||
|
|
008a24bfff | ||
|
|
5484766ef1 | ||
|
|
b36a3bfbdb | ||
|
|
7fc842c9ef | ||
|
|
8615faff4a | ||
|
|
ba05c831cc | ||
|
|
3f11a14d92 | ||
|
|
0284dec33a | ||
|
|
8d376d9e5a | ||
|
|
ce731b1832 | ||
|
|
64a8552647 | ||
|
|
e0b06449ec | ||
|
|
64d62b10a1 | ||
|
|
1a3cf8695d | ||
|
|
1251dd1d2d | ||
|
|
06d5b1a779 | ||
|
|
c18a3dde9e | ||
|
|
6961bc073a | ||
|
|
e1b6f8d66f | ||
|
|
b95edec916 | ||
|
|
8756cd1f20 | ||
|
|
8122c92bbf | ||
|
|
3e53455a05 | ||
|
|
07ddb66264 | ||
|
|
4fef230a06 | ||
|
|
2d73131863 | ||
|
|
f90ae6bade | ||
|
|
b4a45a17c7 | ||
|
|
8767540164 | ||
|
|
fb8e327856 | ||
|
|
344b30aa62 | ||
|
|
d776693aea | ||
|
|
aaf2f9c6c9 | ||
|
|
5fbabfbe23 | ||
|
|
42645a5414 | ||
|
|
5af63e0adf | ||
|
|
ba430052de | ||
|
|
a81ebed663 | ||
|
|
920883ada1 | ||
|
|
41c5f57ccc | ||
|
|
2aa7862fe1 | ||
|
|
22795bab81 | ||
|
|
9ae1c0c1bb | ||
|
|
c155e560ba | ||
|
|
f92dd71d35 | ||
|
|
474f9f0197 | ||
|
|
aee76ba2ff | ||
|
|
398b6fc37b | ||
|
|
454cea3556 | ||
|
|
90816b5c67 | ||
|
|
7c1ac4b267 | ||
|
|
c145b64441 | ||
|
|
2ad5ab7a87 | ||
|
|
95fd69c5da | ||
|
|
1a3f18cbc2 | ||
|
|
f4031ead02 | ||
|
|
37b7783b40 | ||
|
|
8059743e0b | ||
|
|
2c1beedf8b | ||
|
|
484510da77 | ||
|
|
ddf046cd20 | ||
|
|
1c5316ba4e | ||
|
|
5503caed79 | ||
|
|
1f11530d04 | ||
|
|
95b5fca205 | ||
|
|
7432fe7aba | ||
|
|
c4630042c7 | ||
|
|
16f4178e36 | ||
|
|
421d080ec7 | ||
|
|
381a759c4b | ||
|
|
08bb1e1174 | ||
|
|
7fec602f34 | ||
|
|
1f9aca7399 | ||
|
|
6b5c5bedea | ||
|
|
0901af5106 | ||
|
|
5ef7b85e87 | ||
|
|
7facf974e6 | ||
|
|
eb21d3d11c | ||
|
|
03e8d7873e | ||
|
|
11faab902f | ||
|
|
9c1b00ef23 | ||
|
|
f40d3a5aac | ||
|
|
07aa13d9b0 | ||
|
|
0b267091ba | ||
|
|
ead6860674 | ||
|
|
a87c34f1fd | ||
|
|
4e4813fd5c | ||
|
|
58e728f26c | ||
|
|
35282945cc | ||
|
|
18965af7d8 | ||
|
|
a3987abf11 | ||
|
|
58ab5cf4cf | ||
|
|
6f115d1766 | ||
|
|
568a489993 | ||
|
|
2e50607a90 | ||
|
|
560321f4a3 | ||
|
|
afc0173423 | ||
|
|
da5ac90189 | ||
|
|
d8d435c994 | ||
|
|
0e225db32d | ||
|
|
e06e5ac3a7 | ||
|
|
a073314c28 | ||
|
|
c643776790 | ||
|
|
a67f3cd11e | ||
|
|
04087259c3 | ||
|
|
3f6177150d | ||
|
|
3f339821f7 | ||
|
|
9abef592fb | ||
|
|
a561f05b32 | ||
|
|
a8c4f0d505 | ||
|
|
93551d6b2d | ||
|
|
6ff176e5d4 | ||
|
|
cdec126f54 | ||
|
|
2885129531 | ||
|
|
f84a04356a | ||
|
|
621bc6aef7 | ||
|
|
e4dfb30870 | ||
|
|
2b4da660c7 | ||
|
|
a870e09609 | ||
|
|
32f3a11713 | ||
|
|
104ae44b84 | ||
|
|
17f8932ff4 | ||
|
|
0d4d820993 | ||
|
|
878bf49dc4 | ||
|
|
fe8ab52595 | ||
|
|
dd1a3774f8 | ||
|
|
7e1bd28d26 | ||
|
|
59593db4ec | ||
|
|
6694739a4f | ||
|
|
4323802862 | ||
|
|
c2056853e0 | ||
|
|
d15a439477 | ||
|
|
bb59cfa68a | ||
|
|
144b502c25 | ||
|
|
b6c8992441 | ||
|
|
7a5124a820 | ||
|
|
f5cac67d38 | ||
|
|
5b38ea3b3f | ||
|
|
e6a687264b | ||
|
|
6d16fbe260 | ||
|
|
047c6c94be | ||
|
|
41229fbab0 | ||
|
|
8fb885a373 | ||
|
|
dc294ccef0 | ||
|
|
24ef392b1c | ||
|
|
da1efc7261 | ||
|
|
c42245abf3 | ||
|
|
76dc857a43 | ||
|
|
df11c3b517 | ||
|
|
f6500096fa | ||
|
|
792c0afe06 | ||
|
|
11f7faa0ad | ||
|
|
75f03ac207 | ||
|
|
b1df22d207 | ||
|
|
47737a72f8 | ||
|
|
0331bcfa7b | ||
|
|
0928227fac | ||
|
|
8fbc961f47 | ||
|
|
1f667b4133 | ||
|
|
379d6aab8e | ||
|
|
82e164b351 | ||
|
|
f4ca06d27e | ||
|
|
a1fe7bb5db | ||
|
|
f01d5efa92 | ||
|
|
9d0e607c0b | ||
|
|
413c58c936 | ||
|
|
c83b6e4c76 | ||
|
|
c5d1e73dc6 | ||
|
|
35d8b6106d | ||
|
|
6d9f0b9413 | ||
|
|
2d98e6c40b | ||
|
|
10a300b614 | ||
|
|
902d50cef4 | ||
|
|
c518ce4482 | ||
|
|
4d719e4308 | ||
|
|
5bd52c23ae | ||
|
|
2755c93147 | ||
|
|
25c5182877 | ||
|
|
5af8cb9745 | ||
|
|
45ca60570c | ||
|
|
d65334086e | ||
|
|
c2b3e3f88e | ||
|
|
7b3b939cc6 | ||
|
|
0995366ae2 | ||
|
|
16be89be00 | ||
|
|
3bf1a8d398 | ||
|
|
1e876a9217 | ||
|
|
2d21a85259 | ||
|
|
87ef92eff8 | ||
|
|
0e16bbf4f5 | ||
|
|
edb79e3d7d | ||
|
|
1c6ba006b5 | ||
|
|
6350dbc397 | ||
|
|
827ab9f82d | ||
|
|
587e729bdc | ||
|
|
851265e14a | ||
|
|
b553ef1c75 | ||
|
|
5570f8be88 | ||
|
|
775f4f12fe | ||
|
|
1410aaf7ef | ||
|
|
b85aece87b | ||
|
|
4caf2f1e6e | ||
|
|
60eb78c27f | ||
|
|
701edfa823 | ||
|
|
e234f3bce4 | ||
|
|
f80241b5c0 | ||
|
|
9adc74ff46 | ||
|
|
fbc4d054b0 | ||
|
|
3676e1f568 | ||
|
|
88832da5ea | ||
|
|
2175dad72f | ||
|
|
7c45258976 | ||
|
|
01e1ccb4bf | ||
|
|
966312373e | ||
|
|
b1b06f480b | ||
|
|
ea1252c07c | ||
|
|
871695731c | ||
|
|
b7e1326489 | ||
|
|
d02ae1e212 | ||
|
|
6af700e187 | ||
|
|
01f0175467 | ||
|
|
86d85aec3c | ||
|
|
c2001c24f6 | ||
|
|
7726ad974c | ||
|
|
1f859997f4 | ||
|
|
1c20fd4c19 | ||
|
|
bdee61948d | ||
|
|
6c903dcd20 | ||
|
|
dd5cf0eace | ||
|
|
c713d9a59b | ||
|
|
2029447d86 | ||
|
|
81f225be5b | ||
|
|
6258e524d9 | ||
|
|
cf3ecaa325 | ||
|
|
9c9afa9925 | ||
|
|
9ee1357b53 | ||
|
|
cf376364ba | ||
|
|
0fa5a97661 | ||
|
|
a3c7b2a6fb | ||
|
|
a1cc6c5adf | ||
|
|
86051d3b01 | ||
|
|
28e5bbf2de | ||
|
|
9373375a95 | ||
|
|
295f37b9f5 | ||
|
|
06c526ef74 | ||
|
|
d4f737167d | ||
|
|
8c3be43f25 | ||
|
|
880487f841 | ||
|
|
2d73e10bbf | ||
|
|
6a37a8e9e4 | ||
|
|
4868e506f3 | ||
|
|
db9721e96a | ||
|
|
34c560e105 | ||
|
|
c65f20f8bb | ||
|
|
0fefcafba8 | ||
|
|
730e61836f | ||
|
|
f212fdcae8 | ||
|
|
9acffafda0 | ||
|
|
1a62889cb9 | ||
|
|
4ff5ea9fec | ||
|
|
77bc356f4b | ||
|
|
be5633f29c | ||
|
|
1bf6c00e4c | ||
|
|
9cc5eab93d | ||
|
|
39bd57b3f6 | ||
|
|
a50452fb58 | ||
|
|
63229b42c1 | ||
|
|
8e5f3ec3b6 | ||
|
|
61a4ef17a4 | ||
|
|
4498d231ad | ||
|
|
eb659dd453 | ||
|
|
7aa3205c79 | ||
|
|
3a49e3ba01 | ||
|
|
cb07a9c8dd | ||
|
|
bd8a20e64c | ||
|
|
dcb0471d33 | ||
|
|
49c1ca45c8 | ||
|
|
e2ca160507 | ||
|
|
0a3b6f5bc0 | ||
|
|
2391174a0a | ||
|
|
1ddaa8d162 | ||
|
|
54e5930e8d | ||
|
|
dad839ca56 | ||
|
|
8d60831e65 | ||
|
|
837a888b37 | ||
|
|
39dae8f8e8 | ||
|
|
90761b1599 | ||
|
|
75394d2239 | ||
|
|
bf3dab1585 | ||
|
|
f224645c9c | ||
|
|
36a9d621b4 | ||
|
|
f1c2d2c95a | ||
|
|
b71581d48a | ||
|
|
81e85fba7e | ||
|
|
ed6244919d | ||
|
|
782ff2bb66 | ||
|
|
2a02efec0d | ||
|
|
a74c48d8d0 | ||
|
|
5054dbdc7d | ||
|
|
0db27e1690 | ||
|
|
93937be836 | ||
|
|
2649b21ac7 | ||
|
|
d83dc9370d | ||
|
|
c3bae00331 | ||
|
|
13b5ad11a8 | ||
|
|
bf90cbd9ee | ||
|
|
ac25b978e3 | ||
|
|
6549c3e51a | ||
|
|
aa301b007c | ||
|
|
d17663bb6e | ||
|
|
49ae2bdbe3 | ||
|
|
f2bc513869 | ||
|
|
6abc2e4cf9 | ||
|
|
0fba48dd73 | ||
|
|
9fa484d378 | ||
|
|
697189a2b9 | ||
|
|
b39493bd3d | ||
|
|
44e1f1b265 | ||
|
|
e73855a830 | ||
|
|
4e50cf3f8d | ||
|
|
7869a3163f | ||
|
|
036e9939c7 | ||
|
|
b743f64b3e | ||
|
|
0f40f58801 | ||
|
|
f52a20aa1a | ||
|
|
158ca27531 | ||
|
|
e48ed3aac6 | ||
|
|
733ed9daf0 | ||
|
|
f15ea0ee00 | ||
|
|
e95bd7e3c1 | ||
|
|
af667093ef | ||
|
|
5b3c8fd9da | ||
|
|
cc76d48f00 | ||
|
|
994cfa9f21 | ||
|
|
cee15d5ebb | ||
|
|
6fe72ebb9f | ||
|
|
a4cc33d3ce | ||
|
|
7eeb1c3bf2 | ||
|
|
ca49a100fa | ||
|
|
fd41df894f | ||
|
|
82b85e6bfd | ||
|
|
b154e4c5f0 | ||
|
|
03112dd9f7 | ||
|
|
ef51d5d2b9 | ||
|
|
58577e74f7 | ||
|
|
54aac33525 | ||
|
|
b27cef0d1b | ||
|
|
5dfc3b4f02 | ||
|
|
5acf70169c | ||
|
|
0e388f5888 | ||
|
|
2a5c5cb24f | ||
|
|
58ec32be6f | ||
|
|
21fb5fc48b | ||
|
|
d6fcafe8e0 | ||
|
|
95ade723f9 | ||
|
|
de31594250 | ||
|
|
0ff208c2f9 | ||
|
|
92c69e3d97 | ||
|
|
c065eaac6b | ||
|
|
fed9d76bcb | ||
|
|
fdb9819ca9 | ||
|
|
90b773dd52 | ||
|
|
206ffd17c8 | ||
|
|
5363378ea1 | ||
|
|
bee1e760b8 | ||
|
|
996760489f | ||
|
|
65af8e24c1 | ||
|
|
d59ef7ddfe | ||
|
|
9ab654b290 | ||
|
|
7217e75a15 | ||
|
|
ae4847d056 | ||
|
|
88b882c74d | ||
|
|
7b12cb9922 | ||
|
|
06a6d6e575 | ||
|
|
20f043885a | ||
|
|
7ff5d24d10 | ||
|
|
70059b25d8 | ||
|
|
a7f23c7b72 | ||
|
|
c056add894 | ||
|
|
fbca333163 | ||
|
|
52b153ba82 | ||
|
|
6b9bc3cdd9 | ||
|
|
c6647ff807 | ||
|
|
b4c2d075ab | ||
|
|
4835485085 | ||
|
|
937b01e55b | ||
|
|
3630b44d38 | ||
|
|
086b07ace9 | ||
|
|
1efdad97eb | ||
|
|
4bd2b31e1d | ||
|
|
97cd94de4a | ||
|
|
820b9fd969 | ||
|
|
a64f4d1377 | ||
|
|
15e9b5c712 | ||
|
|
c8302d6024 | ||
|
|
cc98e5bdb8 | ||
|
|
9f07840030 | ||
|
|
534bd61c3a | ||
|
|
e68847ca77 | ||
|
|
8a91ccd47e | ||
|
|
e082511d59 | ||
|
|
146d4d42e8 | ||
|
|
d364b2c87a | ||
|
|
257b2e33f2 | ||
|
|
b6afe6f5db | ||
|
|
d6959cd074 | ||
|
|
96f752c6ac | ||
|
|
f8fa4b6c67 | ||
|
|
db7a94a10c | ||
|
|
6aa9eef8ea | ||
|
|
dac5053eb5 | ||
|
|
c0a3a6c54f | ||
|
|
b2a34380b6 | ||
|
|
1237a9f8ac | ||
|
|
0a6d00b4ab | ||
|
|
27c8a1575f | ||
|
|
f7a137537c | ||
|
|
8953ee1f7f | ||
|
|
8da5f35c33 | ||
|
|
0bc2d2b896 | ||
|
|
3167dfab09 | ||
|
|
6c1130aed2 | ||
|
|
bded70f6c6 | ||
|
|
097a6feeb7 | ||
|
|
bd2d49de85 | ||
|
|
4dd43961d4 | ||
|
|
9c968d1f4f | ||
|
|
261592a6b3 | ||
|
|
a5b45c318d | ||
|
|
d50f42a8b0 | ||
|
|
1ec2e1dd4b | ||
|
|
62bd5588e3 | ||
|
|
1c6fd7050d | ||
|
|
94a5ddcc5f | ||
|
|
5b756b0c7a | ||
|
|
d940bb63d6 | ||
|
|
4610626cc7 | ||
|
|
09704a2f0f | ||
|
|
bce4952254 | ||
|
|
4b7fb82710 | ||
|
|
acb06b6953 | ||
|
|
c11bee25d1 | ||
|
|
7543021cff | ||
|
|
019c29bfae | ||
|
|
1c7e010aa2 | ||
|
|
a7c9b7dd58 | ||
|
|
8e0ff61597 | ||
|
|
8d5cc68a87 | ||
|
|
3e765ba22c | ||
|
|
bea91dd654 | ||
|
|
d14f9a7a4d | ||
|
|
8694d39dcb | ||
|
|
7c808f608f | ||
|
|
42856485fc | ||
|
|
76e768dda8 | ||
|
|
59d243bb5b | ||
|
|
3a6cd04a84 | ||
|
|
4b0bc25e0b | ||
|
|
2ef6206599 | ||
|
|
e092da882c | ||
|
|
9c10b06390 | ||
|
|
65208fba3c | ||
|
|
36d13cdd72 | ||
|
|
f9c3484c0e | ||
|
|
f8e22cbfbc | ||
|
|
5da7f8e2f4 | ||
|
|
c7bfc871c1 | ||
|
|
fd26567990 | ||
|
|
d27e304136 | ||
|
|
7f91075a92 | ||
|
|
4436c1d9f1 | ||
|
|
69822b4071 | ||
|
|
91117a5dea | ||
|
|
2a1b799f7e | ||
|
|
119d759105 | ||
|
|
b94462fdf0 | ||
|
|
2820a132d8 | ||
|
|
46625fdecc | ||
|
|
3ec0cfad4d | ||
|
|
6e08ec86ae | ||
|
|
125abe9e36 | ||
|
|
eaf94aa97f | ||
|
|
8a0c0f61f5 | ||
|
|
a49efd7403 | ||
|
|
6196b828c3 | ||
|
|
7a5421706e | ||
|
|
43f62669e9 | ||
|
|
f1d77a639c | ||
|
|
b2e6f55487 | ||
|
|
c6b520fb65 | ||
|
|
e5d5447957 | ||
|
|
3048b098b5 | ||
|
|
b71430f135 | ||
|
|
20bae023d9 | ||
|
|
9fd973c496 | ||
|
|
97ed8000b8 | ||
|
|
c7f18e434f | ||
|
|
6d5b9dd188 | ||
|
|
af59a13dd9 | ||
|
|
771ac02c77 | ||
|
|
925065b317 | ||
|
|
2893050504 | ||
|
|
849a48597c | ||
|
|
ab85d0b03a | ||
|
|
7ce2dd95e2 | ||
|
|
07032c21db | ||
|
|
9cb56466c8 | ||
|
|
c49e985013 | ||
|
|
fbd3f1798a | ||
|
|
66a53f3486 | ||
|
|
45acdecedc | ||
|
|
ec105f2b55 | ||
|
|
858c5b9342 | ||
|
|
b548f6adf3 | ||
|
|
719147997c | ||
|
|
9489ddad5e | ||
|
|
f479c36fcd | ||
|
|
9a925e7100 | ||
|
|
7e7f091ab5 | ||
|
|
8aa6c41037 | ||
|
|
f30994b82e | ||
|
|
873b83cb31 | ||
|
|
4a04f50f98 | ||
|
|
ff31cb0377 | ||
|
|
8c16e3dc42 | ||
|
|
799d06dd10 | ||
|
|
985a2b30c6 | ||
|
|
66a8c909ee | ||
|
|
6439021fc2 | ||
|
|
0a09423e10 | ||
|
|
119ae933e2 | ||
|
|
1fd27c5acf | ||
|
|
e3f338c91f | ||
|
|
39723bcf5b | ||
|
|
d570a77bd5 | ||
|
|
7b1fcfb15a | ||
|
|
17ba841440 | ||
|
|
6ea830cbc7 | ||
|
|
179b1769ae | ||
|
|
3cae6da5d7 | ||
|
|
b1ce420393 | ||
|
|
a8d1bdc8a8 | ||
|
|
f9d0d239b8 | ||
|
|
877153a998 | ||
|
|
35c7c4c08d | ||
|
|
e3340a76db | ||
|
|
94cef1b5f8 | ||
|
|
ecc224776e | ||
|
|
7deb1bba58 | ||
|
|
7ff965c73d | ||
|
|
9cc136bfda | ||
|
|
8ef0aeef13 | ||
|
|
d906c1103b | ||
|
|
7bfdc6255c | ||
|
|
c5162a05aa | ||
|
|
d5353a87dc | ||
|
|
3072531c24 | ||
|
|
d87ff342d4 | ||
|
|
3266b8ccd6 | ||
|
|
03cc03b6bf | ||
|
|
2df8cc5f2a | ||
|
|
8fd7116759 | ||
|
|
bd4310e6e8 | ||
|
|
dfa7ba031e | ||
|
|
6ddea672ad | ||
|
|
047378a11d | ||
|
|
3420c4abb0 | ||
|
|
15a8d70f5f | ||
|
|
91f90a6609 | ||
|
|
e170b70999 | ||
|
|
b489011d24 | ||
|
|
58adfbd261 | ||
|
|
3bb18b6daf | ||
|
|
120bf18416 | ||
|
|
428c4ef7d6 | ||
|
|
40fc05fc15 | ||
|
|
2e913223a2 | ||
|
|
48f12a912c | ||
|
|
0228c0b8a1 | ||
|
|
ba07aeb4d7 | ||
|
|
529b13f18b | ||
|
|
5720fddb59 | ||
|
|
f5c80b230a | ||
|
|
87ee01bac9 | ||
|
|
126ad044f2 | ||
|
|
b636648457 | ||
|
|
bf3672a8c4 | ||
|
|
e6478d5161 | ||
|
|
ddea78aa4b | ||
|
|
b872fb17b8 | ||
|
|
4ec2c17df1 | ||
|
|
38a51f48d0 | ||
|
|
838897611c | ||
|
|
f42150063c | ||
|
|
d164d1219d | ||
|
|
fb71b4d781 | ||
|
|
f338738c1e | ||
|
|
fb704c7388 | ||
|
|
c99bca4200 | ||
|
|
92f43acb80 | ||
|
|
a7c85a24f2 | ||
|
|
fe5616b3f7 | ||
|
|
5c882c5ce2 | ||
|
|
61a4ed43b9 | ||
|
|
b242ad7ae4 | ||
|
|
1b02e806f8 | ||
|
|
eaf23a24ba | ||
|
|
ce64296c01 | ||
|
|
6e3716fe01 | ||
|
|
dcdd83243b | ||
|
|
986ec2987c | ||
|
|
c98609db5d | ||
|
|
0b4e3c6303 | ||
|
|
bc3e5a706d | ||
|
|
0b56d9dd23 | ||
|
|
62ccdf8eba | ||
|
|
72f4226419 | ||
|
|
f6a45beac5 | ||
|
|
a657ddef58 | ||
|
|
726e38ff2e | ||
|
|
191d3f760e | ||
|
|
2cd3b0f39c | ||
|
|
abd990eef7 | ||
|
|
cd875af7d7 | ||
|
|
b1e8f93371 | ||
|
|
64afd0222b | ||
|
|
32369f1f38 | ||
|
|
f00be506cc | ||
|
|
248fd1ce9c | ||
|
|
3e2d4bb665 | ||
|
|
92689941ca | ||
|
|
c46110cfea | ||
|
|
b5250e0b8d | ||
|
|
0e81397305 | ||
|
|
95d306eaed | ||
|
|
339a4fe01f | ||
|
|
48cdbc7681 | ||
|
|
5e85ba10f4 | ||
|
|
ea7dd7aba5 | ||
|
|
c60b766f37 | ||
|
|
da9ded2ae1 | ||
|
|
29eb517bb9 | ||
|
|
84a3f6caff | ||
|
|
b34821463a | ||
|
|
127abbaaeb | ||
|
|
d462d42775 | ||
|
|
5e80ad7e4e | ||
|
|
e1aadade5f | ||
|
|
77d6f5d977 | ||
|
|
c4ae2ec0a3 | ||
|
|
07ee9269b7 | ||
|
|
3ee43f1c50 | ||
|
|
dd65196af9 | ||
|
|
5547de006b | ||
|
|
0fbf571c60 | ||
|
|
f5f05697b6 | ||
|
|
5f179f6a26 | ||
|
|
37c6d29e84 | ||
|
|
0e81ba31cb | ||
|
|
1f24d32109 | ||
|
|
ff16f84c43 | ||
|
|
5b929bf515 | ||
|
|
bdcb3db238 | ||
|
|
f7960b1695 | ||
|
|
825d1b95da | ||
|
|
0e7abaf3f0 | ||
|
|
2731989ae8 | ||
|
|
5ad09e11df | ||
|
|
653d2eeeee | ||
|
|
756ad615ed | ||
|
|
dd6abafad5 | ||
|
|
0434f06bb5 | ||
|
|
50971098ad | ||
|
|
1787f10fca | ||
|
|
6d2684db70 | ||
|
|
f0f1b3a946 | ||
|
|
ae9b703726 | ||
|
|
10512fb74b | ||
|
|
ce9bdba30c | ||
|
|
188c98589c | ||
|
|
e5fa42ff14 | ||
|
|
f42aa95003 | ||
|
|
b16dc816b6 | ||
|
|
b86fe4d65d | ||
|
|
d7e17416f1 | ||
|
|
91355f1dd5 | ||
|
|
ae9e3ed07c | ||
|
|
f701993b0f | ||
|
|
c5bf617f65 | ||
|
|
f09cae1684 | ||
|
|
99d7f06981 | ||
|
|
eaaf7e2bbf | ||
|
|
897382de2b | ||
|
|
55d02a8412 | ||
|
|
7733582238 | ||
|
|
754c8339df | ||
|
|
ebf7baaa65 | ||
|
|
8b26b12813 | ||
|
|
5deb4186f9 | ||
|
|
d3d501f94a | ||
|
|
634f1213d4 | ||
|
|
6fa97ee435 | ||
|
|
05b3342763 | ||
|
|
d434f42d12 | ||
|
|
084f13eda4 | ||
|
|
ed59b54618 | ||
|
|
df282b95ad | ||
|
|
1b38533c83 | ||
|
|
8f4c370ab2 | ||
|
|
9360ea4d85 | ||
|
|
c87d6581c8 | ||
|
|
7133fc6c03 | ||
|
|
46af5af250 | ||
|
|
5b9cc6778c | ||
|
|
5260ad8243 | ||
|
|
2996772020 | ||
|
|
d37534cb6b | ||
|
|
92eb48c21e | ||
|
|
d9e867cb6e | ||
|
|
f200c6e59a | ||
|
|
c668f3bf9a | ||
|
|
bd7afee09b | ||
|
|
ff09a4aec0 | ||
|
|
1e6c60f2ed | ||
|
|
beedb1538d | ||
|
|
8463f9e963 | ||
|
|
ce8f88b8d4 | ||
|
|
ff7d080b5a | ||
|
|
12ad83c607 | ||
|
|
cdf5d2c37c | ||
|
|
9af8e1729e | ||
|
|
d5219ed322 | ||
|
|
7af9a72e77 | ||
|
|
08817bde7e | ||
|
|
d1e9aadd71 | ||
|
|
b6307182cd | ||
|
|
cc70e55b53 | ||
|
|
ed08e97a50 | ||
|
|
44904786d9 | ||
|
|
12b67ce189 | ||
|
|
21b0edf092 | ||
|
|
2adb446d80 | ||
|
|
60738458bb | ||
|
|
a591026258 | ||
|
|
1bab7fa17c | ||
|
|
2713216728 | ||
|
|
f9a529275a | ||
|
|
e992203b7c | ||
|
|
0ed057d705 | ||
|
|
9b70ca6faf | ||
|
|
422c3e4a6c | ||
|
|
6dac880d61 | ||
|
|
c911b2d64e | ||
|
|
39f72f9058 | ||
|
|
2bff354ad0 | ||
|
|
cb55e9b96f | ||
|
|
33f551d464 | ||
|
|
282fa7bdda | ||
|
|
0abd97e8e1 | ||
|
|
5dd616685d | ||
|
|
aff99be92d | ||
|
|
c255f1e498 | ||
|
|
70342daf07 | ||
|
|
f95672e57d | ||
|
|
8f5da42df4 | ||
|
|
4022903069 | ||
|
|
4600c185e4 | ||
|
|
30e8ec0038 | ||
|
|
367b212ca3 | ||
|
|
6533feb62b | ||
|
|
95ef37f6a3 | ||
|
|
6216c66d95 | ||
|
|
02b6e4a728 | ||
|
|
00c1e926e6 | ||
|
|
ef8109e2e8 | ||
|
|
732ee76957 | ||
|
|
b97d2261de | ||
|
|
c362bfc8da | ||
|
|
cc445e4aea | ||
|
|
4ac83d59b5 | ||
|
|
5025a5fa94 | ||
|
|
b8cb19c8db | ||
|
|
593153de8f | ||
|
|
6c36b83d64 | ||
|
|
1381fdd3bb | ||
|
|
f680a37eb1 | ||
|
|
4531b7af52 | ||
|
|
0c4d1af2dc | ||
|
|
d6a18ecca6 | ||
|
|
dc768952c3 | ||
|
|
57cdcfcbd1 | ||
|
|
fb4837ce40 | ||
|
|
b85e1e6937 | ||
|
|
727201ece9 | ||
|
|
c711c3126e | ||
|
|
cdcf218160 | ||
|
|
ee652593c0 | ||
|
|
90773529e6 | ||
|
|
0134a7c82c | ||
|
|
fec3d40d7d | ||
|
|
bdf36f42d3 | ||
|
|
23967490e9 | ||
|
|
3bcd197618 | ||
|
|
ac3eba5654 | ||
|
|
9576856b0b | ||
|
|
f17975c92f | ||
|
|
07a24eab2f | ||
|
|
edc9276fac | ||
|
|
c7531eb652 | ||
|
|
5c1452ff57 | ||
|
|
e467044e41 | ||
|
|
5c5780f720 | ||
|
|
7119bab9c9 | ||
|
|
2eccc92510 | ||
|
|
b9e0fcdb04 | ||
|
|
5c15912b1b | ||
|
|
ac6525e49a | ||
|
|
3e67973a27 | ||
|
|
ed4e295e71 | ||
|
|
73b57399d5 | ||
|
|
9e0b67478d | ||
|
|
341eb37ff4 | ||
|
|
6d68a77e42 | ||
|
|
b50f56a5d4 | ||
|
|
e226438b09 | ||
|
|
a5db0793d6 | ||
|
|
07e1f1a1d3 | ||
|
|
de77e3484b | ||
|
|
24571ea22f | ||
|
|
515f90105c | ||
|
|
0d305dbc7e | ||
|
|
05bf888ba6 | ||
|
|
a2a53e8215 | ||
|
|
e6290b8aba | ||
|
|
aa5f6e7cbe | ||
|
|
054b447236 | ||
|
|
52db475469 | ||
|
|
6282800fdb | ||
|
|
20517fdf51 | ||
|
|
40400817c1 | ||
|
|
834e454eb2 | ||
|
|
2747c9efc8 | ||
|
|
c2cc782ee3 | ||
|
|
d414e79b6b | ||
|
|
9e0f95ed94 | ||
|
|
167da56a0b | ||
|
|
af213869c8 | ||
|
|
39b9e42f62 | ||
|
|
1aa07afabc | ||
|
|
84d7d0bef6 | ||
|
|
fbb7f2320e | ||
|
|
506f5c7597 | ||
|
|
38d3a40d7a | ||
|
|
44cb073de8 | ||
|
|
8a680e5429 | ||
|
|
16dbdc5ec3 | ||
|
|
4cece4566c | ||
|
|
2340cbbbb2 | ||
|
|
e00feded18 | ||
|
|
d7c5e88c00 | ||
|
|
7462d739f4 | ||
|
|
f1bbdeac45 | ||
|
|
178bdbf988 | ||
|
|
82a5d40a10 | ||
|
|
c888bd3f3c | ||
|
|
53f45f49c3 | ||
|
|
3c108c7520 | ||
|
|
11bce261ae | ||
|
|
5f8204c48e | ||
|
|
f6043ed413 | ||
|
|
5ab22cd5c4 | ||
|
|
ee20e461d2 | ||
|
|
9fa6513d4b | ||
|
|
e86c7b190a | ||
|
|
0b2c5dc578 | ||
|
|
55b569c057 | ||
|
|
02d6fb3b14 | ||
|
|
8ed21f8fa9 | ||
|
|
6c1a12b345 | ||
|
|
05f4462195 | ||
|
|
e4bb903391 | ||
|
|
5028fe9d7f | ||
|
|
9bca7a3598 | ||
|
|
dddb414c7d | ||
|
|
ad67bed400 | ||
|
|
5ffa2fac40 | ||
|
|
cca1bc6607 | ||
|
|
55f49d7d3a | ||
|
|
a24f8bef3d | ||
|
|
c41a0de1af | ||
|
|
d4c8b795fa | ||
|
|
47c219ad8c | ||
|
|
59b34ddf3e | ||
|
|
01f1e3993c | ||
|
|
c21cadad1d | ||
|
|
307d15ff24 | ||
|
|
5edebee51e | ||
|
|
3d936f4abd | ||
|
|
e725c05e9c | ||
|
|
5a8a33cebc | ||
|
|
da28a5feca | ||
|
|
a058e84ddb | ||
|
|
49ccf080ad | ||
|
|
176a9a89b6 | ||
|
|
40f907300d | ||
|
|
0219b14844 | ||
|
|
08acebd2c6 | ||
|
|
9bf0109c0d | ||
|
|
1f31ddd8d0 | ||
|
|
9540fb68dc | ||
|
|
432975f409 | ||
|
|
8b2acc764a | ||
|
|
f79ca38ef4 | ||
|
|
446cc973d6 | ||
|
|
a847d6a3eb | ||
|
|
67f9d9bbd9 | ||
|
|
2d49885572 | ||
|
|
a940674c6f | ||
|
|
7bbb073552 | ||
|
|
105d784b4a | ||
|
|
23eb0fef69 | ||
|
|
b9afb74a01 | ||
|
|
edfb68c946 | ||
|
|
66897e8e93 | ||
|
|
3b754b79e7 | ||
|
|
11fa834af7 | ||
|
|
d319c97430 | ||
|
|
744f9866da | ||
|
|
251746a57e | ||
|
|
b1bbfe7e1b | ||
|
|
7a911de8ca | ||
|
|
e5398cc1be | ||
|
|
b87f28f8bf | ||
|
|
5f8e16eb5d | ||
|
|
f83e1cc0b7 | ||
|
|
447bc94100 | ||
|
|
17cdb9c87b | ||
|
|
2cb35c17b2 | ||
|
|
0e0ca2a504 | ||
|
|
dca61fcd16 | ||
|
|
d0a198c6ad | ||
|
|
c1211123dd | ||
|
|
3a9d4ffa12 | ||
|
|
5855dc2abf | ||
|
|
ed3e6314d1 | ||
|
|
b83a36d41a | ||
|
|
1beaf3f436 | ||
|
|
0bb9145288 | ||
|
|
ba76a1f0fd | ||
|
|
f0732ae3c3 | ||
|
|
8a761382f0 | ||
|
|
481e3ba041 | ||
|
|
4615a174f0 | ||
|
|
18de5e9289 | ||
|
|
122c6d8b80 | ||
|
|
d00f93ee94 | ||
|
|
bbc2a8132a | ||
|
|
f4e61583fa | ||
|
|
9ee72b2c6b | ||
|
|
dd2c17e4ac | ||
|
|
e87dde9c6a | ||
|
|
83a5723dee | ||
|
|
3e6b968400 | ||
|
|
c3f9dffd50 | ||
|
|
972504202e | ||
|
|
d26b0bed38 | ||
|
|
e5b886ed21 | ||
|
|
d9e6082f1a | ||
|
|
67001e272b | ||
|
|
5534aa3fda | ||
|
|
55e73d3469 | ||
|
|
8a7b03676d | ||
|
|
0adafcb433 | ||
|
|
356eb98b94 | ||
|
|
86b5af9210 | ||
|
|
c4a5406330 | ||
|
|
857e6ff86b | ||
|
|
5bec30d2c7 | ||
|
|
c7a226999f | ||
|
|
1bea6d3f3e | ||
|
|
ffa70d5684 | ||
|
|
8de9ea9c1b | ||
|
|
f8c4393074 | ||
|
|
79cada7128 | ||
|
|
4c40edd00c | ||
|
|
20ab276cef | ||
|
|
689f2d3876 | ||
|
|
c403deb38d | ||
|
|
d90ecbb8d1 | ||
|
|
ae16a513ed | ||
|
|
7339a28d92 | ||
|
|
d544a633cc | ||
|
|
a6b78dca57 | ||
|
|
7dd78345f1 | ||
|
|
0a67c2a910 | ||
|
|
4e9475a6eb | ||
|
|
b5a6a8d027 | ||
|
|
1e1869718f | ||
|
|
ee69e29153 | ||
|
|
137ba631af | ||
|
|
aa972fa5b3 |
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Заготовка для репорта багов
|
||||
title: Ошибка обновлении ленты новостей
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Опишите сам баг**
|
||||
При обновлении новостей вылезает ошибка
|
||||
|
||||
**Опишите действия для воспроизведения ошибки**
|
||||
1. Обновил ленту интересных новостей
|
||||
2. Пролистал до конца страницы
|
||||
|
||||
**Опишите ожидаемый результат**
|
||||
Интересная лента должна прогружать результаты
|
||||
|
||||
**Скриншот ошибки**
|
||||
|
||||
**Информация об устройстве:**
|
||||
- Устройство: Samsung Galaxy J1 Mini
|
||||
- OS: Android 9 | OneUi 3.0
|
||||
- Коммит: fa12e8f (В О Приложении написана версия коммита)
|
||||
|
||||
**Дополнительная информация о баге или мыслях почему оно может происходить (если есть)**
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Предложение функции
|
||||
title: Улучшение голосовых сообщений
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Опишите вашу идею максимально подробно и понятно**
|
||||
Необходима функция улучшения голосовых сообщений. Она значительно улучшит запись голосовых сообщений и собеседнику будет лучше слышно меня
|
||||
|
||||
**Опишите возможное решение/реализацию функции (если понимаете как это можно реализовать на Java/C++)**
|
||||
|
||||
**Остальные мысли о предложенной функции**
|
||||
@@ -29,3 +29,4 @@ smali2/build/resources.zip
|
||||
smali/build/resources.zip
|
||||
smali/assets/version.properties
|
||||
*.keystore
|
||||
gradle.properties
|
||||
|
||||
@@ -55,10 +55,11 @@
|
||||
* Лента
|
||||
* Блокировка рекламы в ленте
|
||||
* Блокировка постов через фильтры
|
||||
* Возможность добавлять в вайтлист фильтров группы, чтобы в них не удалялись посты
|
||||
* Возможность отключить рекомендации от ВК
|
||||
* Редактор докбара
|
||||
* Редактор элементов Superapp
|
||||
* Медиа контент
|
||||
* Медиаконтент
|
||||
* Округление дат
|
||||
* 1 января в 4:20
|
||||
* 1 января в 4:20:00
|
||||
@@ -66,9 +67,9 @@
|
||||
* Округление чисел
|
||||
* Сокращение постов **(Показать полностью)**
|
||||
* Результаты опросов без участия
|
||||
* Возможность указывать папку для загрузки фото/видео/музыки/всего
|
||||
* Интеграция с <a href="https://www.last.fm/">LAST.FM</a> (а также возможность скробблинга)
|
||||
* Новый раздел музыки
|
||||
* Возможность скачивать видео
|
||||
* Возможность скачивать видео, очищать историю просмотра видео
|
||||
* Возможность найти фотографию через поисковые системы
|
||||
* Интеграция с <a href="https://vkx.app/">VK X</a>
|
||||
* Сообщения
|
||||
@@ -76,20 +77,23 @@
|
||||
* Возможность выключения кнопки записи голосовых сообщений
|
||||
* Возможность скрыть блок рекомендаций в сообщениях
|
||||
* Режим **VK Me**
|
||||
* Возможность вернуть вкладку уведомлений
|
||||
* Возможность вернуть вкладку уведомлений для режима VK Me
|
||||
* Встроенный переводчик текста для сообщений
|
||||
* Возможность выбрать язык перевода
|
||||
* Автоматический перевод ваших сообщений на выбранный язык
|
||||
* Автоматический перевод ваших сообщений на выбранный язык
|
||||
* Альтернативный дизайн эмодзи меню
|
||||
* Возможность использовать системные эмодзи
|
||||
* Возможнсть использовать Enter с аппаратной клавиатуры
|
||||
* Активность
|
||||
* Возможность видеть удаленные сообщения
|
||||
* Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
|
||||
* Нечиталка/неписалка для ЛС/бесед/ботов/групп
|
||||
* Возможность отключить информацию о том, что вы просмотрели историю/прослушали голосовое сообщение
|
||||
* Возможность видеть онлайн пользователей, если у вас скрыт онлайн через VK Messenger
|
||||
* Возможность сохранять настройки бесшумных сообщений/сообщений-бомбочек
|
||||
* Отключить уведомление о том, что вы сделали скриншот в беседе
|
||||
* Отключение редиректа через away.php
|
||||
* Отключение отправки метрики
|
||||
* Внешний вид
|
||||
* **Milkshake** тема (дизайн новых версий ВК)
|
||||
* Название заголовков в докбаре
|
||||
@@ -103,14 +107,17 @@
|
||||
* Возможность отключить блок историй наверху ленты
|
||||
* Возможность отключить боковую камеру в разделе «Новости»
|
||||
* Возможность отключить блок «Что у вас нового?» в разделе «Новости»
|
||||
* Возможность менять раздел, который запускается при запуске
|
||||
* Возможность показывать лайки у постов справа
|
||||
* Возможность менять стартовый раздел при запуске приложения
|
||||
* Анимации фрагментов
|
||||
* Возможность изменять размер закругления картинок (аватарок и прочего)
|
||||
* Возможность отключить редизайн видеопостов
|
||||
* Возможность отключить блок миниаппов/VK Pay/Superapp
|
||||
* Возможность отключить сокращение названий разделов в меню
|
||||
* Возможность скрыть блок рекомендаций друзей в разделе «Друзья»
|
||||
* Возможность скрыть подписи в настройках
|
||||
* Прокси
|
||||
* Поддержка сторонних прокси, таких как: Zaborona, VikaMobile, рандомные прокси и прочие
|
||||
* Возможность использовать **HTTP/HTTPS/SOCKS** прокси, менять домены **API/OAuth/Static**
|
||||
* Другое
|
||||
* Очистка кеша приложения (**всё/стикеры/изображения/видео/сообщения/WebView**)
|
||||
|
||||
+5
-5
@@ -69,19 +69,19 @@ task exportDex {
|
||||
|
||||
android {
|
||||
compileSdkVersion 32
|
||||
buildToolsVersion '32.0.0'
|
||||
buildToolsVersion '33.0.0'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "ru.vtosters.lite.utils.lite"
|
||||
minSdkVersion 21
|
||||
minSdk 22
|
||||
targetSdkVersion 32
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
@@ -89,9 +89,9 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
// IVKExternalMusic.aidl
|
||||
package bruhcollective.itaysonlab.libvkx;
|
||||
|
||||
interface ILibVkxService {
|
||||
// Starts playback. playbackContext can be "other" if you don't sure what it is
|
||||
void play(in List<String> audio_ids, in int index, in String playbackContext) = 1;
|
||||
|
||||
// A UserID. CHECK FOR THIS TO BE EQUAL BEFORE SENDING ANYTHING TO SERVICE!
|
||||
int getUserId() = 2;
|
||||
|
||||
// Cache add/remove
|
||||
void addTrackToCache(in int track_id, in int owner_id, in String access_key) = 3;
|
||||
void deleteTrackFromCache(in int track_id, in int owner_id) = 4;
|
||||
boolean isTrackCached(in int track_id, in int owner_id) = 5;
|
||||
|
||||
// Get a list of cached tracks. The response is close to VK's original response (an audio list)
|
||||
// Also, the items in the list are JSON ones.
|
||||
List<String> getCache() = 6;
|
||||
|
||||
// [ver 1.1]
|
||||
void downloadTrack(in int track_id, in int owner_id, in String access_key) = 7;
|
||||
void downloadPlaylist(in int playlist_id, in int owner_id, in String access_key) = 8;
|
||||
void addPlaylistToCache(in int playlist_id, in int owner_id, in String access_key) = 9;
|
||||
void deletePlaylistFromCache(in int playlist_id, in int owner_id) = 10;
|
||||
|
||||
// [ver 1.2]
|
||||
String getCacheCatalog() = 11;
|
||||
List<String> getTracksInPlaylist(in String playlist_id, in String owner_id) = 12;
|
||||
String getPlaylistDefJson(in String playlist_id, in String owner_id) = 13;
|
||||
|
||||
// [ver 1.3]
|
||||
boolean isPlaylistCached(in int playlist_id, in int owner_id) = 14;
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* This file is auto-generated. DO NOT MODIFY.
|
||||
*/
|
||||
package bruhcollective.itaysonlab.libvkx;
|
||||
|
||||
// Works only with original signature of VT
|
||||
|
||||
public interface ILibVkxService extends android.os.IInterface {
|
||||
void play(java.util.List<String> audio_ids, int index, String playbackContext) throws android.os.RemoteException;
|
||||
|
||||
int getUserId() throws android.os.RemoteException;
|
||||
// Starts playback. playbackContext can be "other" if you don't sure what it is
|
||||
|
||||
void addTrackToCache(int track_id, int owner_id, String access_key) throws android.os.RemoteException;
|
||||
// A UserID. CHECK FOR THIS TO BE EQUAL BEFORE SENDING ANYTHING TO SERVICE!
|
||||
|
||||
void deleteTrackFromCache(int track_id, int owner_id) throws android.os.RemoteException;
|
||||
// Cache add/remove
|
||||
|
||||
boolean isTrackCached(int track_id, int owner_id) throws android.os.RemoteException;
|
||||
|
||||
java.util.List<String> getCache() throws android.os.RemoteException;
|
||||
|
||||
/**
|
||||
* Default implementation for ILibVkxService.
|
||||
*/
|
||||
class Default implements ILibVkxService {
|
||||
// Starts playback. playbackContext can be "other" if you don't sure what it is
|
||||
|
||||
@Override
|
||||
public void play(java.util.List<String> audio_ids, int index, String playbackContext) throws android.os.RemoteException {
|
||||
}
|
||||
// A UserID. CHECK FOR THIS TO BE EQUAL BEFORE SENDING ANYTHING TO SERVICE!
|
||||
|
||||
@Override
|
||||
public int getUserId() throws android.os.RemoteException {
|
||||
return 0;
|
||||
}
|
||||
// Cache add/remove
|
||||
|
||||
@Override
|
||||
public void addTrackToCache(int track_id, int owner_id, String access_key) throws android.os.RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrackFromCache(int track_id, int owner_id) throws android.os.RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrackCached(int track_id, int owner_id) throws android.os.RemoteException {
|
||||
return false;
|
||||
}
|
||||
// Get a list of cached tracks. The response is close to VK's original response (an audio list)
|
||||
// Also, the items in the list are JSON ones.
|
||||
|
||||
@Override
|
||||
public java.util.List<String> getCache() throws android.os.RemoteException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.os.IBinder asBinder() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Get a list of cached tracks. The response is close to VK's original response (an audio list)
|
||||
// Also, the items in the list are JSON ones.
|
||||
|
||||
/**
|
||||
* Local-side IPC implementation stub class.
|
||||
*/
|
||||
abstract class Stub extends android.os.Binder implements ILibVkxService {
|
||||
static final int TRANSACTION_play = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
|
||||
static final int TRANSACTION_getUserId = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
|
||||
static final int TRANSACTION_addTrackToCache = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
|
||||
static final int TRANSACTION_deleteTrackFromCache = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
|
||||
static final int TRANSACTION_isTrackCached = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
|
||||
static final int TRANSACTION_getCache = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
|
||||
private static final String DESCRIPTOR = "bruhcollective.itaysonlab.libvkx.ILibVkxService";
|
||||
|
||||
/**
|
||||
* Construct the stub at attach it to the interface.
|
||||
*/
|
||||
public Stub() {
|
||||
this.attachInterface(this, DESCRIPTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast an IBinder object into an bruhcollective.itaysonlab.libvkx.ILibVkxService interface,
|
||||
* generating a proxy if needed.
|
||||
*/
|
||||
public static ILibVkxService asInterface(android.os.IBinder obj) {
|
||||
if ((obj == null)) {
|
||||
return null;
|
||||
}
|
||||
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
|
||||
if (((iin != null) && (iin instanceof ILibVkxService))) {
|
||||
return ((ILibVkxService) iin);
|
||||
}
|
||||
return new Proxy(obj);
|
||||
}
|
||||
|
||||
public static boolean setDefaultImpl(ILibVkxService impl) {
|
||||
if (Proxy.sDefaultImpl == null && impl != null) {
|
||||
Proxy.sDefaultImpl = impl;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ILibVkxService getDefaultImpl() {
|
||||
return Proxy.sDefaultImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.os.IBinder asBinder() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
|
||||
String descriptor = DESCRIPTOR;
|
||||
switch (code) {
|
||||
case INTERFACE_TRANSACTION: {
|
||||
reply.writeString(descriptor);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_play: {
|
||||
data.enforceInterface(descriptor);
|
||||
java.util.List<String> _arg0;
|
||||
_arg0 = data.createStringArrayList();
|
||||
int _arg1;
|
||||
_arg1 = data.readInt();
|
||||
String _arg2;
|
||||
_arg2 = data.readString();
|
||||
this.play(_arg0, _arg1, _arg2);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getUserId: {
|
||||
data.enforceInterface(descriptor);
|
||||
int _result = this.getUserId();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(_result);
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_addTrackToCache: {
|
||||
data.enforceInterface(descriptor);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
int _arg1;
|
||||
_arg1 = data.readInt();
|
||||
String _arg2;
|
||||
_arg2 = data.readString();
|
||||
this.addTrackToCache(_arg0, _arg1, _arg2);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_deleteTrackFromCache: {
|
||||
data.enforceInterface(descriptor);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
int _arg1;
|
||||
_arg1 = data.readInt();
|
||||
this.deleteTrackFromCache(_arg0, _arg1);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_isTrackCached: {
|
||||
data.enforceInterface(descriptor);
|
||||
int _arg0;
|
||||
_arg0 = data.readInt();
|
||||
int _arg1;
|
||||
_arg1 = data.readInt();
|
||||
boolean _result = this.isTrackCached(_arg0, _arg1);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(((_result) ? (1) : (0)));
|
||||
return true;
|
||||
}
|
||||
case TRANSACTION_getCache: {
|
||||
data.enforceInterface(descriptor);
|
||||
java.util.List<String> _result = this.getCache();
|
||||
reply.writeNoException();
|
||||
reply.writeStringList(_result);
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Proxy implements ILibVkxService {
|
||||
public static ILibVkxService sDefaultImpl;
|
||||
private final android.os.IBinder mRemote;
|
||||
|
||||
Proxy(android.os.IBinder remote) {
|
||||
mRemote = remote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.os.IBinder asBinder() {
|
||||
return mRemote;
|
||||
}
|
||||
// Starts playback. playbackContext can be "other" if you don't sure what it is
|
||||
|
||||
public String getInterfaceDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
// A UserID. CHECK FOR THIS TO BE EQUAL BEFORE SENDING ANYTHING TO SERVICE!
|
||||
|
||||
@Override
|
||||
public void play(java.util.List<String> audio_ids, int index, String playbackContext) throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeStringList(audio_ids);
|
||||
_data.writeInt(index);
|
||||
_data.writeString(playbackContext);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_play, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
getDefaultImpl().play(audio_ids, index, playbackContext);
|
||||
return;
|
||||
}
|
||||
_reply.readException();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
}
|
||||
// Cache add/remove
|
||||
|
||||
@Override
|
||||
public int getUserId() throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
int _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_getUserId, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
return getDefaultImpl().getUserId();
|
||||
}
|
||||
_reply.readException();
|
||||
_result = _reply.readInt();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTrackToCache(int track_id, int owner_id, String access_key) throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(track_id);
|
||||
_data.writeInt(owner_id);
|
||||
_data.writeString(access_key);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_addTrackToCache, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
getDefaultImpl().addTrackToCache(track_id, owner_id, access_key);
|
||||
return;
|
||||
}
|
||||
_reply.readException();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrackFromCache(int track_id, int owner_id) throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(track_id);
|
||||
_data.writeInt(owner_id);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_deleteTrackFromCache, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
getDefaultImpl().deleteTrackFromCache(track_id, owner_id);
|
||||
return;
|
||||
}
|
||||
_reply.readException();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
}
|
||||
// Get a list of cached tracks. The response is close to VK's original response (an audio list)
|
||||
// Also, the items in the list are JSON ones.
|
||||
|
||||
@Override
|
||||
public boolean isTrackCached(int track_id, int owner_id) throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
boolean _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeInt(track_id);
|
||||
_data.writeInt(owner_id);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_isTrackCached, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
return getDefaultImpl().isTrackCached(track_id, owner_id);
|
||||
}
|
||||
_reply.readException();
|
||||
_result = (0 != _reply.readInt());
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.List<String> getCache() throws android.os.RemoteException {
|
||||
android.os.Parcel _data = android.os.Parcel.obtain();
|
||||
android.os.Parcel _reply = android.os.Parcel.obtain();
|
||||
java.util.List<String> _result;
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
boolean _status = mRemote.transact(Stub.TRANSACTION_getCache, _data, _reply, 0);
|
||||
if (!_status && getDefaultImpl() != null) {
|
||||
return getDefaultImpl().getCache();
|
||||
}
|
||||
_reply.readException();
|
||||
_result = _reply.createStringArrayList();
|
||||
} finally {
|
||||
_reply.recycle();
|
||||
_data.recycle();
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,13 @@ public class LibVKXClient {
|
||||
return getInstance().runOnService(action);
|
||||
}
|
||||
|
||||
public static void lambdaplay(List<MusicTrack> list, MusicTrack musicTrack, ILibVkxService iLibVkxService) {
|
||||
public static void lambdaplay(List<MusicTrack> list, MusicTrack musicTrack, ILibVkxService iLibVkxService, MusicPlaybackLaunchContext playerRefer) {
|
||||
var playbackContext = "";
|
||||
|
||||
if (playerRefer != null) {
|
||||
playbackContext = playerRefer.v0();
|
||||
}
|
||||
|
||||
int indexOf = list.indexOf(musicTrack);
|
||||
var ids = new ArrayList<String>();
|
||||
for (var track : list) {
|
||||
@@ -65,17 +71,21 @@ public class LibVKXClient {
|
||||
}
|
||||
|
||||
try {
|
||||
iLibVkxService.play(ids, indexOf, "");
|
||||
iLibVkxService.play(ids, indexOf, playbackContext);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String asId(MusicTrack musicTrack) {
|
||||
String str = musicTrack.e + "_" + musicTrack.d;
|
||||
if (musicTrack.O == null || musicTrack.O.equals("")) {
|
||||
return str;
|
||||
public static void lambdaplay(List<MusicTrack> list, MusicTrack musicTrack, ILibVkxService iLibVkxService){
|
||||
lambdaplay(list, musicTrack, iLibVkxService, null);
|
||||
}
|
||||
|
||||
public static String asId(MusicTrack musicTrack) {
|
||||
String id = musicTrack.e + "_" + musicTrack.d;
|
||||
if (musicTrack.O != null && !musicTrack.O.equals("")) {
|
||||
id += "_" + musicTrack.O;
|
||||
}
|
||||
return str + "_" + musicTrack.O;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
/**
|
||||
* Created by Aefyr on 20.05.2018.
|
||||
@@ -39,7 +40,7 @@ public class NotificationsHelper {
|
||||
downloadingPacksNotificationsIds = new HashMap<>();
|
||||
|
||||
if (oreo()) {
|
||||
NotificationChannel notificationChan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Сервис стикеров Telegram в VTosters", NotificationManager.IMPORTANCE_HIGH);
|
||||
NotificationChannel notificationChan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, AndroidUtils.getString("telegram_stickers_service"), NotificationManager.IMPORTANCE_HIGH);
|
||||
((NotificationManager) c.getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(notificationChan);
|
||||
}
|
||||
}
|
||||
@@ -49,8 +50,8 @@ public class NotificationsHelper {
|
||||
downloadingPacksNotificationsIds.put(pack, notificationId);
|
||||
|
||||
Notification.Builder builder = commonBuilder();
|
||||
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + ": загрузка" : pack.title + ": обновление");
|
||||
builder.setContentText("Получение информации");
|
||||
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString("telegram_stickers_downloading") : pack.title + AndroidUtils.getString("telegram_stickers_updating"));
|
||||
builder.setContentText(AndroidUtils.getString("telegram_stickers_gettinginfo"));
|
||||
builder.setOngoing(true);
|
||||
|
||||
manager.notify(notificationId, builder.build());
|
||||
@@ -63,7 +64,7 @@ public class NotificationsHelper {
|
||||
lastNotificationTime.set(System.currentTimeMillis());
|
||||
|
||||
Notification.Builder builder = commonBuilder();
|
||||
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + ": загрузка" : pack.title + ": обновление");
|
||||
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString("telegram_stickers_downloading") : pack.title + AndroidUtils.getString("telegram_stickers_updating"));
|
||||
builder.setContentText(downloadProgress + "/" + pack.stickersCount);
|
||||
builder.setProgress(pack.stickersCount, downloadProgress, false);
|
||||
builder.setOngoing(true);
|
||||
@@ -77,13 +78,13 @@ public class NotificationsHelper {
|
||||
Notification.Builder builder = commonBuilder();
|
||||
builder.setProgress(0, 0, false);
|
||||
builder.setOngoing(false);
|
||||
builder.setContentTitle("Сервис стикеров Telegram в VTosters");
|
||||
builder.setContentTitle(AndroidUtils.getString("telegram_stickers_service"));
|
||||
|
||||
String text;
|
||||
if (success)
|
||||
text = wasUpdate ? pack.title + " успешно обновлен" : pack.title + " успешно загружен";
|
||||
text = wasUpdate ? pack.title + AndroidUtils.getString("telegram_stickers_updated") : pack.title + AndroidUtils.getString("telegram_stickers_downloaded");
|
||||
else
|
||||
text = wasUpdate ? pack.title + ": ошибка при обновлении" : pack.title + ": ошибка при загрузке";
|
||||
text = wasUpdate ? pack.title + AndroidUtils.getString("telegram_stickers_error_update") : pack.title + AndroidUtils.getString("telegram_stickers_error_download");
|
||||
|
||||
if (e instanceof TelegramStickersGrabber.TSGException) {
|
||||
text = pack.title + ": " + e.getMessage();
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.vk.core.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.common.i.MediaUtils;
|
||||
import com.facebook.x.g.EncodedImage;
|
||||
import com.vk.core.concurrent.VkExecutors;
|
||||
import com.vk.imageloader.VKImageLoader;
|
||||
import com.vk.log.L;
|
||||
import com.vk.metrics.eventtracking.VkTracker;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.HashSet;
|
||||
|
||||
import b.h.g.m.CameraUtils;
|
||||
import b.h.g.m.FileUtils;
|
||||
|
||||
public class DownloadUtils {
|
||||
public static final HashSet<Long> a = new HashSet<>();
|
||||
|
||||
public static void a(Context context, String str, String str2) {
|
||||
a(context, str, str2, false);
|
||||
}
|
||||
|
||||
public static void b(Context context, String str, String str2) {
|
||||
a(context, str, str2, true);
|
||||
}
|
||||
|
||||
private static void a(Context context, String str, String str2, boolean z) {
|
||||
try {
|
||||
b(context, str, str2, z);
|
||||
} catch (Exception e2) {
|
||||
L.d(e2);
|
||||
ToastUtils.a(context.getString(R.string.error) + " [" + e2.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void b(final Context context, String str, String uri, boolean z) {
|
||||
if (TextUtils.isEmpty(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = a(str, uri);
|
||||
File file;
|
||||
|
||||
Log.v("DownloadUtils", "download: filename = " + fileName + " | str = " + str + "| str2 = " + uri);
|
||||
|
||||
if (fileName.endsWith(".jpg") || fileName.endsWith(".png") || fileName.endsWith(".jpeg")) {
|
||||
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(), fileName);
|
||||
} else if (fileName.endsWith(".mp3") || fileName.endsWith(".wav") || fileName.endsWith(".ogg") || fileName.endsWith(".aac") || fileName.endsWith(".flac") || fileName.endsWith(".m4a")) {
|
||||
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath(), fileName);
|
||||
} else if (fileName.endsWith(".mp4") || fileName.endsWith(".mkv") || fileName.endsWith(".avi") || fileName.endsWith(".flv") || fileName.endsWith(".wmv") || fileName.endsWith(".mov") || fileName.endsWith(".3gp")) {
|
||||
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath(), fileName);
|
||||
} else {
|
||||
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(), fileName);
|
||||
}
|
||||
|
||||
boolean isImage = MediaUtils.c(FileUtils.f(file));
|
||||
|
||||
if (isImage) {
|
||||
EncodedImage encodedImage = VKImageLoader.c(Uri.parse(uri));
|
||||
final InputStream h = encodedImage == null ? null : encodedImage.h();
|
||||
if (h != null) {
|
||||
try {
|
||||
if (file.getParentFile() != null) {
|
||||
file.createNewFile();
|
||||
}
|
||||
if (file.exists()) {
|
||||
VkExecutors.x.e().execute(() -> {
|
||||
try {
|
||||
FileUtils.b.a(h, new FileOutputStream(file), h.available(), null);
|
||||
ToastUtils.a(context.getString(R.string.file_saved, file.getAbsoluteFile()));
|
||||
} catch (IOException e) {
|
||||
ToastUtils.a(context.getString(R.string.error) + " [" + e.getMessage() + "]");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (Exception e2) {
|
||||
L.a(e2);
|
||||
}
|
||||
|
||||
CameraUtils.a(context, file, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(uri));
|
||||
var mimeType = FileUtils.f(file);
|
||||
request.setDestinationUri(Uri.fromFile(file));
|
||||
request.setMimeType(mimeType);
|
||||
request.setNotificationVisibility(1);
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setTitle(str);
|
||||
DownloadManager a2 = a(context);
|
||||
if (a2 == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
long enqueue = a2.enqueue(request);
|
||||
if (!z) {
|
||||
return;
|
||||
}
|
||||
a.add(enqueue);
|
||||
} catch (IllegalArgumentException e3) {
|
||||
L.a(e3);
|
||||
b(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static String a(String str, String str2) {
|
||||
String lastPathSegment = Uri.parse(str2).getLastPathSegment();
|
||||
if (!TextUtils.isEmpty(str)) {
|
||||
String replace = str.replace('/', '_');
|
||||
return !replace.contains(".") ? lastPathSegment : replace;
|
||||
}
|
||||
return lastPathSegment;
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("Range")
|
||||
public static boolean a(Context context, Uri uri) {
|
||||
try {
|
||||
Cursor query = a(context).query(new DownloadManager.Query().setFilterByStatus(11));
|
||||
if (query != null && query.moveToFirst()) {
|
||||
while (!uri.toString().equals(query.getString(query.getColumnIndex("uri")))) {
|
||||
if (!query.moveToNext()) {
|
||||
query.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@SuppressLint("Range")
|
||||
int i = query.getInt(query.getColumnIndex("status"));
|
||||
@SuppressLint("Range")
|
||||
String string = query.getString(query.getColumnIndex("local_uri"));
|
||||
query.close();
|
||||
if (i == 1 || i == 2) {
|
||||
ToastUtils.a(R.string.download_is_in_progress);
|
||||
query.close();
|
||||
return true;
|
||||
} else if (i != 8) {
|
||||
query.close();
|
||||
return false;
|
||||
} else {
|
||||
boolean a2 = a(context, string);
|
||||
query.close();
|
||||
return a2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (SQLiteException e2) {
|
||||
VkTracker.k.b(e2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void b(Context context) {
|
||||
if (context != null) {
|
||||
try {
|
||||
context.startActivity(new Intent("android.settings.APPLICATION_DETAILS_SETTINGS").setData(Uri.parse("package:com.android.providers.downloads")));
|
||||
} catch (ActivityNotFoundException e2) {
|
||||
L.a(e2);
|
||||
context.startActivity(new Intent("android.settings.MANAGE_APPLICATIONS_SETTINGS"));
|
||||
}
|
||||
ToastUtils.a("Please enable download manager", true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean a(Context context, String str) {
|
||||
File file;
|
||||
try {
|
||||
try {
|
||||
file = new File(new URI(str));
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
return false;
|
||||
}
|
||||
Intent intent = new Intent("android.intent.action.VIEW");
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
var mimeType = FileUtils.f(file);
|
||||
var uri = FileUtils.uriFromFile(file);
|
||||
Log.d("DownloadUtils", "openFile: " + file.getAbsolutePath() + "| mimetype " + mimeType + "| uri " + uri);
|
||||
if (!TextUtils.isEmpty(mimeType)) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setDataAndType(uri, mimeType);
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
throw new ActivityNotFoundException();
|
||||
} catch (ActivityNotFoundException unused) {
|
||||
return a(context, file);
|
||||
}
|
||||
} catch (ActivityNotFoundException unused2) {
|
||||
unused2.printStackTrace();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
VkTracker.k.b(th);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean a(Context context, File file) {
|
||||
if (file != null && file.exists()) {
|
||||
var mimeType = FileUtils.f(file);
|
||||
var uri = FileUtils.uriFromFile(file);
|
||||
|
||||
Intent intent = new Intent("android.intent.action.VIEW");
|
||||
intent.setDataAndType(uri, mimeType);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Log.d("DownloadUtils", "openFile: " + file.getAbsolutePath() + "| mimetype " + mimeType + "| uri " + uri);
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
} catch (ActivityNotFoundException e2) {
|
||||
L.a(e2);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static DownloadManager a(Context context) {
|
||||
return (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,7 @@ public enum DialogAction {
|
||||
DNT_OFF,
|
||||
pinmsg,
|
||||
unpinmsg,
|
||||
ENCRYPT_SETT,
|
||||
ENCRYPT,
|
||||
DOWNLOAD
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ public final class DialogActionsHelper {
|
||||
CollectionExt.a(arrayList, DialogAction.NOTIFICATIONS_OFF, dialog.b(b2));
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.vk.im.ui.views;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Corners{
|
||||
private int topLeft;
|
||||
private int topRight;
|
||||
private int bottomLeft;
|
||||
private int bottomRight;
|
||||
|
||||
public Corners(int i, int i2, int i3, int i4){
|
||||
this.topLeft = i;
|
||||
this.topRight = i2;
|
||||
this.bottomLeft = i3;
|
||||
this.bottomRight = i4;
|
||||
}
|
||||
|
||||
public Corners(int i, int i2, int i3, int i4, int i5) {
|
||||
this((i5 & 1) != 0 ? 0 : i, (i5 & 2) != 0 ? 0 : i2, (i5 & 4) != 0 ? 0 : i3, (i5 & 8) != 0 ? 0 : i4);
|
||||
}
|
||||
|
||||
public Corners() {
|
||||
this(0, 0, 0, 0, 15);
|
||||
}
|
||||
|
||||
public static Corners a(Corners corners, int i, int i2, int i3, int i4, int i5, Object obj){
|
||||
if ((i5 & 1) != 0) {
|
||||
i = corners.topLeft;
|
||||
}
|
||||
if ((i5 & 2) != 0) {
|
||||
i2 = corners.topRight;
|
||||
}
|
||||
if ((i5 & 4) != 0) {
|
||||
i3 = corners.bottomLeft;
|
||||
}
|
||||
if ((i5 & 8) != 0) {
|
||||
i4 = corners.bottomRight;
|
||||
}
|
||||
corners.a(i, i2, i3, i4);
|
||||
return corners;
|
||||
}
|
||||
|
||||
public int a(){
|
||||
return this.bottomLeft;
|
||||
}
|
||||
|
||||
public int b(){
|
||||
return this.bottomRight;
|
||||
}
|
||||
|
||||
public int c(){
|
||||
return this.topLeft;
|
||||
}
|
||||
|
||||
public int d(){
|
||||
return this.topRight;
|
||||
}
|
||||
|
||||
public boolean e(){
|
||||
int i;
|
||||
int i2 = this.topLeft;
|
||||
int i3 = this.topRight;
|
||||
return i2 == i3 && i3 == (i = this.bottomRight) && this.bottomLeft == i;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj){
|
||||
if (this != obj) {
|
||||
if (!(obj instanceof Corners)) {
|
||||
return false;
|
||||
}
|
||||
Corners corners = (Corners) obj;
|
||||
return this.topLeft == corners.topLeft && this.topRight == corners.topRight && this.bottomLeft == corners.bottomLeft && this.bottomRight == corners.bottomRight;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean f(){
|
||||
return this.topLeft == 0 && e();
|
||||
}
|
||||
|
||||
public void g(){
|
||||
a(0, 0);
|
||||
}
|
||||
|
||||
public int hashCode(){
|
||||
return (((((this.topLeft * 31) + this.topRight) * 31) + this.bottomLeft) * 31) + this.bottomRight;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toString(){
|
||||
return "Corners(topLeft=" + this.topLeft + ", topRight=" + this.topRight + ", bottomLeft=" + this.bottomLeft + ", bottomRight=" + this.bottomRight + ")";
|
||||
}
|
||||
|
||||
public Corners a(int i, int i2){
|
||||
if ((i2 & 1) > 0) {
|
||||
this.topLeft = i;
|
||||
}
|
||||
if ((i2 & 2) > 0) {
|
||||
this.topRight = i;
|
||||
}
|
||||
if ((i2 & 4) > 0) {
|
||||
this.bottomLeft = i;
|
||||
}
|
||||
if ((i2 & 8) > 0) {
|
||||
this.bottomRight = i;
|
||||
}
|
||||
if (i2 == 0) {
|
||||
this.topLeft = 0;
|
||||
this.topRight = 0;
|
||||
this.bottomLeft = 0;
|
||||
this.bottomRight = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Corners a(int i, int i2, int i3, int i4){
|
||||
this.topLeft = i;
|
||||
this.topRight = i2;
|
||||
this.bottomLeft = i3;
|
||||
this.bottomRight = i4;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Corners a(Corners corners){
|
||||
this.topLeft = corners.topLeft;
|
||||
this.topRight = corners.topRight;
|
||||
this.bottomLeft = corners.bottomLeft;
|
||||
this.bottomRight = corners.bottomRight;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.vk.im.ui.views.avatars;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.vk.im.ui.o;
|
||||
|
||||
public class StoryBorderView extends View {
|
||||
private boolean B;
|
||||
private final Paint a;
|
||||
private LinearGradient f15747b;
|
||||
private int f15748c;
|
||||
private int f15749d;
|
||||
private int f15750e;
|
||||
private int f15751f;
|
||||
private final int g;
|
||||
private final int h;
|
||||
|
||||
public StoryBorderView(Context context) {
|
||||
this(context, null, 0, 6);
|
||||
}
|
||||
|
||||
public StoryBorderView(Context context, AttributeSet attributeSet) {
|
||||
this(context, attributeSet, 0, 4);
|
||||
}
|
||||
|
||||
public StoryBorderView(Context context, AttributeSet attributeSet, int i, int i2) {
|
||||
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
||||
}
|
||||
|
||||
public int getBorderColor() {
|
||||
return this.f15749d;
|
||||
}
|
||||
|
||||
public int getBorderGradientEndColor() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
public int getBorderGradientStartColor() {
|
||||
return this.g;
|
||||
}
|
||||
|
||||
public int getBorderWidth() {
|
||||
return this.f15750e;
|
||||
}
|
||||
|
||||
public int getPadding() {
|
||||
return this.f15751f;
|
||||
}
|
||||
|
||||
public int getViewSize() {
|
||||
return this.f15748c;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawOval(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), this.a);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int i, int i2) {
|
||||
int i3 = this.f15748c;
|
||||
setMeasuredDimension(i3, i3);
|
||||
}
|
||||
|
||||
public void setBorderColor(int i) {
|
||||
if (this.f15749d != i) {
|
||||
this.f15749d = i;
|
||||
this.a.setColor(i);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setBorderWidth(int i) {
|
||||
if (this.f15750e != i) {
|
||||
this.f15750e = i;
|
||||
this.a.setStrokeWidth(i);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayoutParams(ViewGroup.LayoutParams layoutParams) {
|
||||
super.setLayoutParams(layoutParams);
|
||||
boolean z = false;
|
||||
this.f15748c = Math.min(layoutParams != null ? layoutParams.width : 0, layoutParams != null ? layoutParams.height : 0);
|
||||
this.f15747b = new LinearGradient(0.0f, 0.0f, 0.0f, this.f15748c, this.g, this.h, Shader.TileMode.MIRROR);
|
||||
if (this.f15748c > 0) {
|
||||
z = true;
|
||||
}
|
||||
if (z) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Only exact size supported, specify avatar sizes in layout params");
|
||||
}
|
||||
|
||||
public void setLive(boolean z) {
|
||||
if (this.B != z) {
|
||||
this.a.setShader(z ? this.f15747b : null);
|
||||
this.a.setColor(z ? -1 : this.f15749d);
|
||||
this.B = z;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setPadding(int i) {
|
||||
if (this.f15751f != i) {
|
||||
this.f15751f = i;
|
||||
setPadding(i, i, i, i);
|
||||
}
|
||||
}
|
||||
|
||||
public void setViewSize(int i) {
|
||||
this.f15748c = i;
|
||||
}
|
||||
|
||||
public StoryBorderView(Context context, AttributeSet attributeSet, int i) {
|
||||
super(context, attributeSet, i);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
this.a = paint;
|
||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, o.StoryBorderView);
|
||||
setBorderColor(obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderColor, 0));
|
||||
this.g = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientStartColor, 0);
|
||||
this.h = obtainStyledAttributes.getInt(o.StoryBorderView_vkim_borderGradientEndColor, 0);
|
||||
setBorderWidth(obtainStyledAttributes.getDimensionPixelSize(o.StoryBorderView_vkim_borderWidth, 0));
|
||||
obtainStyledAttributes.recycle();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
package com.vk.profile.ui.community;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.vk.core.ui.themes.Themable;
|
||||
import com.vk.core.ui.themes.VKThemeHelper;
|
||||
import com.vk.core.util.ContextExtKt;
|
||||
import com.vk.dto.stories.model.StoriesContainer;
|
||||
import com.vk.stories.view.BorderedCircleImageView;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
public final class CommunityBorderedImageView extends BorderedCircleImageView implements Themable{
|
||||
private boolean f0;
|
||||
private int g0;
|
||||
private int h0;
|
||||
private PorterDuffColorFilter i0;
|
||||
private PorterDuffColorFilter j0;
|
||||
private int unread;
|
||||
private int read;
|
||||
|
||||
public CommunityBorderedImageView(Context context){
|
||||
this(context, null, 0, 6);
|
||||
}
|
||||
|
||||
public CommunityBorderedImageView(Context context, AttributeSet attributeSet){
|
||||
this(context, attributeSet, 0, 4);
|
||||
}
|
||||
|
||||
public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i, int i2){
|
||||
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
||||
}
|
||||
|
||||
public CommunityBorderedImageView(Context context, AttributeSet attributeSet, int i){
|
||||
super(context, attributeSet, i);
|
||||
this.g0 = VKThemeHelper.d((int) R.attr.accent);
|
||||
this.h0 = ContextExtKt.a(context, (int) R.color.gray_200);
|
||||
this.i0 = new PorterDuffColorFilter(this.g0, PorterDuff.Mode.SRC_IN);
|
||||
this.j0 = new PorterDuffColorFilter(this.h0, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
public final void c(int unread, int read){ // get unread and read resid
|
||||
this.unread = unread;
|
||||
this.read = read;
|
||||
this.T = BitmapFactory.decodeResource(getResources(), this.unread);
|
||||
}
|
||||
|
||||
public final int getPrimaryColor(){
|
||||
return this.g0;
|
||||
}
|
||||
|
||||
public final void setPrimaryColor(int i){
|
||||
this.g0 = i;
|
||||
this.i0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
public final int getWasViewedColor(){
|
||||
return this.h0;
|
||||
}
|
||||
|
||||
public final void setWasViewedColor(int i){
|
||||
this.h0 = i;
|
||||
this.j0 = new PorterDuffColorFilter(i, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
public final void o(){
|
||||
this.f0 = false;
|
||||
l();
|
||||
}
|
||||
|
||||
@Override // com.vk.stories.view.ClippedImageView, android.widget.ImageView, android.view.View
|
||||
public void onDraw(Canvas canvas){
|
||||
super.onDraw(canvas);
|
||||
if (this.f0) {
|
||||
a(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public final void setStoryContainer(StoriesContainer storiesContainer){
|
||||
if (storiesContainer.L1()) {
|
||||
this.f0 = true;
|
||||
m();
|
||||
if (storiesContainer.K1()) {
|
||||
this.T = BitmapFactory.decodeResource(getResources(), this.unread);
|
||||
setBorderColorFilter(this.i0);
|
||||
b(getWidth(), getHeight());
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
this.T = BitmapFactory.decodeResource(getResources(), this.read);
|
||||
setBorderColorFilter(this.j0);
|
||||
b(getWidth(), getHeight());
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
o();
|
||||
}
|
||||
|
||||
@Override // com.vk.core.ui.themes.Themable
|
||||
public void v(){
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package com.vk.sharing.view;
|
||||
|
||||
import static b.h.z.f.roboto_medium;
|
||||
import static ru.vtosters.lite.utils.Preferences.preferences;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchy;
|
||||
import com.facebook.drawee.view.DraweeHolder;
|
||||
import com.vk.core.util.ContextExtKt;
|
||||
import com.vk.core.util.Screen;
|
||||
import com.vk.im.ui.views.avatars.AbbreviationAvatarDrawable;
|
||||
import com.vk.imageloader.view.VKMultiImageView;
|
||||
import com.vk.navigation.NavigatorKeys;
|
||||
import com.vtosters.lite.R;
|
||||
import com.vtosters.lite.a0;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@UiThread
|
||||
public class TargetImageView extends VKMultiImageView {
|
||||
private static final int I = Screen.a(2);
|
||||
private final int B;
|
||||
private final int C;
|
||||
private Drawable D;
|
||||
private String[] F;
|
||||
private int G;
|
||||
private final RectF f21629d;
|
||||
private final Paint f21630e;
|
||||
private Canvas f21631f;
|
||||
private final RectF g;
|
||||
private final Paint h;
|
||||
|
||||
public TargetImageView(Context context, Drawable d) {
|
||||
this(context, null, 0, 6);
|
||||
}
|
||||
|
||||
public TargetImageView(Context context, AttributeSet attributeSet) {
|
||||
this(context, attributeSet, 0, 4);
|
||||
}
|
||||
|
||||
public TargetImageView(Context context, AttributeSet attributeSet, int i, int i2) {
|
||||
this(context, (i2 & 2) != 0 ? null : attributeSet, (i2 & 4) != 0 ? 0 : i);
|
||||
}
|
||||
|
||||
private void e() {
|
||||
int i = this.G;
|
||||
for (int i2 = 0; i2 < i; i2++) {
|
||||
a(i2, this.F[i2]);
|
||||
}
|
||||
}
|
||||
|
||||
private void f() {
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingTop = getPaddingTop();
|
||||
int width = (getWidth() - paddingLeft) - getPaddingRight();
|
||||
int height = (getHeight() - paddingTop) - getPaddingBottom();
|
||||
int i = width / 2;
|
||||
int i2 = height / 2;
|
||||
int i3 = this.G;
|
||||
if (i3 == 1) {
|
||||
DraweeHolder<GenericDraweeHierarchy> a2 = this.a.a(0);
|
||||
Drawable d2 = a2.d();
|
||||
if (d2 != null) {
|
||||
d2.setBounds(paddingLeft, paddingTop, width + paddingLeft, height + paddingTop);
|
||||
}
|
||||
} else if (i3 == 2) {
|
||||
DraweeHolder<GenericDraweeHierarchy> a3 = this.a.a(0);
|
||||
Drawable d3 = a3.d();
|
||||
if (d3 != null) {
|
||||
int i4 = i + paddingLeft;
|
||||
int i5 = height + paddingTop;
|
||||
d3.setBounds(paddingLeft, paddingTop, i4, i5);
|
||||
DraweeHolder<GenericDraweeHierarchy> a4 = this.a.a(1);
|
||||
Drawable d4 = a4.d();
|
||||
if (d4 != null) {
|
||||
d4.setBounds(i4, paddingTop, paddingLeft + width, i5);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (i3 == 3) {
|
||||
DraweeHolder<GenericDraweeHierarchy> a5 = this.a.a(0);
|
||||
Drawable d5 = a5.d();
|
||||
if (d5 != null) {
|
||||
int i6 = i + paddingLeft;
|
||||
int i7 = height + paddingTop;
|
||||
d5.setBounds(paddingLeft, paddingTop, i6, i7);
|
||||
DraweeHolder<GenericDraweeHierarchy> a6 = this.a.a(1);
|
||||
Drawable d6 = a6.d();
|
||||
if (d6 != null) {
|
||||
int i9 = paddingLeft + width;
|
||||
int i10 = i2 + paddingTop;
|
||||
d6.setBounds(i6, paddingTop, i9, i10);
|
||||
DraweeHolder<GenericDraweeHierarchy> a7 = this.a.a(2);
|
||||
Drawable d7 = a7.d();
|
||||
if (d7 != null) {
|
||||
d7.setBounds(i6, i10, i9, i7);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
DraweeHolder<GenericDraweeHierarchy> a8 = this.a.a(0);
|
||||
Drawable d8 = a8.d();
|
||||
if (d8 != null) {
|
||||
int i11 = i + paddingLeft;
|
||||
int i13 = i2 + paddingTop;
|
||||
d8.setBounds(paddingLeft, paddingTop, i11, i13);
|
||||
DraweeHolder<GenericDraweeHierarchy> a9 = this.a.a(1);
|
||||
Drawable d9 = a9.d();
|
||||
if (d9 != null) {
|
||||
int i16 = height + paddingTop;
|
||||
d9.setBounds(paddingLeft, i13, i11, i16);
|
||||
DraweeHolder<GenericDraweeHierarchy> a10 = this.a.a(2);
|
||||
Drawable d10 = a10.d();
|
||||
if (d10 != null) {
|
||||
int i18 = paddingLeft + width;
|
||||
d10.setBounds(i11, paddingTop, i18, i13);
|
||||
DraweeHolder<GenericDraweeHierarchy> a11 = this.a.a(3);
|
||||
Drawable d11 = a11.d();
|
||||
if (d11 != null) {
|
||||
d11.setBounds(i11, i13, i18, i16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void g() {
|
||||
if (getWidth() == 0 || getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
f();
|
||||
}
|
||||
|
||||
private AbbreviationAvatarDrawable getAbbreviationDrawable() {
|
||||
return a(getContext(), null, 2);
|
||||
}
|
||||
|
||||
private AbbreviationAvatarDrawable a(Context context, Typeface typeface, int i) {
|
||||
return new AbbreviationAvatarDrawable(context, (i & 2) != 0 ? ContextExtKt.e(context, roboto_medium) : typeface);
|
||||
}
|
||||
|
||||
public void a(String str) {
|
||||
Arrays.fill(this.F, null);
|
||||
if (str == null) {
|
||||
b();
|
||||
this.G = 1;
|
||||
g();
|
||||
e();
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = Uri.parse(str);
|
||||
if (uri.getScheme().equals("vkchatphoto")) {
|
||||
this.G = 0;
|
||||
while (true) {
|
||||
String queryParameter = uri.getQueryParameter("photo" + this.G);
|
||||
if (queryParameter != null) {
|
||||
int i = this.G;
|
||||
this.G = i + 1;
|
||||
this.F[i] = queryParameter;
|
||||
} else {
|
||||
g();
|
||||
e();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (uri.getScheme().equals("vkchatplaceholder")) {
|
||||
this.G = 1;
|
||||
g();
|
||||
AbbreviationAvatarDrawable abbreviationDrawable = getAbbreviationDrawable();
|
||||
String queryParameter2 = uri.getQueryParameter(NavigatorKeys.Q);
|
||||
int parseInt = queryParameter2 != null ? Integer.parseInt(queryParameter2) : 0;
|
||||
String queryParameter3 = uri.getQueryParameter(NavigatorKeys.d);
|
||||
if (queryParameter3 == null) {
|
||||
queryParameter3 = "";
|
||||
}
|
||||
abbreviationDrawable.a(parseInt, queryParameter3);
|
||||
a(0, getAbbreviationDrawable());
|
||||
} else {
|
||||
this.F[0] = str;
|
||||
this.G = 1;
|
||||
g();
|
||||
e();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onDraw(Canvas canvas) {
|
||||
Canvas canvas2 = this.f21631f;
|
||||
int pref = preferences.getInt("pic_rounding", 0);
|
||||
|
||||
if (canvas2 != null) {
|
||||
canvas2.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
}
|
||||
int i = this.G;
|
||||
for (int i2 = 0; i2 < i; i2++) {
|
||||
DraweeHolder<GenericDraweeHierarchy> a2 = this.a.a(i2);
|
||||
Drawable d2 = a2.d();
|
||||
if (d2 != null) {
|
||||
Canvas canvas3 = this.f21631f;
|
||||
d2.draw(canvas3);
|
||||
}
|
||||
}
|
||||
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(this.f21629d, pref, pref, this.f21630e);
|
||||
} else {
|
||||
canvas.drawOval(this.f21629d, this.f21630e);
|
||||
}
|
||||
|
||||
if (isSelected()) {
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(this.g.left, this.g.top, this.g.right, this.g.bottom, pref, pref, this.h);
|
||||
} else {
|
||||
canvas.drawCircle(this.g.centerX(), this.g.centerY(), this.g.width() / 2, this.h);
|
||||
}
|
||||
|
||||
this.D.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onSizeChanged(int i, int i2, int i3, int i4) {
|
||||
super.onSizeChanged(i, i2, i3, i4);
|
||||
int paddingLeft = getPaddingLeft();
|
||||
int paddingRight = getPaddingRight();
|
||||
int paddingTop = getPaddingTop();
|
||||
int i5 = i - paddingRight;
|
||||
int paddingBottom = i2 - getPaddingBottom();
|
||||
this.f21629d.set(paddingLeft, paddingTop, i5, paddingBottom);
|
||||
Bitmap createBitmap = Bitmap.createBitmap(i5, paddingBottom, Bitmap.Config.ARGB_8888);
|
||||
this.f21631f = new Canvas(createBitmap);
|
||||
Shader.TileMode tileMode = Shader.TileMode.CLAMP;
|
||||
this.f21630e.setShader(new BitmapShader(createBitmap, tileMode, tileMode));
|
||||
float f2 = I / 2;
|
||||
this.g.set(f2, f2, i - f2, (float) i2 - f2);
|
||||
float f4 = i / 2;
|
||||
this.h.setShader(new LinearGradient(f4, 0.0f, f4, (float) i2, this.B, this.C, Shader.TileMode.CLAMP));
|
||||
Drawable drawable = this.D;
|
||||
drawable.setBounds(i - drawable.getIntrinsicWidth(), i2 - this.D.getIntrinsicHeight(), i, i2);
|
||||
f();
|
||||
}
|
||||
|
||||
public TargetImageView(Context context, AttributeSet attributeSet, int i) {
|
||||
super(context, attributeSet, i);
|
||||
this.f21629d = new RectF();
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setDither(true);
|
||||
paint.setFilterBitmap(true);
|
||||
this.f21630e = paint;
|
||||
this.g = new RectF();
|
||||
Paint paint2 = new Paint();
|
||||
paint2.setAntiAlias(true);
|
||||
paint2.setStrokeWidth(I);
|
||||
paint2.setStyle(Paint.Style.STROKE);
|
||||
this.h = paint2;
|
||||
this.B = getAccentColor();
|
||||
this.C = this.B;
|
||||
Drawable c2 = ContextExtKt.c(context, R.drawable.ic_check_circle_composite_24_full);
|
||||
if (c2 != null) {
|
||||
this.D = c2;
|
||||
this.F = new String[4];
|
||||
int i2 = I * 2;
|
||||
setPadding(i2, i2, i2, i2);
|
||||
if (attributeSet != null) {
|
||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, a0.TargetImageView);
|
||||
setSelected(obtainStyledAttributes.getBoolean(0, false));
|
||||
obtainStyledAttributes.recycle();
|
||||
}
|
||||
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.user_placeholder);
|
||||
for (int i3 = 0; i3 <= 3; i3++) {
|
||||
a();
|
||||
DraweeHolder<GenericDraweeHierarchy> a3 = this.a.a(i3);
|
||||
a3.b().e(drawable);
|
||||
}
|
||||
setScaleType(ScalingUtils.b.o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
package com.vk.stories.view;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.airbnb.lottie.LottieComposition;
|
||||
import com.airbnb.lottie.LottieCompositionFactory;
|
||||
import com.airbnb.lottie.LottieDrawable;
|
||||
import com.airbnb.lottie.LottieListener;
|
||||
import com.facebook.common.internal.Supplier;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
import com.facebook.u.b.a.PipelineDraweeControllerBuilder;
|
||||
import com.facebook.x.g.CloseableImage;
|
||||
import com.vk.core.ui.themes.Themable;
|
||||
import com.vk.core.ui.themes.VKThemeHelper;
|
||||
import com.vk.core.util.DrawableUtils;
|
||||
import com.vk.core.util.ResUtils;
|
||||
import com.vk.core.util.Screen;
|
||||
import com.vk.dto.stories.d.StoriesContainerExt;
|
||||
import com.vk.dto.stories.model.CommunityGroupedStoriesContainer;
|
||||
import com.vk.dto.stories.model.StoriesContainer;
|
||||
import com.vk.dto.stories.model.StoryEntry;
|
||||
import com.vk.im.engine.models.Image;
|
||||
import com.vk.im.ui.views.avatars.AvatarDataSource;
|
||||
import com.vk.imageloader.FrescoWrapper;
|
||||
import com.vk.stories.StoriesController;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class StoryCircleImageView extends BorderedCircleImageView implements Themable{
|
||||
private static final HashSet<Integer> B0 = new HashSet<>();
|
||||
private final PipelineDraweeControllerBuilder f0;
|
||||
private int A0;
|
||||
private Drawable g0;
|
||||
private Drawable h0;
|
||||
private Drawable i0;
|
||||
private int j0;
|
||||
private int k0;
|
||||
private int l0;
|
||||
private int m0;
|
||||
private int n0;
|
||||
private int o0;
|
||||
private Bitmap p0;
|
||||
private Bitmap q0;
|
||||
private Bitmap r0;
|
||||
private Bitmap s0;
|
||||
private boolean t0;
|
||||
private boolean u0;
|
||||
private boolean v0;
|
||||
private boolean w0;
|
||||
private boolean x0;
|
||||
private String y0;
|
||||
private LottieDrawable z0;
|
||||
|
||||
public StoryCircleImageView(Context context){
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public StoryCircleImageView(Context context, AttributeSet attributeSet){
|
||||
this(context, attributeSet, 0);
|
||||
}
|
||||
|
||||
public StoryCircleImageView(Context context, AttributeSet attributeSet, int i){
|
||||
super(context, attributeSet, i);
|
||||
this.f0 = FrescoWrapper.c.d();
|
||||
a(context, attributeSet, i);
|
||||
}
|
||||
|
||||
private void b(Canvas canvas){
|
||||
this.z0.draw(canvas);
|
||||
}
|
||||
|
||||
private boolean o(){
|
||||
return !this.u0 && !this.t0 && this.z0 == null && !this.w0 && !this.v0;
|
||||
}
|
||||
|
||||
private void p(){
|
||||
this.z0.stop();
|
||||
this.z0 = null;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void q(){
|
||||
int i = this.j0;
|
||||
if (i != 0 && VKThemeHelper.k.a(i)) {
|
||||
this.k0 = VKThemeHelper.d(this.j0);
|
||||
}
|
||||
this.g0 = DrawableUtils.a(this.g0, ColorStateList.valueOf(this.k0));
|
||||
Drawable drawable = this.g0;
|
||||
int i2 = this.A0;
|
||||
this.p0 = DrawableUtils.a(drawable, i2, i2);
|
||||
int i3 = this.l0;
|
||||
if (i3 != 0 && VKThemeHelper.k.a(i3)) {
|
||||
this.m0 = VKThemeHelper.d(this.l0);
|
||||
}
|
||||
int i4 = this.m0;
|
||||
if (i4 != 0) {
|
||||
this.i0 = DrawableUtils.a(this.i0, ColorStateList.valueOf(i4));
|
||||
}
|
||||
int i5 = this.n0;
|
||||
if (i5 != 0 && VKThemeHelper.k.a(i5)) {
|
||||
this.o0 = VKThemeHelper.d(this.n0);
|
||||
}
|
||||
Drawable a2 = DrawableUtils.a(this.g0, ColorStateList.valueOf(0));
|
||||
int i6 = this.A0;
|
||||
DrawableUtils.a(a2, i6, i6);
|
||||
Drawable a3 = DrawableUtils.a(this.h0, ColorStateList.valueOf(this.o0));
|
||||
int i7 = this.A0;
|
||||
this.r0 = DrawableUtils.a(a3, i7, i7);
|
||||
Drawable a4 = DrawableUtils.a(this.g0, ColorStateList.valueOf(ContextCompat.getColor(getContext(), R.color.orange)));
|
||||
int i8 = this.A0;
|
||||
this.s0 = DrawableUtils.a(a4, i8, i8);
|
||||
}
|
||||
|
||||
private void r(){
|
||||
this.c0 = Screen.a(o() ? 0 : 2);
|
||||
m();
|
||||
q();
|
||||
}
|
||||
|
||||
private void setUploadFailed(boolean z){
|
||||
this.u0 = z;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override // com.vk.stories.view.ClippedImageView, android.widget.ImageView, android.view.View
|
||||
public void onDraw(Canvas canvas){
|
||||
super.onDraw(canvas);
|
||||
setBorderAlpha(255);
|
||||
if (this.u0) {
|
||||
this.i0.draw(canvas);
|
||||
} else if (this.t0) {
|
||||
if (this.z0 != null) {
|
||||
b(canvas);
|
||||
return;
|
||||
}
|
||||
this.T = this.x0 ? this.s0 : this.p0;
|
||||
b(getWidth(), getHeight());
|
||||
a(canvas);
|
||||
} else if (this.z0 != null) {
|
||||
p();
|
||||
} else if (!this.w0) {
|
||||
|
||||
} else {
|
||||
this.T = this.r0;
|
||||
setBorderAlpha(163);
|
||||
b(getWidth(), getHeight());
|
||||
a(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // com.vk.stories.view.BorderedCircleImageView, android.view.View
|
||||
public void onSizeChanged(int i, int i2, int i3, int i4){
|
||||
super.onSizeChanged(i, i2, i3, i4);
|
||||
this.i0.setBounds(0, 0, i, i2);
|
||||
b(i);
|
||||
}
|
||||
|
||||
public void setStoryContainer(@Nullable StoriesContainer storiesContainer){
|
||||
boolean z = true;
|
||||
boolean z2 = storiesContainer != null && storiesContainer.Q1();
|
||||
this.v0 = storiesContainer != null && storiesContainer.S1() && !storiesContainer.L1();
|
||||
boolean z3 = storiesContainer instanceof CommunityGroupedStoriesContainer;
|
||||
this.x0 = z3;
|
||||
if (storiesContainer != null) {
|
||||
if (!StoriesContainerExt.c(storiesContainer) && !StoriesContainerExt.d(storiesContainer)) {
|
||||
this.T = this.p0;
|
||||
} else {
|
||||
this.T = this.q0;
|
||||
}
|
||||
b(getWidth(), getHeight());
|
||||
if (StoriesContainerExt.e(storiesContainer)) {
|
||||
a(storiesContainer);
|
||||
} else if (this.z0 != null) {
|
||||
p();
|
||||
}
|
||||
this.w0 = !storiesContainer.K1() && storiesContainer.L1();
|
||||
if (z2) {
|
||||
String h = storiesContainer.h(ResUtils.c(R.dimen.story_preview_image_size));
|
||||
if (h != null) {
|
||||
a(h);
|
||||
} else {
|
||||
g();
|
||||
}
|
||||
if (StoriesController.a(storiesContainer)) {
|
||||
setUploadFailed(true);
|
||||
a(false, false);
|
||||
} else {
|
||||
setUploadFailed(false);
|
||||
if (!storiesContainer.K1() && !StoriesContainerExt.c(storiesContainer)) {
|
||||
a(false, false);
|
||||
} else {
|
||||
StoryEntry A1 = storiesContainer.A1();
|
||||
if (A1 != null) {
|
||||
a(true, !B0.contains(A1.b));
|
||||
B0.add(A1.b);
|
||||
} else {
|
||||
a(true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setUploadFailed(false);
|
||||
if (!storiesContainer.K1() && !StoriesContainerExt.c(storiesContainer)) {
|
||||
z = false;
|
||||
}
|
||||
a(z, false);
|
||||
if (z3) {
|
||||
b(((CommunityGroupedStoriesContainer) storiesContainer).j(ResUtils.c(R.dimen.story_preview_image_size)));
|
||||
} else {
|
||||
String h2 = storiesContainer.h(ResUtils.c(R.dimen.story_preview_image_size));
|
||||
if (h2 != null) {
|
||||
a(h2);
|
||||
} else {
|
||||
g();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g();
|
||||
a(false, false);
|
||||
setUploadFailed(false);
|
||||
this.w0 = false;
|
||||
}
|
||||
r();
|
||||
}
|
||||
|
||||
@Override // com.vk.core.ui.themes.Themable
|
||||
public void v(){
|
||||
r();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void b(int i){
|
||||
LottieDrawable lottieDrawable = this.z0;
|
||||
if (lottieDrawable == null || lottieDrawable.e() == null) {
|
||||
return;
|
||||
}
|
||||
this.z0.d(i / this.z0.e().a().width());
|
||||
}
|
||||
|
||||
protected void a(Context context, AttributeSet attributeSet, int i){
|
||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, com.vtosters.lite.a0.StoryCircleImageView, i, 0);
|
||||
a(context, attributeSet, obtainStyledAttributes);
|
||||
obtainStyledAttributes.recycle();
|
||||
r();
|
||||
this.T = this.p0;
|
||||
}
|
||||
|
||||
public void b(@Nullable List<String> list){
|
||||
PipelineDraweeControllerBuilder pipelineDraweeControllerBuilder = this.f0;
|
||||
pipelineDraweeControllerBuilder.j();
|
||||
pipelineDraweeControllerBuilder.a(getController());
|
||||
setControllerListener(pipelineDraweeControllerBuilder);
|
||||
a(pipelineDraweeControllerBuilder, list);
|
||||
setController(pipelineDraweeControllerBuilder.k0());
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
private void a(Context context, AttributeSet attributeSet, TypedArray typedArray){ // bullshit vk code is not working with custom bordered circle image
|
||||
Drawable drawable;
|
||||
this.A0 = typedArray.getDimensionPixelSize(0, Screen.a(64.0f));
|
||||
if (typedArray.hasValue(2)) {
|
||||
this.g0 = typedArray.getDrawable(2);
|
||||
} else {
|
||||
this.g0 = ContextCompat.getDrawable(context, R.drawable.bg_stores_unread_border_80);
|
||||
}
|
||||
if (typedArray.hasValue(3)) {
|
||||
this.h0 = typedArray.getDrawable(3);
|
||||
} else {
|
||||
this.h0 = ContextCompat.getDrawable(context, R.drawable.bg_stores_read_border_80);
|
||||
}
|
||||
|
||||
int b2 = VKThemeHelper.b(attributeSet, "vk_borderTint");
|
||||
if (b2 != 0 && VKThemeHelper.k.a(b2)) {
|
||||
this.j0 = b2;
|
||||
} else if (typedArray.hasValue(5)) {
|
||||
this.k0 = typedArray.getColor(5, 0);
|
||||
} else {
|
||||
this.j0 = R.attr.accent;
|
||||
}
|
||||
|
||||
int b3 = VKThemeHelper.b(attributeSet, "vk_failBorderTint");
|
||||
if (b3 != 0 && VKThemeHelper.k.a(b3)) {
|
||||
this.l0 = b3;
|
||||
} else if (typedArray.hasValue(7)) {
|
||||
this.m0 = typedArray.getColor(7, 0);
|
||||
}
|
||||
|
||||
int b4 = VKThemeHelper.b(attributeSet, "vk_borderSeenTint");
|
||||
if (b4 != 0 && VKThemeHelper.k.a(b4)) {
|
||||
this.n0 = b4;
|
||||
} else if (typedArray.hasValue(4)) {
|
||||
this.o0 = typedArray.getColor(4, 0);
|
||||
} else {
|
||||
this.o0 = 0;
|
||||
}
|
||||
if (typedArray.hasValue(6)) {
|
||||
this.i0 = typedArray.getDrawable(6);
|
||||
} else {
|
||||
this.i0 = ContextCompat.getDrawable(context, R.drawable.ic_story_error_size64);
|
||||
}
|
||||
if (typedArray.hasValue(8)) {
|
||||
drawable = typedArray.getDrawable(8);
|
||||
} else {
|
||||
drawable = ContextCompat.getDrawable(context, R.drawable.bg_live_unread_border);
|
||||
}
|
||||
if (typedArray.hasValue(9)) {
|
||||
this.y0 = typedArray.getString(9);
|
||||
} else {
|
||||
this.y0 = "stories_animation_64.json";
|
||||
}
|
||||
|
||||
int i = this.A0;
|
||||
this.q0 = DrawableUtils.a(Objects.requireNonNull(drawable), i, i);
|
||||
Drawable drawable2 = this.g0;
|
||||
int i2 = this.A0;
|
||||
DrawableUtils.a(drawable2, i2, i2);
|
||||
Drawable drawable3 = this.h0;
|
||||
int i3 = this.A0;
|
||||
this.r0 = DrawableUtils.a(drawable3, i3, i3);
|
||||
Drawable drawable4 = this.g0;
|
||||
int i4 = this.A0;
|
||||
this.s0 = DrawableUtils.a(drawable4, i4, i4);
|
||||
}
|
||||
|
||||
public void a(StoriesContainer storiesContainer){
|
||||
if (this.z0 == null) {
|
||||
LottieCompositionFactory.a(getContext(), this.y0).b(new a(storiesContainer));
|
||||
}
|
||||
}
|
||||
|
||||
public void a(StoriesContainer storiesContainer, LottieComposition lottieComposition){
|
||||
this.z0 = new LottieDrawable();
|
||||
this.z0.a(lottieComposition);
|
||||
this.z0.d(-1);
|
||||
b(getWidth());
|
||||
if (!StoriesContainerExt.f(storiesContainer)) {
|
||||
this.z0.a(new b());
|
||||
this.z0.start();
|
||||
return;
|
||||
}
|
||||
this.z0.stop();
|
||||
}
|
||||
|
||||
private void a(boolean z, boolean z2){
|
||||
k();
|
||||
this.t0 = z;
|
||||
if (!z2) {
|
||||
setSelectionAmount(1.0f);
|
||||
} else {
|
||||
n();
|
||||
}
|
||||
}
|
||||
|
||||
private void a(@NonNull PipelineDraweeControllerBuilder pipelineDraweeControllerBuilder, @Nullable final List<String> list){
|
||||
pipelineDraweeControllerBuilder.b(null);
|
||||
pipelineDraweeControllerBuilder.a((Supplier<DataSource<CloseableReference<com.facebook.x.g.CloseableImage>>>) null);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
if (list.size() == 1) {
|
||||
ImageRequestBuilder b2 = ImageRequestBuilder.b(Uri.parse(list.get(0)));
|
||||
b2.a(ImageRequest.CacheChoice.SMALL);
|
||||
pipelineDraweeControllerBuilder.b(b2.a());
|
||||
return;
|
||||
}
|
||||
|
||||
pipelineDraweeControllerBuilder.a((Supplier<DataSource<CloseableReference<CloseableImage>>>) () -> StoryCircleImageView.this.a(list));
|
||||
return;
|
||||
}
|
||||
pipelineDraweeControllerBuilder.b(null);
|
||||
}
|
||||
|
||||
public DataSource<CloseableReference<CloseableImage>> a(@Nullable List<String> list){
|
||||
ArrayList<Image> arrayList = new ArrayList<>();
|
||||
for (String o : Objects.requireNonNull(list)) {
|
||||
arrayList.add(new Image(-1, -1, o));
|
||||
}
|
||||
return AvatarDataSource.a(arrayList, this.A0, 0);
|
||||
}
|
||||
|
||||
public class a implements LottieListener<LottieComposition>{
|
||||
final StoriesContainer a;
|
||||
|
||||
a(StoriesContainer storiesContainer){
|
||||
this.a = storiesContainer;
|
||||
}
|
||||
|
||||
@Override // com.airbnb.lottie.LottieListener
|
||||
public void a(LottieComposition lottieComposition){
|
||||
if (StoryCircleImageView.this.getWidth() > 0) {
|
||||
StoryCircleImageView.this.a(this.a, lottieComposition);
|
||||
} else {
|
||||
StoryCircleImageView.this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver$OnGlobalLayoutListenerC0393a(lottieComposition));
|
||||
}
|
||||
}
|
||||
|
||||
public class ViewTreeObserver$OnGlobalLayoutListenerC0393a implements ViewTreeObserver.OnGlobalLayoutListener{
|
||||
final LottieComposition a;
|
||||
|
||||
ViewTreeObserver$OnGlobalLayoutListenerC0393a(LottieComposition lottieComposition){
|
||||
this.a = lottieComposition;
|
||||
}
|
||||
|
||||
@Override // android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
public void onGlobalLayout(){
|
||||
StoryCircleImageView.this.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
a aVar = a.this;
|
||||
StoryCircleImageView.this.a(aVar.a, this.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class b implements ValueAnimator.AnimatorUpdateListener{
|
||||
b(){
|
||||
}
|
||||
|
||||
@Override // android.animation.ValueAnimator.AnimatorUpdateListener
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator){
|
||||
StoryCircleImageView.this.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3560,26 +3560,26 @@ public class R {
|
||||
public static final int stickers_notifications_size = 0x7f070285;
|
||||
public static final int stickers_tabs_padding = 0x7f070286;
|
||||
public static final int stickers_tabs_size = 0x7f070287;
|
||||
public static final int stiсkers_catalog_button_padding_bottom = 0x7f070288;
|
||||
public static final int stiсkers_catalog_button_padding_left = 0x7f070289;
|
||||
public static final int stiсkers_catalog_button_padding_right = 0x7f07028a;
|
||||
public static final int stiсkers_catalog_button_padding_top = 0x7f07028b;
|
||||
public static final int stiсkers_catalog_double_stacked_slider_image_bg_size = 0x7f07028c;
|
||||
public static final int stiсkers_catalog_double_stacked_slider_image_corner_radius = 0x7f07028d;
|
||||
public static final int stiсkers_catalog_double_stacked_slider_image_size = 0x7f07028e;
|
||||
public static final int stiсkers_catalog_large_slider_image_bg_height = 0x7f07028f;
|
||||
public static final int stiсkers_catalog_large_slider_image_bg_width = 0x7f070290;
|
||||
public static final int stiсkers_catalog_large_slider_image_corner_radius = 0x7f070291;
|
||||
public static final int stiсkers_catalog_large_slider_image_size = 0x7f070292;
|
||||
public static final int stiсkers_catalog_small_slider_image_bg_size = 0x7f070293;
|
||||
public static final int stiсkers_catalog_small_slider_image_corner_radius = 0x7f070294;
|
||||
public static final int stiсkers_catalog_small_slider_image_size = 0x7f070295;
|
||||
public static final int stiсkers_catalog_triple_stacked_slider_image_bg_size = 0x7f070296;
|
||||
public static final int stiсkers_catalog_triple_stacked_slider_image_corner_radius = 0x7f070297;
|
||||
public static final int stiсkers_catalog_triple_stacked_slider_image_size = 0x7f070298;
|
||||
public static final int stiсkers_catalog_vertical_list_image_bg_size = 0x7f070299;
|
||||
public static final int stiсkers_catalog_vertical_list_image_corner_radius = 0x7f07029a;
|
||||
public static final int stiсkers_catalog_vertical_list_image_size = 0x7f07029b;
|
||||
public static final int stickers_catalog_button_padding_bottom = 0x7f070288;
|
||||
public static final int stickers_catalog_button_padding_left = 0x7f070289;
|
||||
public static final int stickers_catalog_button_padding_right = 0x7f07028a;
|
||||
public static final int stickers_catalog_button_padding_top = 0x7f07028b;
|
||||
public static final int stickers_catalog_double_stacked_slider_image_bg_size = 0x7f07028c;
|
||||
public static final int stickers_catalog_double_stacked_slider_image_corner_radius = 0x7f07028d;
|
||||
public static final int stickers_catalog_double_stacked_slider_image_size = 0x7f07028e;
|
||||
public static final int stickers_catalog_large_slider_image_bg_height = 0x7f07028f;
|
||||
public static final int stickers_catalog_large_slider_image_bg_width = 0x7f070290;
|
||||
public static final int stickers_catalog_large_slider_image_corner_radius = 0x7f070291;
|
||||
public static final int stickers_catalog_large_slider_image_size = 0x7f070292;
|
||||
public static final int stickers_catalog_small_slider_image_bg_size = 0x7f070293;
|
||||
public static final int stickers_catalog_small_slider_image_corner_radius = 0x7f070294;
|
||||
public static final int stickers_catalog_small_slider_image_size = 0x7f070295;
|
||||
public static final int stickers_catalog_triple_stacked_slider_image_bg_size = 0x7f070296;
|
||||
public static final int stickers_catalog_triple_stacked_slider_image_corner_radius = 0x7f070297;
|
||||
public static final int stickers_catalog_triple_stacked_slider_image_size = 0x7f070298;
|
||||
public static final int stickers_catalog_vertical_list_image_bg_size = 0x7f070299;
|
||||
public static final int stickers_catalog_vertical_list_image_corner_radius = 0x7f07029a;
|
||||
public static final int stickers_catalog_vertical_list_image_size = 0x7f07029b;
|
||||
public static final int story_author_title_top_space = 0x7f07029c;
|
||||
public static final int story_cadre_bottom_btn_top_padding = 0x7f07029d;
|
||||
public static final int story_camera_tabs_height = 0x7f07029e;
|
||||
|
||||
+11
-4
@@ -1,5 +1,6 @@
|
||||
package com.vtosters.lite.ui.bottomnavigation;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.vkme;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getCSTDock;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -219,13 +220,19 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
|
||||
// ViewCompat.setScaleY(mLargeLabel, !mStaticModeEnabled && !checked ? mLargeScale : 1.0f);
|
||||
ViewCompat.setScaleX(mSmallLabel, !mStaticModeEnabled && checked ? mSmallScale : 1.0f);
|
||||
ViewCompat.setScaleY(mSmallLabel, !mStaticModeEnabled && checked ? mSmallScale : 1.0f);
|
||||
// make mLargeLabel and mSmallLabel are bold
|
||||
mLargeLabel.setTypeface(null, Typeface.BOLD);
|
||||
mSmallLabel.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
// make mLargeLabel and mSmallLabel text size is 10sp
|
||||
mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
mSmallLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
|
||||
int textsize;
|
||||
|
||||
if (vkme()){
|
||||
textsize = 11;
|
||||
} else {
|
||||
textsize = 10;
|
||||
}
|
||||
|
||||
mLargeLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, textsize);
|
||||
mSmallLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, textsize);
|
||||
|
||||
mLargeLabel.setVisibility(/*checked ? VISIBLE :*/ INVISIBLE);
|
||||
mSmallLabel.setVisibility(VISIBLE);//checked ? INVISIBLE : VISIBLE);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.vtosters.lite.deviceinfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Device {
|
||||
private String androidId;
|
||||
private String bluetoothMac;
|
||||
@@ -86,6 +88,7 @@ public class Device {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "Device{serialId='" + this.serialId + "', androidId='" + this.androidId + "', wifiMac='" + this.wifiMac + "', bluetoothMac='" + this.bluetoothMac + "'}";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.vtosters.lite.deviceinfo;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiInfo;
|
||||
@@ -28,6 +29,7 @@ public class DeviceInfoCollector {
|
||||
private String serialId() {
|
||||
String str;
|
||||
try {
|
||||
@SuppressLint("PrivateApi")
|
||||
Class<?> cls = Class.forName("android.os.SystemProperties");
|
||||
Method method = cls.getMethod("get", String.class);
|
||||
str = (String) method.invoke(cls, "gsm.sn1");
|
||||
@@ -53,6 +55,7 @@ public class DeviceInfoCollector {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
private String androidId(Context context) {
|
||||
return Settings.Secure.getString(context.getContentResolver(), "android_id").toLowerCase();
|
||||
}
|
||||
@@ -99,7 +102,7 @@ public class DeviceInfoCollector {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = hardwareAddress.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(String.format("%02X:", Byte.valueOf(hardwareAddress[i])));
|
||||
sb.append(String.format("%02X:", hardwareAddress[i]));
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
|
||||
@@ -1,93 +1,297 @@
|
||||
package ru.vtosters.lite.dnr;
|
||||
|
||||
import static com.vk.im.engine.h.im_ic_done;
|
||||
import static com.vk.im.engine.h.im_ic_edit_msg;
|
||||
import static com.vk.im.engine.h.im_ic_more_vertical;
|
||||
import static com.vk.im.engine.h.im_ic_pinned_msg_hide;
|
||||
import static com.vk.im.engine.h.im_ic_pinned_msg_show;
|
||||
import static com.vk.im.ui.c.im_ic_keyboard;
|
||||
import static ru.vtosters.lite.dnr.DNRModule.hookDNR;
|
||||
import static ru.vtosters.lite.dnr.DNRModule.hookDNT;
|
||||
import static ru.vtosters.lite.dnr.DNRModule.isDnrEnabledFor;
|
||||
import static ru.vtosters.lite.dnr.DNRModule.isDntEnabledFor;
|
||||
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.isTablet;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.core.network.Network;
|
||||
import com.vk.im.engine.ImEngine1;
|
||||
import com.vk.im.engine.events.OnDialogUpdateEvent;
|
||||
import com.vk.im.engine.models.EntityIntMap;
|
||||
import com.vk.im.engine.models.dialogs.Dialog;
|
||||
import com.vk.im.engine.models.messages.Msg;
|
||||
import com.vk.im.engine.models.messages.MsgFromUser;
|
||||
import com.vk.im.ui.components.common.DialogAction;
|
||||
import com.vk.im.ui.components.common.MsgAction;
|
||||
import com.vk.im.ui.components.viewcontrollers.popup.DelegateMsg;
|
||||
import com.vk.im.ui.views.dialog_actions.DialogActionsListView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import ru.vtosters.lite.downloaders.MessagesDownloader;
|
||||
import ru.vtosters.lite.encryption.EncryptProvider;
|
||||
import ru.vtosters.lite.encryption.base.IMProcessor;
|
||||
import ru.vtosters.lite.hooks.CryptImHook;
|
||||
import ru.vtosters.lite.ui.dialogs.Translate;
|
||||
|
||||
public class DNRInjector {
|
||||
public static void inject(Dialog dialog, List<DialogAction> list) {
|
||||
int peerId = dialog.getId();
|
||||
int peerId = 0;
|
||||
|
||||
list.add(DialogAction.valueOf("pinmsg"));
|
||||
list.add(DialogAction.valueOf("unpinmsg"));
|
||||
if (dialog != null) {
|
||||
try {
|
||||
peerId = dialog.getId();
|
||||
} catch (Exception e){
|
||||
Log.d("DialoggetId", "null");
|
||||
}
|
||||
}
|
||||
|
||||
if (peerId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
list.add(DialogAction.DOWNLOAD);
|
||||
|
||||
list.add(DialogAction.pinmsg);
|
||||
list.add(DialogAction.unpinmsg);
|
||||
|
||||
if (isDnrEnabledFor(peerId)) {
|
||||
list.add(DialogAction.valueOf("DNR_OFF"));
|
||||
list.add(DialogAction.DNR_OFF);
|
||||
} else {
|
||||
list.add(DialogAction.valueOf("DNR_ON"));
|
||||
list.add(DialogAction.DNR_ON);
|
||||
}
|
||||
|
||||
if (isDntEnabledFor(peerId)) {
|
||||
list.add(DialogAction.valueOf("DNT_OFF"));
|
||||
list.add(DialogAction.DNT_OFF);
|
||||
} else {
|
||||
list.add(DialogAction.valueOf("DNT_ON"));
|
||||
list.add(DialogAction.DNT_ON);
|
||||
}
|
||||
|
||||
list.add(DialogAction.ENCRYPT);
|
||||
IMProcessor prov = EncryptProvider.getProcessorFor(peerId);
|
||||
if (prov != null && !prov.isPublic()) {
|
||||
list.add(DialogAction.ENCRYPT_SETT);
|
||||
}
|
||||
}
|
||||
|
||||
public static LinkedHashMap<DialogAction, Integer> injectToHashMap(LinkedHashMap<DialogAction, Integer> hashMap) {
|
||||
hashMap.put(DialogAction.valueOf("DNR_ON"), getIdentifier("DNR_ON", "string"));
|
||||
hashMap.put(DialogAction.valueOf("DNR_OFF"), getIdentifier("DNR_OFF", "string"));
|
||||
hashMap.put(DialogAction.valueOf("DNT_ON"), getIdentifier("DNT_ON", "string"));
|
||||
hashMap.put(DialogAction.valueOf("DNT_OFF"), getIdentifier("DNT_OFF", "string"));
|
||||
hashMap.put(DialogAction.DOWNLOAD, getIdentifier("download_dl", "string"));
|
||||
|
||||
hashMap.put(DialogAction.DNR_ON, getIdentifier("DNR_ON", "string"));
|
||||
hashMap.put(DialogAction.DNR_OFF, getIdentifier("DNR_OFF", "string"));
|
||||
hashMap.put(DialogAction.DNT_ON, getIdentifier("DNT_ON", "string"));
|
||||
hashMap.put(DialogAction.DNT_OFF, getIdentifier("DNT_OFF", "string"));
|
||||
|
||||
hashMap.put(DialogAction.valueOf("pinmsg"), getIdentifier("pinmsg", "string"));
|
||||
hashMap.put(DialogAction.valueOf("unpinmsg"), getIdentifier("unpinmsg", "string"));
|
||||
hashMap.put(DialogAction.ENCRYPT, getIdentifier("encryption", "string"));
|
||||
hashMap.put(DialogAction.ENCRYPT_SETT, getIdentifier("encryption_sett", "string"));
|
||||
|
||||
// hashMap.put(DialogAction.pinmsg, getIdentifier("pinmsg", "string"));
|
||||
// hashMap.put(DialogAction.unpinmsg, getIdentifier("unpinmsg", "string"));
|
||||
return hashMap;
|
||||
}
|
||||
|
||||
public static boolean onClick(Dialog dialog, DialogAction action) {
|
||||
var id = dialog.getId();
|
||||
@SuppressLint("ResourceType")
|
||||
public static List<Object> injectToList(List<Object> actions) {
|
||||
var list = new ArrayList<>(actions);
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.DOWNLOAD, 1, getIdentifier("im_ic_msgdl", "attr"), getIdentifier("download_dl", "string"))); // DialogAction, Int, Icon, String
|
||||
|
||||
if (action == DialogAction.valueOf("MARK_AS_READ")) {
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.DNR_ON, 2, im_ic_pinned_msg_hide, getIdentifier("DNR_ON", "string"))); // DialogAction, Int, Icon, String
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.DNR_OFF, 2, im_ic_pinned_msg_show, getIdentifier("DNR_OFF", "string"))); // DialogAction, Int, Icon, String
|
||||
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.DNT_ON, 2, im_ic_edit_msg, getIdentifier("DNT_ON", "string"))); // DialogAction, Int, Icon, String
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.DNT_OFF, 2, im_ic_edit_msg, getIdentifier("DNT_OFF", "string"))); // DialogAction, Int, Icon, String
|
||||
|
||||
if (!isTablet()) {
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT, 3, im_ic_keyboard, getIdentifier("encryption", "string"))); // DialogAction, Int, Icon, String
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT_SETT, 3, im_ic_more_vertical, getIdentifier("encryption_sett", "string"))); // DialogAction, Int, Icon, String
|
||||
}
|
||||
|
||||
list.add(new DialogActionsListView.b.a(DialogAction.MARK_AS_READ, 4, im_ic_done, getIdentifier("vkim_dialogs_list_option_mark_as_read", "string"))); // DialogAction, Int, Icon, String
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void forceInvalidateDialogActions(Dialog d) {
|
||||
EntityIntMap<Dialog> map = new EntityIntMap<>();
|
||||
map.a(d.getId(), d);
|
||||
ImEngine1.a().a(new OnDialogUpdateEvent(null, map));
|
||||
}
|
||||
|
||||
public static List<DialogAction> injectToListAccess(List<DialogAction> actions, Dialog dialog) {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
try {
|
||||
peerId = dialog.getId();
|
||||
} catch (Exception e){
|
||||
Log.d("DialoggetId", "null");
|
||||
}
|
||||
}
|
||||
|
||||
if (peerId == 0) {
|
||||
return actions;
|
||||
}
|
||||
|
||||
actions.add(DialogAction.DOWNLOAD);
|
||||
|
||||
if (isDnrEnabledFor(peerId)) {
|
||||
actions.add(DialogAction.DNR_OFF);
|
||||
} else {
|
||||
actions.add(DialogAction.DNR_ON);
|
||||
}
|
||||
|
||||
if (isDntEnabledFor(peerId)) {
|
||||
actions.add(DialogAction.DNT_OFF);
|
||||
} else {
|
||||
actions.add(DialogAction.DNT_ON);
|
||||
}
|
||||
|
||||
actions.add(DialogAction.ENCRYPT);
|
||||
IMProcessor prov = EncryptProvider.getProcessorFor(peerId);
|
||||
if (prov != null && !prov.isPublic()) {
|
||||
actions.add(DialogAction.ENCRYPT_SETT);
|
||||
}
|
||||
|
||||
actions.add(DialogAction.MARK_AS_READ);
|
||||
return actions;
|
||||
}
|
||||
|
||||
public static boolean onClickHeader(DialogAction action, Dialog dialog) {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
try {
|
||||
peerId = dialog.getId();
|
||||
} catch (Exception e){
|
||||
Log.d("DialoggetId", "null");
|
||||
}
|
||||
}
|
||||
|
||||
if (peerId == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.MARK_AS_READ) {
|
||||
DNRModule.hookRead(dialog);
|
||||
forceInvalidateDialogActions(dialog);
|
||||
}
|
||||
|
||||
if (action == DialogAction.DOWNLOAD) {
|
||||
File out = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "dialog-" + peerId + ".html");
|
||||
try {
|
||||
new MessagesDownloader().downloadDialog(peerId, new MessagesDownloader.HtmlDialogDownloaderFormatProvider(), out);
|
||||
} catch (Exception e) {
|
||||
sendToast(getString("download_dl_error"));
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.DNR_ON) {
|
||||
setDnr(dialog, true);
|
||||
forceInvalidateDialogActions(dialog);
|
||||
return true;
|
||||
} else if (action == DialogAction.DNR_OFF) {
|
||||
setDnr(dialog, false);
|
||||
forceInvalidateDialogActions(dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.DNT_ON) {
|
||||
setDnt(dialog, true);
|
||||
forceInvalidateDialogActions(dialog);
|
||||
return true;
|
||||
} else if (action == DialogAction.DNT_OFF) {
|
||||
setDnt(dialog, false);
|
||||
forceInvalidateDialogActions(dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.ENCRYPT_SETT) {
|
||||
CryptImHook.hookPref(peerId);
|
||||
return true;
|
||||
} else if (action == DialogAction.ENCRYPT) {
|
||||
CryptImHook.hook(peerId, dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean onClick(Dialog dialog, DialogAction action) {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
try {
|
||||
peerId = dialog.getId();
|
||||
} catch (Exception e){
|
||||
Log.d("DialoggetId", "null");
|
||||
}
|
||||
}
|
||||
|
||||
if (peerId == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.MARK_AS_READ) {
|
||||
DNRModule.hookRead(dialog);
|
||||
}
|
||||
|
||||
if (action == DialogAction.valueOf("DNR_ON")) {
|
||||
if (action == DialogAction.DOWNLOAD) {
|
||||
File out = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), peerId + "-dialog.html");
|
||||
try {
|
||||
new MessagesDownloader().downloadDialog(peerId, new MessagesDownloader.HtmlDialogDownloaderFormatProvider(), out);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.DNR_ON) {
|
||||
setDnr(dialog, true);
|
||||
return true;
|
||||
} else if (action == DialogAction.valueOf("DNR_OFF")) {
|
||||
} else if (action == DialogAction.DNR_OFF) {
|
||||
setDnr(dialog, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.valueOf("DNT_ON")) {
|
||||
if (action == DialogAction.DNT_ON) {
|
||||
setDnt(dialog, true);
|
||||
return true;
|
||||
} else if (action == DialogAction.valueOf("DNT_OFF")) {
|
||||
} else if (action == DialogAction.DNT_OFF) {
|
||||
setDnt(dialog, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.valueOf("pinmsg")) {
|
||||
pinnedMsg(id, true);
|
||||
if (action == DialogAction.pinmsg) {
|
||||
pinnedMsg(peerId, true);
|
||||
return true;
|
||||
} else if (action == DialogAction.valueOf("unpinmsg")) {
|
||||
pinnedMsg(id, false);
|
||||
} else if (action == DialogAction.unpinmsg) {
|
||||
pinnedMsg(peerId, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.ENCRYPT_SETT) {
|
||||
CryptImHook.hookPref(peerId);
|
||||
return true;
|
||||
} else if (action == DialogAction.ENCRYPT) {
|
||||
CryptImHook.hook(peerId, dialog);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -101,27 +305,40 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
public static boolean onClickMsg(Context context, MsgAction action, Msg msg) {
|
||||
var text = ((MsgFromUser) msg).f();
|
||||
var fullMsg = ((MsgFromUser) msg).j2(); // text + reply
|
||||
var reply = fullMsg.substring(text.length() + 1);
|
||||
var peerId = msg.v1();
|
||||
|
||||
if (action == MsgAction.valueOf("READTO")) {
|
||||
DNRModule.hookReadStartMsgTo(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
var isTextExist = !text.isEmpty() && !text.equals(" ");
|
||||
var isReplyExist = !reply.isEmpty() && !reply.equals(" ");
|
||||
if (msg instanceof MsgFromUser){
|
||||
var text = ((MsgFromUser) msg).f();
|
||||
var fullMsg = ((MsgFromUser) msg).j2(); // text + reply
|
||||
var reply = fullMsg.substring(text.length() + 1);
|
||||
|
||||
if (action == MsgAction.valueOf("TRANSLATE")) {
|
||||
if (isTextExist && isReplyExist) {
|
||||
Translate.showTranslatedText(context, text + "\n\nРеплай:\n" + reply);
|
||||
} else if (isReplyExist) {
|
||||
Translate.showTranslatedText(context, "Реплай:\n" + reply);
|
||||
} else if (isTextExist) {
|
||||
Translate.showTranslatedText(context, text);
|
||||
} else {
|
||||
sendToast("Нет текста для перевода");
|
||||
var isTextExist = !text.isEmpty() && !text.equals(" ");
|
||||
var isReplyExist = !reply.isEmpty() && !reply.equals(" ");
|
||||
|
||||
|
||||
if (isReplyExist) {
|
||||
reply = decryptMessage(reply, peerId);
|
||||
}
|
||||
|
||||
if (isTextExist) {
|
||||
text = decryptMessage(text, peerId);
|
||||
}
|
||||
|
||||
if (action == MsgAction.valueOf("TRANSLATE")) {
|
||||
if (isTextExist && isReplyExist) {
|
||||
Translate.showTranslatedText(context, text + "\n\n" + getString("translator_reply") + "\n" + reply);
|
||||
} else if (isReplyExist) {
|
||||
Translate.showTranslatedText(context, getString("translator_reply") + "\n" + reply);
|
||||
} else if (isTextExist) {
|
||||
Translate.showTranslatedText(context, text);
|
||||
} else {
|
||||
sendToast(getString("translator_no_text"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +349,7 @@ public class DNRInjector {
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
var request = new Request.a()
|
||||
.b("https://api.vk.com/method/" + (needToBePinned ? "messages.pinConversation" : "messages.unpinConversation") + "?peer_id=" + dialogid + "&access_token=" + getUserToken() + "&v=5.119")
|
||||
.b("https://" + getApi() + "/method/" + (needToBePinned ? "messages.pinConversation" : "messages.unpinConversation") + "?peer_id=" + dialogid + "&access_token=" + getUserToken() + "&v=5.119")
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8"))
|
||||
.a();
|
||||
|
||||
@@ -149,15 +366,27 @@ public class DNRInjector {
|
||||
|
||||
thread.start();
|
||||
|
||||
sendToast("Диалог успешно " + (needToBePinned ? "закреплен" : "откреплен"));
|
||||
sendToast(getString("pin_dialog") + " " + getString(needToBePinned ? "dialog_pinned" : "dialog_unpinned"));
|
||||
}
|
||||
|
||||
public static void setDnr(Dialog dialog, boolean value) {
|
||||
hookDNR(dialog.getId());
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
hookDNR(peerId);
|
||||
}
|
||||
|
||||
public static void setDnt(Dialog dialog, boolean value) {
|
||||
hookDNT(dialog.getId());
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
}
|
||||
|
||||
hookDNT(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ru.vtosters.lite.dnr;
|
||||
|
||||
import static ru.vtosters.lite.net.Request.makeRequest;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.getApi;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
@@ -1,111 +1,136 @@
|
||||
package ru.vtosters.lite.downloaders;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getTrackFolder;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
import ru.vtosters.lite.music.FFMpeg;
|
||||
import ru.vtosters.lite.music.M3UDownloader;
|
||||
import ru.vtosters.lite.music.MP3Downloader;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.music.callback.MusicCallbackBuilder;
|
||||
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
|
||||
import ru.vtosters.lite.music.downloader.AudioGet;
|
||||
import ru.vtosters.lite.music.downloader.PlaylistDownloader;
|
||||
import ru.vtosters.lite.music.downloader.TrackDownloader;
|
||||
import ru.vtosters.lite.music.notification.MusicNotificationBuilder;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
/**
|
||||
* Entrypoint for downloading audio
|
||||
* <p>
|
||||
* The actual code is in {@link ru.vtosters.lite.music}
|
||||
*/
|
||||
|
||||
public class AudioDownloader {
|
||||
private static NotificationManagerCompat notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
private static final Executor executor = Executors.newFixedThreadPool(4);
|
||||
|
||||
public static void downloadAudio(MusicTrack track) {
|
||||
if (AndroidUtils.getDefaultPrefs().getBoolean("new_music_downloading_way", false)) {
|
||||
downloadMP3(track);
|
||||
} else {
|
||||
downloadM3U8(track);
|
||||
}
|
||||
public static void downloadPlaylist(Playlist playlist) {
|
||||
var tracks = PlaylistConverter.getPlaylist(playlist);
|
||||
|
||||
var playlistName = IOUtils.getValidFileName(playlist.g);
|
||||
|
||||
var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
|
||||
var downloadPath = musicPath + File.separator + playlistName;
|
||||
|
||||
var notificationId = playlistName.hashCode();
|
||||
var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlistName, notificationId);
|
||||
|
||||
PlaylistDownloader.downloadPlaylist(
|
||||
tracks,
|
||||
IOUtils.getValidFileName(playlist.g),
|
||||
downloadPath,
|
||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
||||
);
|
||||
}
|
||||
|
||||
public static void downloadM3U8(MusicTrack track) {
|
||||
|
||||
public static void downloadAudio(MusicTrack track) {
|
||||
downloadM3U8(track, false);
|
||||
}
|
||||
|
||||
public static void cacheTrack(MusicTrack track) {
|
||||
var trackId = track.y1();
|
||||
if (CacheDatabaseDelegate.isCached(trackId)) {
|
||||
CacheDatabaseDelegate.removeTrackFromCache(LibVKXClient.asId(track));
|
||||
return;
|
||||
}
|
||||
|
||||
var trackFile = FileCacheImplementation.getTrackFile(trackId);
|
||||
if (!trackFile.exists())
|
||||
trackFile.getParentFile().mkdirs();
|
||||
downloadM3U8(track, true);
|
||||
MusicNotificationBuilder.notifySavingToCache(track);
|
||||
CacheDatabaseDelegate.insertTrack(track);
|
||||
}
|
||||
|
||||
public static void cachePlaylist(Playlist playlist) {
|
||||
var tracks = PlaylistConverter.getPlaylist(playlist);
|
||||
|
||||
var notificationId = playlist.g.hashCode();
|
||||
var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlist.g, notificationId);
|
||||
|
||||
PlaylistDownloader.cachePlaylist(
|
||||
tracks,
|
||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
||||
);
|
||||
}
|
||||
|
||||
public static void cacheAllAudios() {
|
||||
var tracks = AudioGet.getAudios();
|
||||
|
||||
var notificationId = getUserId();
|
||||
var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
||||
|
||||
PlaylistDownloader.cachePlaylist(
|
||||
tracks,
|
||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
||||
);
|
||||
}
|
||||
|
||||
public static void downloadAllAudios() {
|
||||
var tracks = AudioGet.getAudios();
|
||||
|
||||
var notificationId = getUserId();
|
||||
var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
|
||||
|
||||
var playlistName = "Audios of " + getUserId();
|
||||
|
||||
var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
|
||||
var downloadPath = musicPath + File.separator + playlistName;
|
||||
|
||||
PlaylistDownloader.downloadPlaylist(
|
||||
tracks,
|
||||
playlistName,
|
||||
downloadPath,
|
||||
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
|
||||
);
|
||||
}
|
||||
|
||||
private static void downloadM3U8(MusicTrack track, boolean cache) {
|
||||
if (track.D == null) {
|
||||
ToastUtils.a("Не удалось найти ссылку на аудиозапись");
|
||||
ToastUtils.a(getString("link_audio_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
|
||||
var tempId = track.d;
|
||||
var downloadPath = musicPath + File.separator + tempId;
|
||||
var downloadPath = musicPath + File.separator;
|
||||
var notification = MusicNotificationBuilder.buildDownloadNotification(track, tempId);
|
||||
|
||||
var notification = buildDownloadNotification(track, tempId);
|
||||
downloadM3U8(track, downloadPath, new Callback() {
|
||||
@Override
|
||||
public void onProgress(int progress) {
|
||||
notification.setProgress(100, progress, false);
|
||||
notificationManager.notify(tempId, notification.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
try {
|
||||
var fileName = track.toString();
|
||||
var success = FFMpeg.convert(downloadPath, musicPath + File.separator + fileName + ".mp3");
|
||||
if (success) {
|
||||
notification
|
||||
.setContentText("Загрузка завершена")
|
||||
.setProgress(0, 0, false)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done);
|
||||
notificationManager.notify(tempId, notification.build());
|
||||
} else {
|
||||
onFailure();
|
||||
}
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e("AudioDownloader", "Опять эти нативные либы");
|
||||
Log.e("AudioDownloader", e.getMessage());
|
||||
onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
notification.setContentText("Не удалось загрузить аудиозапись").setProgress(0, 0, false);
|
||||
notificationManager.notify(tempId, notification.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeReceived(long size, long header) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void downloadM3U8(MusicTrack track, String path, Callback callback) {
|
||||
File outDir = new File(path);
|
||||
if (!outDir.exists())
|
||||
if (outDir.mkdir())
|
||||
Log.v("AudioDownloader", "Directory created");
|
||||
else
|
||||
Log.e("AudioDownloader", "Directory creation failed");
|
||||
Log.i("AudioDownloader", "Downloading audio to " + path);
|
||||
M3UDownloader.execute(track.D, outDir, callback);
|
||||
}
|
||||
|
||||
private static void downloadMP3(MusicTrack track) {
|
||||
MP3Downloader.execute(track);
|
||||
}
|
||||
|
||||
private static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) {
|
||||
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setContentTitle("Загрузка аудиозаписи")
|
||||
.setContentText(track.toString())
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(true);
|
||||
notificationBuilder.setProgress(100, 0, false);
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
return notificationBuilder;
|
||||
if (cache) {
|
||||
TrackDownloader.cacheTrack(track, MusicCallbackBuilder.buildOneTrackCallback(tempId, notification));
|
||||
} else {
|
||||
TrackDownloader.downloadTrack(track, downloadPath, MusicCallbackBuilder.buildOneTrackCallback(tempId, notification));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,606 @@
|
||||
package ru.vtosters.lite.downloaders;
|
||||
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
|
||||
import static ru.vtosters.lite.net.Request.makeRequest;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getArray;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getResources;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MessagesDownloader{
|
||||
private static SimpleDateFormat sdformat;
|
||||
|
||||
private static SparseArray<MiniUser> usersArray;
|
||||
|
||||
private static final String VK_DOMAIN = "vk.com";
|
||||
|
||||
public static String formatTime(long now){
|
||||
return sdformat.format(new Date(now));
|
||||
}
|
||||
|
||||
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements){
|
||||
Objects.requireNonNull(delimiter);
|
||||
Objects.requireNonNull(elements);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (CharSequence cs : elements) {
|
||||
builder.append(cs).append(delimiter);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void downloadDialog(int peer_id, DialogDownloaderFormatProvider format, File out) throws Exception{
|
||||
sdformat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", getResources().getConfiguration().locale);
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(out);
|
||||
|
||||
// fix TODO with conversation name (API method messages.getConversationsById)
|
||||
fos.write(format.provideDocumentStart("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
fetchDialogInfo(peer_id, 0, ((users, messages) -> {
|
||||
try {
|
||||
fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
|
||||
for (MiniMsg msg : messages) {
|
||||
fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
|
||||
fos.close();
|
||||
Toast.makeText(getGlobalContext(), getString("saved_as") + " " + out.getAbsolutePath(), Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void fetchDialogInfo(int peer_id, int offset, FetchListener fl){
|
||||
makeRequest("https://" + getApi() + "/method/messages.getHistory?extended=1&v=5.140&offset=" + offset + "&count=200&peer_id=" + peer_id + "&access_token=" + getUserToken(),
|
||||
response -> {
|
||||
try {
|
||||
JSONObject mainJson = new JSONObject(response);
|
||||
JSONObject obj = mainJson.getJSONObject("response");
|
||||
|
||||
SparseArray<MiniUser> users = new SparseArray<>();
|
||||
parseUsers(users, obj.optJSONArray("profiles"), false);
|
||||
parseUsers(users, obj.optJSONArray("groups"), true);
|
||||
|
||||
List<MiniMsg> msgs = parseMessages(obj.getJSONArray("items"));
|
||||
|
||||
usersArray = users;
|
||||
|
||||
fl.onReceive(users, msgs);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<MiniMsg> parseMessages(JSONArray jsonArray) throws JSONException{
|
||||
ArrayList<MiniMsg> thing = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
MiniMsg msg = new MiniMsg(jsonArray.getJSONObject(i));
|
||||
thing.add(msg);
|
||||
}
|
||||
|
||||
return thing;
|
||||
}
|
||||
|
||||
private void parseUsers(SparseArray<MiniUser> array, @Nullable JSONArray jsonArray, boolean groups) throws JSONException{
|
||||
if (jsonArray == null) return;
|
||||
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
MiniUser user = new MiniUser(jsonArray.getJSONObject(i), groups);
|
||||
array.put(groups ? -user.id : user.id, user);
|
||||
}
|
||||
}
|
||||
|
||||
interface FetchListener{
|
||||
void onReceive(SparseArray<MiniUser> users, List<MiniMsg> messages);
|
||||
}
|
||||
|
||||
static class MiniUser{
|
||||
public String firstName;
|
||||
public String lastName;
|
||||
public boolean isGroup;
|
||||
int id;
|
||||
String photo100;
|
||||
|
||||
public MiniUser(JSONObject obj, boolean is_group) throws JSONException {
|
||||
id = obj.getInt("id");
|
||||
firstName = obj.optString("first_name", obj.optString("name"));
|
||||
lastName = obj.optString("last_name", "");
|
||||
photo100 = obj.getString("photo_100");
|
||||
isGroup = is_group;
|
||||
}
|
||||
}
|
||||
|
||||
static class MiniMsg{
|
||||
long date;
|
||||
int fromId;
|
||||
int id; // message id
|
||||
String text;
|
||||
List<JSONObject> attachments;
|
||||
List<JSONObject> fwd_messages;
|
||||
JSONObject reply_message; // one reply
|
||||
JSONObject chat_action;
|
||||
JSONObject geo;
|
||||
|
||||
public MiniMsg(JSONObject obj) throws JSONException{
|
||||
date = obj.getLong("date") * 1000;
|
||||
fromId = obj.getInt("from_id");
|
||||
id = obj.getInt("id");
|
||||
text = decryptMessage(obj.getString("text"), obj.getInt("peer_id"));
|
||||
reply_message = obj.optJSONObject("reply_message");
|
||||
chat_action = obj.optJSONObject("action");
|
||||
geo = obj.optJSONObject("geo");
|
||||
|
||||
attachments = new ArrayList<>();
|
||||
fwd_messages = new ArrayList<>();
|
||||
|
||||
JSONArray arr = obj.optJSONArray("attachments");
|
||||
if (arr != null)
|
||||
for (int i = 0; i < arr.length(); i++) attachments.add(arr.getJSONObject(i));
|
||||
|
||||
JSONArray fwdArr = obj.optJSONArray("fwd_messages");
|
||||
if (fwdArr != null)
|
||||
for (int i = 0; i < fwdArr.length(); i++) fwd_messages.add(fwdArr.getJSONObject(i));
|
||||
}
|
||||
}
|
||||
|
||||
// This class defines everything which is needed to create an output file
|
||||
abstract static class DialogDownloaderFormatProvider{
|
||||
// Internal, for JSON/HTML
|
||||
abstract String provideDocumentStart(String dialogName, String date);
|
||||
|
||||
abstract String provideDocumentEnd();
|
||||
|
||||
abstract String provideHeader(String dialogName, String date);
|
||||
|
||||
abstract String provideMessage(MiniMsg message, MiniUser user) throws JSONException;
|
||||
}
|
||||
|
||||
public static class HtmlDialogDownloaderFormatProvider extends DialogDownloaderFormatProvider{
|
||||
@Override
|
||||
String provideDocumentStart(String dialogName, String date){
|
||||
String style = ".vtex-milk-msg {\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" padding: 24px;\n" +
|
||||
" background: white;\n" +
|
||||
" margin: 16px;\n" +
|
||||
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.07);\n" +
|
||||
" word-wrap: break-word;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".vtex-milk-msgcont br {\n" +
|
||||
" display: none;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".vtex-milk-msg p {\n" +
|
||||
" margin: 0;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".vtex-milk-msg.vtex-milk-msg-out {\n" +
|
||||
" background: #ADD3FF;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"p.msg-attaches {\n" +
|
||||
" margin-top: 8px;\n" +
|
||||
" font-style: italic;\n" +
|
||||
"}\n" +
|
||||
"p.chat-action {\n" +
|
||||
" font-style: italic;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".vtex-milk-header {\n" +
|
||||
" border-radius: 8px;\n" +
|
||||
" padding: 24px;\n" +
|
||||
" background: white;\n" +
|
||||
" margin: 16px;\n" +
|
||||
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"h4.vtex-milk-header-txt {\n" +
|
||||
" margin: 0;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"h5.vtex-milk-header-sub {\n" +
|
||||
" margin: 0;\n" +
|
||||
" padding-top: 8px;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"a.msg-attach-link {\n" +
|
||||
" color: #5181B8;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".msg-from a {\n" +
|
||||
" color: #5181B8;\n" +
|
||||
" text-decoration: none;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".msg-from-date {\n" +
|
||||
" color: rgba(0, 0, 0, 0.75);\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"body {\n" +
|
||||
" font-family: Roboto, system-ui, sans-serif;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
".msg-body {\n" +
|
||||
" padding-top: 4px;\n" +
|
||||
"}\n" +
|
||||
".msg-reply {\n" +
|
||||
" border-left: 3px solid #7aa7f6;\n" +
|
||||
" margin-top: 5px;\n" +
|
||||
" padding-left: 3px;\n" +
|
||||
"}\n\n" +
|
||||
".msg-reply p {\n" +
|
||||
" padding-left: 5px;\n" +
|
||||
"}\n\n" +
|
||||
".msg-reply p.reply-header {\n" +
|
||||
" color: #5181B8;\n" +
|
||||
"}\n\n" +
|
||||
".msg-reply p.reply-content {\n" +
|
||||
" color: #000000;\n" +
|
||||
"}\n" +
|
||||
".vtex-milk-msg.poll {\n" +
|
||||
" width: 45%;\n" +
|
||||
" background: linear-gradient(to bottom right, #707c8c, #4d5565);\n" +
|
||||
" font-style: normal;" +
|
||||
"}\n" +
|
||||
".vtex-milk-msg.poll p { color: white; }\n" +
|
||||
".vtex-milk-msg.poll hr { border-top: 1px; background: #ffffff; }\n" +
|
||||
"\n" +
|
||||
"";
|
||||
|
||||
return "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"utf-8\"><title>" + getString("chat_export_title") + "</title><style>" + style + "</style></head><body>";
|
||||
}
|
||||
|
||||
@Override
|
||||
String provideDocumentEnd(){
|
||||
return "</div></body></html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
String provideHeader(String dialogName, String date){
|
||||
return "<div class=\"vtex-milk-header\"><h4 class=\"vtex-milk-header-txt\">VTLite</h4><h5 class=\"vtex-milk-header-sub\">" + getString("chat_export_header") + " (" + date + ")</h5></div><div class=\"vtex-milk-msgcont\">";
|
||||
}
|
||||
|
||||
@Override
|
||||
String provideMessage(MiniMsg message, MiniUser user) throws JSONException{
|
||||
String isOut = (user.id == getUserId()) ? " vtex-milk-msg-out" : "";
|
||||
|
||||
String geoPosition = "";
|
||||
if (message.geo != null) {
|
||||
var coordinates = message.geo.optJSONObject("coordinates");
|
||||
if (coordinates != null) {
|
||||
var latitude = coordinates.optDouble("latitude");
|
||||
var longitude = coordinates.optDouble("longitude");
|
||||
var ll = longitude + "," + latitude;
|
||||
geoPosition = "<a href=\"https://yandex.ru/maps/?ll=" + ll + "&mode=search&sll=" + ll + "&text=" + latitude + "," + longitude + "&z=12\">" +
|
||||
"<img src=\"https://static-maps.yandex.ru/1.x/?ll=" + longitude + "," + latitude + "&z=12&l=map&size=300,200&pt=" + ll + ",vkgrm\"/>" +
|
||||
"</a>";
|
||||
}
|
||||
}
|
||||
|
||||
String chatAction = (message.chat_action != null) ? getHtmlForChatAction(message.chat_action) : "";
|
||||
String attaches = (message.attachments.size() > 0) ? "<p class=\"msg-attaches\">" + getString("attachments") + ": " + getHtmlForAttach(message.attachments) + "</p>" : "";
|
||||
String reply = (message.reply_message != null) ? provideReply(message.reply_message) : "";
|
||||
String fwdMessages = (message.fwd_messages.size() > 0) ? provideForwardMessages(message.fwd_messages) : "";
|
||||
String userLink = (user.isGroup ? "club" : "id") + user.id;
|
||||
|
||||
return "<div class=\"vtex-milk-msg" + isOut + "\" id=\"" + message.id + "\">" +
|
||||
"<p class=\"msg-from\">" + getString("from_lower") + " <a href=\"https://" + VK_DOMAIN + "/" + userLink + "\">" + user.firstName + " " + user.lastName + "</a> <span class=\"msg-from-date\">" + formatTime(message.date) + "</span></p>" + // TODO photo100
|
||||
"<p class=\"msg-body\">" + mentionsReplace(message.text) + "</p>" +
|
||||
geoPosition +
|
||||
chatAction +
|
||||
attaches +
|
||||
reply +
|
||||
fwdMessages +
|
||||
"</div>";
|
||||
}
|
||||
|
||||
private String provideReply(JSONObject replyMessage) {
|
||||
var messageId = replyMessage.optInt("id");
|
||||
var userId = replyMessage.optInt("from_id");
|
||||
var peerId = replyMessage.optInt("peer_id");
|
||||
var text = mentionsReplace(decryptMessage(replyMessage.optString("text"), peerId));
|
||||
var fwdMessages = replyMessage.optJSONArray("fwd_messages");
|
||||
|
||||
var user = usersArray.get(userId);
|
||||
|
||||
if (text.isEmpty()) {
|
||||
if (fwdMessages != null) {
|
||||
var length = fwdMessages.length();
|
||||
if (length > 0) text = length + " " + pluralMsg(length);
|
||||
} else text = getString("attachment"); // Вложение
|
||||
}
|
||||
|
||||
return "<a href=\"#" + messageId + "\" class=\"msg-attach-link\">" +
|
||||
"<div class=\"msg-reply\">" +
|
||||
"<p class=\"reply-header\">" + getString("vkim_msg_fwd_single") + "</p>" +
|
||||
"<p class=\"reply-content\">" + user.firstName + " " + user.lastName + ": " + text + "</p>" +
|
||||
"</div>" +
|
||||
"</a>";
|
||||
}
|
||||
|
||||
private String provideForwardMessages(List<JSONObject> fwd_messages) throws JSONException {
|
||||
StringBuilder fwdmsgs = new StringBuilder("<div class=\"msg-reply\">");
|
||||
|
||||
for (JSONObject fwd_message : fwd_messages) {
|
||||
|
||||
if (fwd_message == null) continue;
|
||||
|
||||
List<JSONObject> attaches = new ArrayList<>();
|
||||
|
||||
JSONArray arr = fwd_message.optJSONArray("attachments");
|
||||
if (arr != null) {
|
||||
for (int i = 0; i < arr.length(); i++) {
|
||||
try {
|
||||
attaches.add(arr.getJSONObject(i));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<JSONObject> fwdsMsg = new ArrayList<>();
|
||||
|
||||
JSONArray fwd_msgs = fwd_message.optJSONArray("fwd_messages");
|
||||
if (fwd_msgs != null) {
|
||||
for (int i = 0; i < fwd_msgs.length(); i++) {
|
||||
try {
|
||||
fwdsMsg.add(fwd_msgs.getJSONObject(i));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject reply_message = fwd_message.optJSONObject("reply_message");
|
||||
|
||||
var attachesHtml = (attaches.size() > 0) ? getString("attachments") + ": " + getHtmlForAttach(attaches) : "";
|
||||
var replyHtml = (reply_message != null) ? provideReply(reply_message) : "";
|
||||
var fwdsHtml = (fwdsMsg.size() > 0) ? provideForwardMessages(fwdsMsg) : ""; // recursion XD :>
|
||||
|
||||
var userId = fwd_message.optInt("from_id");
|
||||
var peerId = fwd_message.optInt("peer_id");
|
||||
var text = mentionsReplace(decryptMessage(fwd_message.optString("text"), peerId));
|
||||
|
||||
var userLink = ((userId < 0) ? "club" : "id") + Math.abs(userId);
|
||||
|
||||
fwdmsgs.append("<p class=\"reply-content\"><a class=\"msg-attach-link\" href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\">@").append(userLink).append("</a>: ").append(text).append(attachesHtml).append(replyHtml).append(fwdsHtml);
|
||||
}
|
||||
|
||||
return fwdmsgs.append("</div>").toString();
|
||||
}
|
||||
|
||||
private String mentionsReplace(String text) {
|
||||
var matcher = Pattern.compile("\\[((?:club|id|public)\\d+)\\|([^]]+)\\]").matcher(text);
|
||||
|
||||
return matcher.replaceAll("<a class=\"msg-attach-link\" href=\"https://" + VK_DOMAIN + "/$1\">$2</a>");
|
||||
}
|
||||
|
||||
private String pluralMsg(int count) {
|
||||
String[] plurals = getArray("msg_plurals");
|
||||
|
||||
var hundredModulo = count % 100;
|
||||
var tenModulo = count % 10;
|
||||
|
||||
if (hundredModulo > 10 && hundredModulo < 20) return plurals[2];
|
||||
else if (tenModulo > 1 && tenModulo < 5) return plurals[1];
|
||||
else if (tenModulo == 1) return plurals[0];
|
||||
|
||||
return plurals[2];
|
||||
}
|
||||
|
||||
private String getHtmlForChatAction(JSONObject action) {
|
||||
var type = action.optString("type");
|
||||
|
||||
StringBuilder htmlAction = new StringBuilder("<p class=\"chat-action\">");
|
||||
|
||||
switch(type) {
|
||||
case "chat_photo_update":
|
||||
htmlAction.append(getString("chat_export_avatar_update"));
|
||||
break;
|
||||
case "chat_photo_remove":
|
||||
htmlAction.append(getString("chat_export_avatar_remove"));
|
||||
break;
|
||||
case "chat_create":
|
||||
var name = action.optString("text");
|
||||
htmlAction.append(getString("chat_export_chat_created")).append((!name.isEmpty()) ? " \"" + name + "\"" : "");
|
||||
break;
|
||||
case "chat_title_update":
|
||||
var newName = action.optString("text");
|
||||
htmlAction.append(getString("chat_export_name_changed")).append((!newName.isEmpty()) ? (" " + getString("chat_export_to") + " \"" + newName + "\"") : "");
|
||||
break;
|
||||
case "chat_invite_user":
|
||||
var newUserId = action.optInt("member_id");
|
||||
htmlAction.append(getString("chat_export_user_added") + " ").append((newUserId > 0) ? getString("chat_export_type_user") : getString("chat_export_type_group"));
|
||||
if (newUserId != 0) {
|
||||
var userLink = ((newUserId < 0) ? "club" : "id") + Math.abs(newUserId);
|
||||
htmlAction.append(" <a href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\" class=\"msg-attach-link\">@").append(userLink).append("</a>");
|
||||
}
|
||||
break;
|
||||
case "chat_kick_user":
|
||||
var removedUserId = action.optInt("member_id");
|
||||
htmlAction.append(getString("chat_export_user_kicked") + " ").append((removedUserId > 0) ? getString("chat_export_type_user") : getString("chat_export_type_group"));
|
||||
if (removedUserId != 0) {
|
||||
var userLink = ((removedUserId < 0) ? "club" : "id") + Math.abs(removedUserId);
|
||||
htmlAction.append(" <a href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\" class=\"msg-attach-link\">@").append(userLink).append("</a>");
|
||||
}
|
||||
break;
|
||||
case "chat_pin_message":
|
||||
var message = action.optString("message");
|
||||
htmlAction.append(getString("chat_export_msg_pinned")).append((!message.isEmpty()) ? (" \"" + message + "\"") : "");
|
||||
break;
|
||||
case "chat_unpin_message":
|
||||
htmlAction.append(getString("chat_export_msg_unpinned"));
|
||||
break;
|
||||
case "chat_invite_user_by_link":
|
||||
htmlAction.append(getString("chat_export_join_by_link"));
|
||||
break;
|
||||
case "chat_screenshot":
|
||||
htmlAction.append(getString("chat_export_chat_screenshot"));
|
||||
break;
|
||||
case "custom":
|
||||
htmlAction.append(action.optString("message"));
|
||||
break;
|
||||
default:
|
||||
htmlAction.append(getString("vkim_msg_unsupported") + " (").append(type).append(")");
|
||||
break;
|
||||
}
|
||||
|
||||
return htmlAction.append("</p>").toString();
|
||||
}
|
||||
|
||||
private String getHtmlForAttach(List<JSONObject> attaches) throws JSONException{
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
for (JSONObject attach : attaches) {
|
||||
String type = attach.getString("type");
|
||||
JSONObject item = attach.optJSONObject(type);
|
||||
|
||||
if (item == null) continue;
|
||||
|
||||
switch(type) { // market_album is not supported
|
||||
case "photo":
|
||||
var sizes = item.getJSONArray("sizes");
|
||||
rs.append("<a href=\"").append(sizes.getJSONObject(sizes.length() - 1).getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_photo")).append("</a>");
|
||||
break;
|
||||
case "doc":
|
||||
rs.append("<a href=\"").append(item.getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_doc")).append("</a>");
|
||||
break;
|
||||
case "link":
|
||||
rs.append("<a href=\"").append(item.getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_link")).append("</a>");
|
||||
break;
|
||||
case "wall":
|
||||
rs.append("<a href=\"https://" + VK_DOMAIN + "/wall").append(item.optString("to_id", item.optString("owner_id"))).append("_").append(item.getString("id")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_wallpost")).append("</a>");
|
||||
break;
|
||||
case "sticker":
|
||||
rs.append("<img style=\"width:128px;height:128px;\" src=\"").append(item.getJSONArray("images").getJSONObject(1).getString("url")).append("\"/>");
|
||||
break;
|
||||
case "audio_message":
|
||||
rs.append("<a href=\"").append(item.optString("link_mp3")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_audiomsg")).append("</a>");
|
||||
break;
|
||||
case "audio":
|
||||
rs.append(getString("chat_export_type_audio")).append(" (").append(item.optString("artist")).append(" — ").append(item.optString("title")).append(")");
|
||||
break;
|
||||
case "video":
|
||||
rs.append(getString("chat_export_type_video")).append(" (").append(getVideoHtml(item.optJSONObject("files"))).append(")");
|
||||
break;
|
||||
case "wall_reply":
|
||||
var link = "https://" + VK_DOMAIN + "/wall" + item.optString("owner_id") + "_" + item.optString("post_id") + "?reply=" + item.optString("id");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(link).append("\">").append(getString("chat_export_type_wallreply")).append("</a>");
|
||||
break;
|
||||
case "story":
|
||||
var story_link = "https://" + VK_DOMAIN + "/story" + item.optString("owner_id") + "_" + item.optString("id");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(story_link).append("\">").append(getString("chat_export_type_story")).append("</a>");
|
||||
break;
|
||||
case "podcast":
|
||||
var podcast_link = "https://" + VK_DOMAIN + "/podcast" + item.optString("owner_id") + "_" + item.optString("id");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(podcast_link).append("\">").append(getString("chat_export_type_podcast")).append("</a>");
|
||||
break;
|
||||
case "gift":
|
||||
rs.append("<img src=\"").append(item.optString("thumb_96")).append("\"/>");
|
||||
break;
|
||||
case "mini_app":
|
||||
var app = item.optJSONObject("app");
|
||||
|
||||
if (app != null) {
|
||||
var miniapp_link = "https://" + VK_DOMAIN + "/app" + app.optString("id");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(miniapp_link).append("\">").append(getString("chat_export_type_miniapp")).append("</a>");
|
||||
break;
|
||||
}
|
||||
|
||||
rs.append(getString("chat_export_error"));
|
||||
break;
|
||||
case "audio_playlist":
|
||||
var playlist_link = "https://" + VK_DOMAIN + "/music/album/" + item.optString("owner_id") + "_" + item.optString("id") + "_" + item.optString("access_key");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(playlist_link).append("\">").append(getString("chat_export_type_playlist")).append(" (").append(item.optString("title")).append(")</a>");
|
||||
break;
|
||||
case "poll":
|
||||
rs.append(getString(item.optBoolean("anonymous") ? "chat_export_poll_anon" : "chat_export_poll_publ")).append(" ").append(getString("chat_export_type_poll")).append(" ").append(getPollHtml(item));
|
||||
break;
|
||||
case "market":
|
||||
var market_item_link = "https://" + VK_DOMAIN + "/market" + item.optString("owner_id") + "_" + item.optString("id");
|
||||
rs.append("<a class=\"msg-attach-link\" href=\"").append(market_item_link).append("\">").append(getString("chat_export_type_market")).append("</a>");
|
||||
break;
|
||||
default:
|
||||
rs.append(getString("vkim_msg_unsupported")).append(" (").append(type).append(")");
|
||||
break;
|
||||
}
|
||||
|
||||
rs.append(" ");
|
||||
}
|
||||
|
||||
return rs.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPollHtml(JSONObject poll) throws JSONException {
|
||||
StringBuilder pollHtml = new StringBuilder(
|
||||
"<div class=\"vtex-milk-msg poll\">" +
|
||||
"<p style=\"text-align:center;\">" + poll.optString("question") + "</p>"
|
||||
);
|
||||
|
||||
var answers = poll.optJSONArray("answers");
|
||||
|
||||
for (int i = 0; i < answers.length(); i++) {
|
||||
var jsonObject = answers.optJSONObject(i);
|
||||
if (jsonObject != null)
|
||||
pollHtml.append("<hr><p>").append(jsonObject.optString("text")).append(" · ").append(jsonObject.optString("votes")).append(" (").append(jsonObject.optString("rate")).append("%)</p>");
|
||||
}
|
||||
|
||||
pollHtml.append("<hr><p style=\"text-align:center;\">").append(getString("chat_export_poll_voted")).append(" ").append(poll.optString("votes")).append(" ").append(getString("chat_export_users_name")).append("</p>");
|
||||
|
||||
return pollHtml.append("</div>").toString();
|
||||
}
|
||||
|
||||
private static String getVideoHtml(JSONObject files) throws JSONException {
|
||||
if (files == null) return getString("chat_export_video_nolinks");
|
||||
|
||||
Iterator< ? > keys = files.keys();
|
||||
StringBuilder videoHtml = new StringBuilder();
|
||||
|
||||
int qualityIndex = 0;
|
||||
String[] quality = {"144", "240", "360", "480", "720", "1080", "1440", "2160"};
|
||||
|
||||
while (keys.hasNext()) {
|
||||
String key = (String) keys.next();
|
||||
if (key.equals("mp4_" + quality[qualityIndex])) {
|
||||
videoHtml.append("<a class=\"msg-attach-link\" href=\"").append(files.get(key)).append("\">").append(quality[qualityIndex]).append("p</a>").append(" ");
|
||||
|
||||
if (qualityIndex == quality.length - 1) break;
|
||||
++qualityIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return videoHtml.toString();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import b.h.g.m.FileUtils;
|
||||
@@ -46,7 +48,7 @@ public class OTADownloader {
|
||||
maxIndex = Math.max(maxIndex, Integer.parseInt(name.replaceFirst(".+-(\\d+).+", "$1")));
|
||||
}
|
||||
File apk = maxIndex == -1 ? new File(downloadDir, "VTLite.apk") : new File(downloadDir, "VTLite-" + maxIndex + ".apk");
|
||||
APKInstallActivity.installOta(context, FileUtils.n(apk));
|
||||
APKInstallActivity.installOta(context, FileUtils.uriFromFile(apk));
|
||||
} else if (status == DownloadManager.STATUS_FAILED) {
|
||||
AndroidUtils.sendToast(getString("downloaderr"));
|
||||
}
|
||||
@@ -56,16 +58,31 @@ public class OTADownloader {
|
||||
|
||||
public static void downloadBuild(String url) {
|
||||
var context = AndroidUtils.getGlobalContext();
|
||||
var uri = Uri.parse(url);
|
||||
try {
|
||||
var uri = Uri.parse(url);
|
||||
|
||||
context.registerReceiver(callback, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||
context.registerReceiver(callback, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||
|
||||
var request = new DownloadManager.Request(uri);
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setTitle("VTLite.apk");
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "VTLite.apk");
|
||||
var request = new DownloadManager.Request(uri);
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setTitle("VTLite.apk");
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "VTLite.apk");
|
||||
|
||||
var manager = (DownloadManager) AndroidUtils.getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
manager.enqueue(request);
|
||||
var manager = (DownloadManager) AndroidUtils.getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
manager.enqueue(request);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
VkAlertDialog.Builder alertDialog = new VkAlertDialog.Builder(context);
|
||||
alertDialog.setTitle(AndroidUtils.getString("update_error"));
|
||||
alertDialog.setMessage(AndroidUtils.getString("update_error_text"));
|
||||
alertDialog.setPositiveButton(AndroidUtils.getString("no"), (dialog, which) -> {
|
||||
dialog.cancel();
|
||||
});
|
||||
alertDialog.setNeutralButton(AndroidUtils.getString("yes"), (dialog, which) -> {
|
||||
context.startActivity(new Intent("android.intent.action.VIEW").setData(Uri.parse("https://github.com/vtosters/lite/releases/latest/download/VTLite.apk")));
|
||||
});
|
||||
var alert = alertDialog.create();
|
||||
alert.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@ public class StoryDownloader {
|
||||
String url = imageSizes.get(imageSizes.size() - 1).url;
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setTitle("photo" + story.E.a);
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DCIM, "/Stories/photo" + story.E.a + ".jpg");
|
||||
request.setTitle("photo" + story.E.a + ".jpg");
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, "photo" + story.E.a + ".jpg");
|
||||
request.allowScanningByMediaScanner();
|
||||
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package ru.vtosters.lite.downloaders;
|
||||
|
||||
import static ru.vtosters.lite.net.Request.makeRequest;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
@@ -14,6 +14,7 @@ import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vk.core.dialogs.bottomsheet.MenuBottomSheetAction;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.common.VideoFile;
|
||||
@@ -27,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.ExternalLinkParser;
|
||||
|
||||
public class VideoDownloader {
|
||||
@@ -104,17 +106,19 @@ public class VideoDownloader {
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setTitle(videoFile.toString());
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
|
||||
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new AlertDialog.Builder(context);
|
||||
var builder = new VkAlertDialog.Builder(context);
|
||||
builder.setItems(max, ((dialog, which) -> {
|
||||
var url = urls.get(which);
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
request.setTitle(videoFile.toString());
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
|
||||
((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
}));
|
||||
@@ -122,13 +126,13 @@ public class VideoDownloader {
|
||||
}
|
||||
|
||||
public static void parseVideoLink(String url, Context ctx) {
|
||||
if (url.contains("vk.com/story")) {
|
||||
ToastUtils.a("Не поддерживается загрузка историй");
|
||||
if (url.contains("vk.com/story") || url.contains("vk.com/story")) {
|
||||
ToastUtils.a(AndroidUtils.getString("video_dl_stories_not_supported"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!url.contains("vk.com/video")) {
|
||||
ToastUtils.a("Неверная ссылка");
|
||||
if (!url.contains("vk.com/video") || !url.contains("vk.com/video")) {
|
||||
ToastUtils.a(AndroidUtils.getString("video_dl_wrong_link"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,10 +140,10 @@ public class VideoDownloader {
|
||||
String ownerId = videoId.split("_")[0];
|
||||
|
||||
final ProgressDialog progressDialog = new ProgressDialog(ctx);
|
||||
progressDialog.setMessage("Обработка видео");
|
||||
progressDialog.setMessage(AndroidUtils.getString("video_dl_progress"));
|
||||
progressDialog.show();
|
||||
|
||||
makeRequest("https://" + "api.vk.com" + "/method/video.get?owner_id=" + ownerId + "&videos=" + videoId + "&v=5.99&access_token=" + getUserToken(),
|
||||
makeRequest("https://" + getApi() + "/method/video.get?owner_id=" + ownerId + "&videos=" + videoId + "&v=5.99&access_token=" + getUserToken(),
|
||||
response -> {
|
||||
progressDialog.cancel();
|
||||
|
||||
@@ -152,7 +156,7 @@ public class VideoDownloader {
|
||||
downloadVideo(videoFile, ctx);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(ctx, "Видео нельзя скачать", Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(ctx, AndroidUtils.getString("video_dl_error"), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+29
-2
@@ -1,5 +1,7 @@
|
||||
package ru.vtosters.lite.downloaders.notifications;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
@@ -12,12 +14,21 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class NotificationChannels {
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_ID = "music_download_channel";
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_NAME = "Загрузка музыки";
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = "Прогресс загрузки музыки";
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_NAME = getString("music_downloading");
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = getString("music_downloading_progress");
|
||||
|
||||
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID = "music_playlist_download_channel";
|
||||
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_NAME = getString("playlist_downloading");
|
||||
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_DESCRIPTION = getString("playlist_downloading_progress");
|
||||
|
||||
public static final String MUSIC_CACHING_NAME = getString("caching_track_or_pic");
|
||||
|
||||
private static NotificationManagerCompat notificationManagerCompat;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void createChannels() {
|
||||
createMusicDownloadChannel();
|
||||
createMusicPlaylistDownloadChannel();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@@ -29,4 +40,20 @@ public class NotificationChannels {
|
||||
var notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
notificationManager.createNotificationChannel(musicDownloadChannel);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void createMusicPlaylistDownloadChannel() {
|
||||
var musicDownloadChannel = new NotificationChannel(MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID, MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
|
||||
musicDownloadChannel.setDescription(MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_DESCRIPTION);
|
||||
musicDownloadChannel.setShowBadge(true);
|
||||
musicDownloadChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||
var notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
notificationManager.createNotificationChannel(musicDownloadChannel);
|
||||
}
|
||||
|
||||
public static NotificationManagerCompat getNotificationManager() {
|
||||
if (notificationManagerCompat == null)
|
||||
notificationManagerCompat = NotificationManagerCompat.from(AndroidUtils.getGlobalContext());
|
||||
return notificationManagerCompat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package ru.vtosters.lite.encryption;
|
||||
|
||||
import static ru.vtosters.lite.hooks.MessagesHook.injectOwnText;
|
||||
import static ru.vtosters.lite.hooks.MessagesHook.injectOwnTextAll;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.vk.im.engine.models.messages.Msg;
|
||||
import com.vk.im.engine.models.messages.MsgFromUser;
|
||||
import com.vk.im.ui.components.msg_list.MsgListComponent;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -48,6 +52,48 @@ public class EncryptProvider {
|
||||
public static int getPeerId(Msg msg) {
|
||||
return msg.v1();
|
||||
}
|
||||
|
||||
// For message editing
|
||||
public static String decryptMessage(MsgFromUser msg, boolean showEmoji) {
|
||||
var decryptedMessage = decryptMessage(getBody(msg), getPeerId(msg));
|
||||
if (!showEmoji && !decryptedMessage.equals(injectOwnTextAll(getBody(msg)))) decryptedMessage = decryptedMessage.replaceAll("^\uD83D\uDD12 ", "");
|
||||
return decryptedMessage;
|
||||
}
|
||||
|
||||
public static String decryptMessage(String msg, MsgListComponent list) {
|
||||
var peerid = list.G().getId();
|
||||
var decryptedMessage = decryptMessage(msg, peerid);
|
||||
if (!decryptedMessage.equals(injectOwnTextAll(msg))) decryptedMessage = decryptedMessage.replaceAll("^\uD83D\uDD12 ", "");
|
||||
return decryptedMessage;
|
||||
}
|
||||
|
||||
public static CharSequence decryptMessage(CharSequence msg, int peerid) {
|
||||
return decryptMessage(msg.toString(), peerid);
|
||||
}
|
||||
|
||||
public static CharSequence decryptMessage(Msg msg, CharSequence orig) {
|
||||
if (msg instanceof MsgFromUser) {
|
||||
var decrypted = decryptMessage(((MsgFromUser) msg).f(), msg.v1());
|
||||
|
||||
if (decrypted.contains("\uD83D\uDD12")) {
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return orig;
|
||||
}
|
||||
|
||||
public static String decryptMessages(String msgBody, int peer, boolean showEmoji) {
|
||||
return (String) decryptMessage(msgBody, peer, showEmoji);
|
||||
}
|
||||
|
||||
// For MentionsFormatter that requires CharSequence
|
||||
public static CharSequence decryptMessage(String msgBody, int peer, boolean showEmoji) {
|
||||
var decryptedMessage = decryptMessage(msgBody, peer);
|
||||
if (!showEmoji && !decryptedMessage.equals(injectOwnTextAll(msgBody))) decryptedMessage = decryptedMessage.replaceAll("^\uD83D\uDD12 ", "");
|
||||
return decryptedMessage;
|
||||
}
|
||||
|
||||
// This will run through EVERY single processor available.
|
||||
public static String decryptMessage(MsgFromUser msg) {
|
||||
@@ -58,12 +104,13 @@ public class EncryptProvider {
|
||||
try {
|
||||
for (IMProcessor processor : processors) {
|
||||
if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || getKeyForProcessor(processor, peer) != null))
|
||||
return "\uD83D\uDD12 " + processor.decode(msgBody, getKeyForProcessor(processor, peer));
|
||||
return "\uD83D\uDD12 " + injectOwnTextAll(processor.decode(msgBody, getKeyForProcessor(processor, peer)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return msgBody;
|
||||
|
||||
return injectOwnTextAll(msgBody);
|
||||
}
|
||||
|
||||
private static byte[] getKeyForProcessor(IMProcessor processor, int peer) {
|
||||
@@ -84,9 +131,8 @@ public class EncryptProvider {
|
||||
return keyString.getBytes();
|
||||
}
|
||||
|
||||
// This will use only the processor which was chosen in chat menu [that's why ID is needed, bruh]
|
||||
// This will use only the processor which was chosen in chat menu
|
||||
public static String encryptMessage(MsgFromUser msg) {
|
||||
|
||||
String msgBody = getBody(msg);
|
||||
|
||||
Log.d("EncryptProvider", "encryptMessage: msg = " + msg);
|
||||
@@ -96,11 +142,11 @@ public class EncryptProvider {
|
||||
for (IMProcessor processor : processors) {
|
||||
int peer = getPeerId(msg);
|
||||
if (processor.isUsedToEncrypt(peer)) {
|
||||
return processor.encode(msgBody, getKeyForProcessor(processor, peer));
|
||||
return injectOwnText(processor.encode(msgBody, getKeyForProcessor(processor, peer)));
|
||||
}
|
||||
}
|
||||
|
||||
return msgBody;
|
||||
return injectOwnText(msgBody);
|
||||
}
|
||||
|
||||
// For rendering UI
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.vtosters.lite.feature.discover;
|
||||
|
||||
import com.vk.dto.discover.DiscoverItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DiscoverTemplates {
|
||||
public static ArrayList<String> getTemplates() {
|
||||
var templates = DiscoverItem.Template.values();
|
||||
var arrayList = new ArrayList<String>();
|
||||
|
||||
for (var template : templates) {
|
||||
if (template != DiscoverItem.Template.GRID_MEDIA) {
|
||||
arrayList.add(template.b());
|
||||
}
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.vtosters.lite.feature.music;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.vk.music.fragment.MusicFragment;
|
||||
|
||||
public class MusicArtistSelectorHook {
|
||||
public static void navigateToSearch(Context context, String artist) {
|
||||
var fr = new MusicFragment.f();
|
||||
fr.a(artist);
|
||||
|
||||
// navigate
|
||||
fr.a(context);
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,29 @@ package ru.vtosters.lite.foaf;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static android.widget.Toast.makeText;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isAnyProxyEnabled;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.apiproxy;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.getApi;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vk.core.network.Network;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -35,6 +38,7 @@ public class FoafBase {
|
||||
private static final Pattern FOAF_REGEX = Pattern.compile("<ya:created dc:date=\"(.+?)\"\\/>");
|
||||
private static final Pattern FOAF_REGEX_LAST_SEEN = Pattern.compile("<ya:lastLoggedIn dc:date=\"(.*)(((\\+|-)\\d\\d):(\\d\\d))\"\\/>");
|
||||
private static final Pattern FOAF_REGEX_LOGIN = Pattern.compile("<ya:created dc:date=\"(.+?)\"\\/>");
|
||||
private static final String API_VKNEXT = "https://api.vtosters.app/v1/getBypassedOnlineInfo?json=1&ids=";
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public static long getLastSeen(long origtime, int id) throws ParseException, IOException {
|
||||
@@ -58,15 +62,57 @@ public class FoafBase {
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
|
||||
Date date = sdf.parse(matcher.group(1));
|
||||
Date date = sdf.parse(Objects.requireNonNull(matcher.group(1)));
|
||||
sdf.setTimeZone(TimeZone.getDefault());
|
||||
return sdf.parse(sdf.format(date)).getTime() / 1000;
|
||||
}
|
||||
|
||||
|
||||
public static JSONObject getBypassedOnlineInfo(int id) throws JSONException {
|
||||
var request = new okhttp3.Request.a()
|
||||
.b(API_VKNEXT + Integer.toString(id))
|
||||
.a();
|
||||
|
||||
String response = null;
|
||||
|
||||
var dummy = new JSONObject();
|
||||
dummy.put("last_seen", 0);
|
||||
|
||||
try {
|
||||
response = client.a(request).execute().a().g();
|
||||
var online_info = new JSONObject(response).optJSONObject("response").optJSONObject(Integer.toString(id));
|
||||
return online_info == null ? dummy : online_info;
|
||||
} catch (Exception e) {
|
||||
Log.e("GetBypassedOnlineInfo", e.getMessage());
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject getBypassedOnlineInfo(String ids) throws JSONException {
|
||||
|
||||
|
||||
var request = new okhttp3.Request.a()
|
||||
.b(API_VKNEXT + ids)
|
||||
.a();
|
||||
|
||||
String response = null;
|
||||
|
||||
var dummy = new JSONObject();
|
||||
|
||||
try {
|
||||
response = client.a(request).execute().a().g();
|
||||
var online_info = new JSONObject(response).optJSONObject("response");
|
||||
return online_info == null ? dummy : online_info;
|
||||
} catch (Exception e) {
|
||||
Log.e("GetBypassedOnlineInfo1", e.getMessage());
|
||||
return dummy;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getLink(int i) {
|
||||
String str;
|
||||
StringBuilder sb;
|
||||
if (apiproxy()) {
|
||||
if (isAnyProxyEnabled()) {
|
||||
sb = new StringBuilder();
|
||||
str = "https://" + getApi() + "/_/vk.com/foaf.php?id=";
|
||||
} else {
|
||||
@@ -95,8 +141,8 @@ public class FoafBase {
|
||||
Matcher matcher2 = FOAF_REGEX_LOGIN.matcher(str);
|
||||
matcher.find();
|
||||
matcher2.find();
|
||||
String normalHumanDate = getNormalHumanDate(matcher.group(1));
|
||||
getNormalHumanDate(matcher2.group(1));
|
||||
String normalHumanDate = getNormalHumanDate(Objects.requireNonNull(matcher.group(1)));
|
||||
getNormalHumanDate(Objects.requireNonNull(matcher2.group(1)));
|
||||
VkAlertDialog.Builder a2 = new VkAlertDialog.Builder(context).setTitle(getIdentifier("addinf", "string"));
|
||||
a2.setMessage(getString("foafid") + " " + i + getString("foafregdate") + " " + normalHumanDate + getString("foafdate") + " " + daysPassedFromFoafDate(normalHumanDate)).setPositiveButton(17039370, null).create().show();
|
||||
} catch (Exception e) {
|
||||
@@ -105,8 +151,9 @@ public class FoafBase {
|
||||
}
|
||||
}
|
||||
|
||||
private static long daysPassedFromFoafDate(String str) {
|
||||
private static long daysPassedFromFoafDate(String str) {
|
||||
try {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
Date parse = new SimpleDateFormat("yyyy-MM-dd").parse(str.split(" ")[1]);
|
||||
return TimeUnit.DAYS.convert(new Date().getTime() - parse.getTime(), TimeUnit.MILLISECONDS);
|
||||
} catch (ParseException e) {
|
||||
@@ -116,7 +163,7 @@ public class FoafBase {
|
||||
}
|
||||
|
||||
private static String getNormalHumanDate(String str) {
|
||||
String[] split = str.split(ExifInterface.GPS_DIRECTION_TRUE);
|
||||
String[] split = str.split("T");
|
||||
return split[1].split("\\+")[0] + " " + split[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCaption;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCopyright;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isBadNews;
|
||||
import static ru.vtosters.lite.utils.Preferences.ads;
|
||||
import static ru.vtosters.lite.utils.Preferences.adsgroup;
|
||||
import static ru.vtosters.lite.utils.Preferences.adsstories;
|
||||
import static ru.vtosters.lite.utils.Preferences.authorsrecomm;
|
||||
import static ru.vtosters.lite.utils.Preferences.dev;
|
||||
import static ru.vtosters.lite.utils.Preferences.friendsrecomm;
|
||||
import static ru.vtosters.lite.utils.Preferences.postsrecomm;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.NewsFeedFiltersUtils;
|
||||
|
||||
public class AdBlockHook {
|
||||
public static Boolean discoverAdBlock(JSONObject jsonObject) {
|
||||
if (jsonObject == null) return true;
|
||||
|
||||
try {
|
||||
var type = jsonObject.optString("type");
|
||||
var post = jsonObject.optJSONObject("post");
|
||||
|
||||
if (jsonObject.optString("template").contains("ads") || jsonObject.has("ads")) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + (post != null ? post.optInt("post_id") : 0) + " from discover, Reason: ads");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isAds(post, type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authorsrecomm() && (type.equals("authors_rec")
|
||||
|| type.startsWith("recommended_") && (type.endsWith("audios")
|
||||
|| type.endsWith("artists")
|
||||
|| type.endsWith("playlists")
|
||||
|| type.endsWith("groups")))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + (post != null ? post.optInt("post_id") : 0) + " from discover, Reason: authorsrecomm");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (postsrecomm() && (type.equals("inline_user_rec") || type.equals("live_recommended"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + (post != null ? post.optInt("post_id") : 0) + " from discover, Reason: postsrecomm");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (friendsrecomm() && (type.equals("user_rec") || type.equals("friends_recomm"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + (post != null ? post.optInt("post_id") : 0) + " from discover, Reason: friendsrecomm");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adsgroup() && (post != null ? post.optInt("marked_as_ads") : 0) == 1 && !isWhitelistedAd(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: marked_as_ads is true");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (post != null) {
|
||||
if (isBadNews(post.optString("text")) && !isWhitelistedFilters(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: text filters");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkCopyright(post) && !isWhitelistedFilters(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: copyright filters");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkCaption(post) && !isWhitelistedFilters(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: caption filters");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NewsFeedFiltersUtils.injectFiltersReposts(post) && !isWhitelistedFilters(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: repost ad");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d("NewsfeedAdBlockV2", "discover: " + e.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static JSONObject discoverInject(JSONObject json) throws JSONException {
|
||||
var items = json.optJSONArray("items");
|
||||
var newObj = new JSONArray();
|
||||
|
||||
// newObj.put(discoverObj()); // template injection
|
||||
|
||||
for (int i = 0; i < (items != null ? items.length() : 0); i++) {
|
||||
try {
|
||||
var curr = items.getJSONObject(i);
|
||||
if (!curr.optString("template").contains("info") && discoverAdBlock(curr)) {
|
||||
newObj.put(items.optJSONObject(i));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d("NewsfeedAdBlockV2", "discover: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
json.put("items", newObj);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject discoverObj() throws JSONException {
|
||||
var mainjson = new JSONObject();
|
||||
|
||||
var info = new JSONObject();
|
||||
info.put("title", "Test Injection");
|
||||
info.put("text_color", "#ffffff");
|
||||
info.put("description", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat");
|
||||
|
||||
var image = new JSONArray();
|
||||
var imageQuality1 = new JSONObject();
|
||||
imageQuality1.put("url", "https://w7.pngwing.com/pngs/505/1021/png-transparent-konata-izumi-lucky-star-desktop-anime-anime-blue-mammal-face.png");
|
||||
imageQuality1.put("width", 72); // 72, 108, 144, 216, 288
|
||||
imageQuality1.put("height", 72); // 72, 108, 144, 216, 288
|
||||
image.put(imageQuality1);
|
||||
|
||||
var background = new JSONArray();
|
||||
var backgroundQuality1 = new JSONObject();
|
||||
backgroundQuality1.put("url", "https://2ch.life/vg/arch/2022-08-22/src/38524739/16440606579870.jpg");
|
||||
backgroundQuality1.put("width", 344); // 344, 516, 680, 1032, 1376
|
||||
backgroundQuality1.put("height", 215); // 215, 323, 426, 645, 860
|
||||
background.put(backgroundQuality1);
|
||||
|
||||
info.put("image", image);
|
||||
info.put("background", background);
|
||||
|
||||
mainjson.put("info", info);
|
||||
mainjson.put("track_code", "info_banner_donut_catalog");
|
||||
mainjson.put("template", "info");
|
||||
|
||||
var action = new JSONObject();
|
||||
action.put("type", "open_url");
|
||||
action.put("url", "vk.com/vtosters_official");
|
||||
action.put("target", "internal");
|
||||
|
||||
mainjson.put("action", action);
|
||||
|
||||
return mainjson;
|
||||
}
|
||||
|
||||
public static JSONArray feedInject(JSONArray items) {
|
||||
if (items.length() == 0) return items;
|
||||
|
||||
try {
|
||||
var newItems = new JSONArray();
|
||||
|
||||
for (int j = 0; j < items.length(); j++) {
|
||||
var list = items.optJSONObject(j);
|
||||
|
||||
if (list == null) continue;
|
||||
|
||||
var type = list.optString("type");
|
||||
|
||||
if (isAds(list, type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (authorsrecomm() && (type.equals("authors_rec")
|
||||
|| type.startsWith("recommended_") && (type.endsWith("audios")
|
||||
|| type.endsWith("artists")
|
||||
|| type.endsWith("playlists")
|
||||
|| type.endsWith("groups")))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: authorsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (postsrecomm() && (type.equals("inline_user_rec") || type.equals("live_recommended"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: postsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (friendsrecomm() && (type.equals("user_rec") || type.equals("friends_recomm"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: friendsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (adsgroup() && list.optInt("marked_as_ads") == 1 && !isWhitelistedAd(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: marked_as_ads is true");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBadNews(list.optString("text")) && !isWhitelistedFilters(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: text filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCopyright(list) && !isWhitelistedFilters(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: copyright filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCaption(list) && !isWhitelistedFilters(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: caption filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NewsFeedFiltersUtils.injectFiltersReposts(list) && !isWhitelistedFilters(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: repost ad");
|
||||
continue;
|
||||
}
|
||||
|
||||
newItems.put(list);
|
||||
}
|
||||
|
||||
return newItems;
|
||||
} catch (Exception e) {
|
||||
Log.d("NewsfeedAdBlockV2", e.getMessage());
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
public static JSONObject storiesads(JSONObject json, boolean isDeleteFix) throws JSONException {
|
||||
if (!adsstories()) {
|
||||
return json;
|
||||
}
|
||||
|
||||
if (json.has("ads")) {
|
||||
var ad = json.optJSONObject("ads");
|
||||
if (isDeleteFix) {
|
||||
if (ad != null) {
|
||||
ad.optJSONObject("settings")
|
||||
.put("stories_interval", 0)
|
||||
.put("authors_interval", 0)
|
||||
.put("time_interval", 0)
|
||||
.put("stories_init", 0)
|
||||
.put("authors_init", 0)
|
||||
.put("time_init", 0);
|
||||
}
|
||||
if (dev()) Log.d("StoriesAds", "Set ads settings at zero values");
|
||||
} else {
|
||||
json.remove("ads");
|
||||
if (dev()) Log.d("StoriesAds", "Removed ads block");
|
||||
}
|
||||
}
|
||||
|
||||
if (!json.has("items")) return json;
|
||||
|
||||
var items = json.optJSONArray("items");
|
||||
if (items != null) {
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
var item = items.optJSONObject(i);
|
||||
if (item != null) {
|
||||
parseStoriesItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private static void parseStoriesItem(JSONObject item) throws JSONException {
|
||||
var stories = item.optJSONArray("stories");
|
||||
var newStories = new JSONArray();
|
||||
|
||||
if (stories == null) return;
|
||||
|
||||
for (int j = 0; j < stories.length(); j++) {
|
||||
var story = stories.optJSONObject(j);
|
||||
|
||||
if (!story.optBoolean("is_ads") && !story.optBoolean("is_promo")) {
|
||||
newStories.put(story);
|
||||
} else {
|
||||
if (dev())
|
||||
Log.d("StoriesAds", "Fetched ad, owner id " + story.optString("owner_id") + ", caption " + story.optString("caption"));
|
||||
}
|
||||
}
|
||||
|
||||
item.put("stories", newStories);
|
||||
}
|
||||
|
||||
public static Boolean isAds(JSONObject list, String type) throws JSONException {
|
||||
if (list == null || type == null || !ads()) return false;
|
||||
|
||||
if (list.has("ads")
|
||||
|| type.equals("ads")
|
||||
|| type.equals("carousel")
|
||||
|| type.equals("html5_ad")
|
||||
|| type.equals("ads_easy_promote")) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: ads");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.equals("promo_button")
|
||||
|| type.equals("app_widget")
|
||||
|| type.equals("app_video")
|
||||
|| type.equals("app_slider")
|
||||
|| type.equals("tags_suggestions")) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: promo");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Boolean isWhitelistedFilters(JSONObject list) {
|
||||
var id = String.valueOf(list.optInt("owner_id"));
|
||||
|
||||
if (id.equals("0")) {
|
||||
id = String.valueOf(list.optInt("source_id"));
|
||||
}
|
||||
|
||||
if (id.equals("-189659924")){
|
||||
return true;
|
||||
}
|
||||
|
||||
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet("whitelisted_filters_groups", Collections.synchronizedSet(new LinkedHashSet<>()));
|
||||
|
||||
return whitelist.contains(id);
|
||||
}
|
||||
|
||||
public static Boolean isWhitelistedAd(JSONObject list) {
|
||||
var id = String.valueOf(list.optInt("owner_id"));
|
||||
|
||||
if (id.equals("0")) {
|
||||
id = String.valueOf(list.optInt("source_id"));
|
||||
}
|
||||
|
||||
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet("whitelisted_ad_groups", Collections.synchronizedSet(new LinkedHashSet<>()));
|
||||
|
||||
return whitelist.contains(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
import static ru.vtosters.lite.dnr.DNRInjector.forceInvalidateDialogActions;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.dp2px;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
import static ru.vtosters.lite.utils.LifecycleUtils.getCurrentActivity;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getAlertStyle;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getSTextAttr;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.text.Editable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vk.im.engine.models.dialogs.Dialog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import ru.vtosters.lite.encryption.EncryptProvider;
|
||||
import ru.vtosters.lite.encryption.base.IMProcessor;
|
||||
|
||||
public class CryptImHook{
|
||||
public static boolean isPrivateProcessor(int peerID){
|
||||
for (IMProcessor processor : EncryptProvider.processors) {
|
||||
if (processor.isUsedToEncrypt(peerID)) {
|
||||
return !processor.isPublic();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void hook(int peerId, Dialog d){
|
||||
List<IMProcessor> processors = EncryptProvider.processors;
|
||||
|
||||
IMProcessor enabled = null;
|
||||
int enabledPosition = 0;
|
||||
|
||||
String[] titles = new String[processors.size() + 1];
|
||||
titles[0] = getString("encryption_disable");
|
||||
|
||||
for (int i = 0; i < processors.size(); i++) {
|
||||
IMProcessor proc = processors.get(i);
|
||||
|
||||
titles[i + 1] = proc.getUIName();
|
||||
|
||||
if (proc.isUsedToEncrypt(peerId)) {
|
||||
enabled = proc;
|
||||
enabledPosition = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
IMProcessor finalEnabled = enabled;
|
||||
Activity ctx = getCurrentActivity();
|
||||
|
||||
if (ctx == null) {
|
||||
sendToast(getString("app_activities_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
new VkAlertDialog.Builder(ctx)
|
||||
.setTitle(getString("encryption_sett"))
|
||||
.setSingleChoiceItems(titles, enabledPosition, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
if (finalEnabled != null) finalEnabled.disableEncryptFor(peerId);
|
||||
} else {
|
||||
IMProcessor proc = processors.get(which - 1);
|
||||
|
||||
if (proc.isUsedToEncrypt(peerId)) {
|
||||
proc.disableEncryptFor(peerId);
|
||||
} else {
|
||||
proc.enableEncryptFor(peerId);
|
||||
if (finalEnabled != null) finalEnabled.disableEncryptFor(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
forceInvalidateDialogActions(d);
|
||||
}).create().show();
|
||||
}
|
||||
|
||||
public static void hookPref(int peerId){
|
||||
IMProcessor enabled = null;
|
||||
String key = null;
|
||||
|
||||
for (IMProcessor processor : EncryptProvider.processors) {
|
||||
if (processor.isUsedToEncrypt(peerId)) {
|
||||
enabled = processor;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled != null && enabled.isPublic()) {
|
||||
sendToast(getString("encryption_public_algorithm_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Objects.requireNonNull(enabled).getEncryptionKeyFor(peerId) != null) {
|
||||
key = enabled.getEncryptionKeyFor(peerId);
|
||||
}
|
||||
|
||||
if (key.equals("VTAesDefault")) {
|
||||
key = null;
|
||||
}
|
||||
|
||||
Context ctx = getCurrentActivity();
|
||||
|
||||
if (ctx == null) {
|
||||
sendToast(getString("app_activities_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
LinearLayout linearLayout = new LinearLayout(ctx);
|
||||
|
||||
final EditText editText = new EditText(ctx);
|
||||
editText.setHintTextColor(getSTextAttr());
|
||||
editText.setBackgroundTintList(ColorStateList.valueOf(getSTextAttr()));
|
||||
|
||||
linearLayout.addView(editText);
|
||||
editText.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) editText.getLayoutParams());
|
||||
margin.setMargins(dp2px(24f), 0, dp2px(24f), 0);
|
||||
editText.setLayoutParams(margin);
|
||||
editText.setText(key == null ? "" : key);
|
||||
|
||||
IMProcessor finalEnabled = enabled;
|
||||
new AlertDialog.Builder(ctx, getAlertStyle())
|
||||
.setTitle(getString("encryption_enter_key"))
|
||||
.setMessage(String.format(getString("encryption_current_algorithm_title"), enabled.getUIName()))
|
||||
.setView(linearLayout)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
Editable edit = editText.getText();
|
||||
finalEnabled.setEncryptionKeyFor(peerId, edit == null ? null : edit.toString());
|
||||
}).setNegativeButton(android.R.string.cancel, null).create().show();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.MD5;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class DeviceInfoHook{
|
||||
public static String getDeviceInfo(String input) {
|
||||
Log.d("DeviceInfoHook", "getDeviceInfo: " + input);
|
||||
@@ -13,27 +16,30 @@ public class DeviceInfoHook{
|
||||
}
|
||||
|
||||
public static String getDeviceId(String input) {
|
||||
Log.d("DeviceInfoHook", "getDeviceId: " + input);
|
||||
Log.d("DeviceInfoHook", "getDeviceId shuffled: " + shuffle(input));
|
||||
if (getBoolValue("hideDeviceInfo", true)) return shuffle(input);
|
||||
return input;
|
||||
return findDeviceId() + ":" + getDeviceId();
|
||||
}
|
||||
|
||||
public static String shuffle(String string) {
|
||||
StringBuilder sb = new StringBuilder(string.length());
|
||||
double rnd;
|
||||
for (char c: string.toCharArray()) {
|
||||
rnd = Math.random();
|
||||
if (rnd < 0.34)
|
||||
sb.append(c);
|
||||
else if (rnd < 0.67)
|
||||
sb.insert(sb.length() / 2, c);
|
||||
else
|
||||
sb.insert(0, c);
|
||||
public static String getDeviceId() {
|
||||
String sb2 = Build.PRODUCT +
|
||||
Build.BOARD +
|
||||
Build.BOOTLOADER +
|
||||
Build.BRAND +
|
||||
Build.DEVICE +
|
||||
Build.DISPLAY +
|
||||
Build.FINGERPRINT +
|
||||
Build.HARDWARE +
|
||||
Build.HOST +
|
||||
Build.ID +
|
||||
Build.MANUFACTURER +
|
||||
Build.MODEL +
|
||||
Build.PRODUCT +
|
||||
Build.TAGS;
|
||||
|
||||
sb.append(new Random().nextInt(10));
|
||||
sb.deleteCharAt(0);
|
||||
}
|
||||
return sb.toString();
|
||||
return MD5(sb2);
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
private static String findDeviceId() {
|
||||
return Settings.Secure.getString(getGlobalContext().getContentResolver(), "android_id");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ public class DockBarInjector {
|
||||
}
|
||||
|
||||
public static Class<?> interceptClick(int id, RightMenu rightMenu) {
|
||||
if (id == tab_menu) {
|
||||
if (id == tab_menu && rightMenu != null) {
|
||||
rightMenu.a();
|
||||
}
|
||||
|
||||
@@ -294,6 +294,7 @@ public class DockBarInjector {
|
||||
if (id == tab.id)
|
||||
return tab.fragmentClass;
|
||||
}
|
||||
|
||||
for (DockBarTab tab : sManager.getDisabledTabs()) {
|
||||
if (id == tab.id)
|
||||
return tab.fragmentClass;
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.foaf.FoafBase.getLastSeen;
|
||||
import static ru.vtosters.lite.foaf.FoafBase.getBypassedOnlineInfo;
|
||||
import static ru.vtosters.lite.hooks.DateHook.getLocale;
|
||||
import static ru.vtosters.lite.utils.Base64Utils.decode;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCaption;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCopyright;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isBadNews;
|
||||
import static ru.vtosters.lite.utils.Preferences.ads;
|
||||
import static ru.vtosters.lite.utils.Preferences.adsgroup;
|
||||
import static ru.vtosters.lite.utils.Preferences.adsstories;
|
||||
import static ru.vtosters.lite.utils.Preferences.authorsrecomm;
|
||||
import static ru.vtosters.lite.hooks.OnlineFormatterHook.onlineHookProfiles;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getStatic;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs;
|
||||
import static ru.vtosters.lite.utils.Preferences.dev;
|
||||
import static ru.vtosters.lite.utils.Preferences.friendsblock;
|
||||
import static ru.vtosters.lite.utils.Preferences.friendsrecomm;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
import static ru.vtosters.lite.utils.Preferences.hasVerification;
|
||||
import static ru.vtosters.lite.utils.Preferences.podcastcatalog;
|
||||
import static ru.vtosters.lite.utils.Preferences.postsrecomm;
|
||||
import static ru.vtosters.lite.utils.VTVerifications.isDeveloper;
|
||||
import static ru.vtosters.lite.utils.VTVerifications.isPrometheus;
|
||||
import static ru.vtosters.lite.utils.VTVerifications.isVerified;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.core.network.Network;
|
||||
@@ -33,6 +28,8 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
@@ -41,7 +38,6 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.NewsFeedFiltersUtils;
|
||||
|
||||
public class JsonInjectors {
|
||||
private static final OkHttpClient mClient = new OkHttpClient();
|
||||
@@ -69,7 +65,7 @@ public class JsonInjectors {
|
||||
}
|
||||
|
||||
public static JSONObject donateRecomm() throws JSONException {
|
||||
var title = "Помоги проекту донатом и получи бонус!";
|
||||
var title = AndroidUtils.getString("donate_to_vtl");
|
||||
var link = "https://vk.com/vtosters_official";
|
||||
var text_color = "2D81E0";
|
||||
|
||||
@@ -110,9 +106,9 @@ public class JsonInjectors {
|
||||
}
|
||||
|
||||
public static JSONObject vktesters(int id) throws JSONException {
|
||||
var title = "Карточка Тестера";
|
||||
var title = AndroidUtils.getString("tester_profile");
|
||||
var text_color = "2D81E0";
|
||||
var link = "https://static.vk.com/bugs?lang=" + LangUtils.a() + "#/reporter" + id;
|
||||
var link = "https://" + getStatic() + "/bugs?lang=" + LangUtils.a() + "#/reporter" + id;
|
||||
|
||||
var json = new JSONObject();
|
||||
var icons = new JSONArray();
|
||||
@@ -154,27 +150,20 @@ public class JsonInjectors {
|
||||
var peerid = Objects.requireNonNull(orig.optJSONObject("peer")).optInt("id");
|
||||
|
||||
var pic = "https://image.pngaaa.com/641/326641-middle.png"; // can be null
|
||||
var text = "Я не загрузил данные (9(9((";
|
||||
var text = AndroidUtils.getString("no_data_error");
|
||||
var link = "https://vtosters.app"; // can be null
|
||||
var linktitle = "Test button"; // can be null
|
||||
|
||||
// "{\"layout\":\"tertiary\",\"text\":\"" + linktitle + "\",\"type\":\"link\",\"link\":\"" + link + "\"}";
|
||||
var buttons = decode("eyJsYXlvdXQiOiJ0ZXJ0aWFyeSIsInRleHQiOiI=") + linktitle + decode("IiwidHlwZSI6ImxpbmsiLCJsaW5rIjoi") + link + decode("In0=");
|
||||
// ,"icon":" + pic + "
|
||||
var icon = decode("LCJpY29uIjoi") + pic + decode("Ig==");
|
||||
|
||||
var hasIcon = !pic.isEmpty();
|
||||
var hasButton = !buttons.isEmpty();
|
||||
var hasButton = !link.isEmpty();
|
||||
|
||||
var isPicture = pic.endsWith(".png") || pic.endsWith(".jpg") || pic.endsWith(".jpeg") || pic.endsWith(".webp");
|
||||
|
||||
if (!isPicture) hasIcon = false;
|
||||
if (!hasIcon) icon = "";
|
||||
if (!hasButton) buttons = "";
|
||||
if (isVerified(peerid)) text = AndroidUtils.getString("i_bought") + " VTosters Premium";
|
||||
if (isPrometheus(peerid)) text = AndroidUtils.getString("i_bought") + " VTosters Premium Gold Prime Pro Plus";
|
||||
if (isDeveloper(peerid)) text = AndroidUtils.getString("i_created_poop");
|
||||
|
||||
if (isVerified(peerid)) text = "Я купил VTosters Premium";
|
||||
if (isPrometheus(peerid)) text = "Я купил VTosters Premium Gold Prime Pro Plus";
|
||||
if (isDeveloper(peerid)) text = "Я создал говно";
|
||||
if (!isVerified(peerid) || text.equals("")) {
|
||||
if (!isVerified(peerid) || text.equals("") || peerid == AccountManagerUtils.getUserId() || peerid == 0) {
|
||||
if (getBoolValue("convBarRecomm", false)) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -182,14 +171,27 @@ public class JsonInjectors {
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev()) return orig.optJSONObject("conversation_bar");
|
||||
if (!dev()) return orig.optJSONObject("conversation_bar"); // for devs only
|
||||
|
||||
// JSONObject("{\"name\":\"group_admin_welcome\",\"text\":\"" + textverif + "\",\"buttons\":[],\"icon\":\"" + pic + "\"}");
|
||||
return new JSONObject(decode("eyJuYW1lIjoiZ3JvdXBfYWRtaW5fd2VsY29tZSIsInRleHQiOiI=")
|
||||
+ text + decode("IiwiYnV0dG9ucyI6Ww==")
|
||||
+ buttons + decode("XQ==")
|
||||
+ icon
|
||||
+ decode("fQ=="));
|
||||
var json = new JSONObject();
|
||||
|
||||
var buttonsJson = new JSONObject();
|
||||
buttonsJson.put("layout", "tertiary");
|
||||
buttonsJson.put("text", linktitle);
|
||||
buttonsJson.put("type", "link");
|
||||
buttonsJson.put("link", link);
|
||||
|
||||
var buttonsjson = new JSONArray(); // max 3 buttons in array
|
||||
if (hasButton) buttonsjson.put(buttonsJson);
|
||||
|
||||
json.put("name", "group_admin_welcome");
|
||||
json.put("text", text);
|
||||
json.put("buttons", buttonsjson);
|
||||
if (hasIcon && isPicture) {
|
||||
json.put("icon", pic);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject menu(JSONObject orig) throws JSONException {
|
||||
@@ -234,30 +236,12 @@ public class JsonInjectors {
|
||||
}
|
||||
|
||||
public static JSONObject musiclink(JSONObject json) {
|
||||
var oldItems = json.optJSONArray("links");
|
||||
var section = json.optJSONObject("section");
|
||||
var linksblock = section.optJSONArray("blocks");
|
||||
var newBlock = new JSONArray();
|
||||
var links = json.optJSONArray("links");
|
||||
|
||||
if (oldItems != null) {
|
||||
if (oldItems.optJSONObject(0).optString("url").contains("?section=recent")) {
|
||||
if (links != null) {
|
||||
if (links.optJSONObject(0).optString("url").contains("?section=recent")) {
|
||||
json.remove("links");
|
||||
|
||||
for (int i = 0; i < linksblock.length(); i++) {
|
||||
var item = linksblock.optJSONObject(i);
|
||||
var datatype = item.optString("data_type");
|
||||
var layname = item.optJSONObject("layout").optString("name");
|
||||
if (!datatype.equals("links") && !layname.equals("separator")) {
|
||||
newBlock.put(item);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
section.putOpt("blocks", newBlock);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (dev()) Log.d("VKMusic", "Removed links buttons");
|
||||
}
|
||||
}
|
||||
@@ -265,195 +249,120 @@ public class JsonInjectors {
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject setOnlineInfo(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
public static JSONObject setOnlineInfo(JSONObject json) throws JSONException {
|
||||
var id = json.optInt("id");
|
||||
if (id == AccountManagerUtils.getUserId()) {
|
||||
return json;
|
||||
}
|
||||
var onlineinfo = json.optJSONObject("online_info");
|
||||
var time = getLastSeen(0L, id);
|
||||
if (onlineinfo != null && !onlineinfo.optBoolean("visible")) {
|
||||
var bypassed = getBypassedOnlineInfo(id);
|
||||
if (bypassed.optInt("last_seen", 0) != 0) {
|
||||
json.remove("online_info");
|
||||
|
||||
if (time != 0L && onlineinfo != null && !onlineinfo.optBoolean("is_online") && id != AccountManagerUtils.getUserId()) {
|
||||
json.remove("online_info");
|
||||
var online_info = new JSONObject()
|
||||
.put("visible", true)
|
||||
.put("last_seen", bypassed.optInt("last_seen"))
|
||||
.put("is_online", bypassed.optBoolean("is_online"))
|
||||
.put("app_id", bypassed.optInt("app_id"))
|
||||
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
||||
|
||||
var last_seen = new JSONObject()
|
||||
.put("platform", bypassed.optInt("platform"))
|
||||
.put("time", bypassed.optInt("last_seen"));
|
||||
|
||||
json.put("last_seen", last_seen).put("online_info", online_info);
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONArray setOnlineInfoUsers(JSONArray profiles) throws JSONException {
|
||||
if (profiles == null || profiles.length() == 0) return profiles;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var curVkId = AccountManagerUtils.getUserId();
|
||||
for (int i=0; i < profiles.length(); i++) {
|
||||
JSONObject profile = profiles.getJSONObject(i);
|
||||
int id = profile.optInt("id", -1);
|
||||
JSONObject onlinfo = profile.optJSONObject("online_info");
|
||||
if (id == curVkId || id < 0 || onlinfo == null || onlinfo.optBoolean("visible")) {
|
||||
continue;
|
||||
}
|
||||
sb.append(id);
|
||||
sb.append(",");
|
||||
}
|
||||
var ids = sb.toString();
|
||||
if (ids.length() > 0) {
|
||||
ids = ids.substring(0, ids.length() - 1);
|
||||
} else {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
JSONObject bypassedObj = getBypassedOnlineInfo(ids);
|
||||
for (int i=0; i < profiles.length(); i++) {
|
||||
JSONObject profile = profiles.getJSONObject(i);
|
||||
int id = profile.optInt("id");
|
||||
JSONObject bypassed = bypassedObj.optJSONObject(Integer.toString(id));
|
||||
if (bypassed == null) {
|
||||
continue;
|
||||
}
|
||||
profile.remove("online_info");
|
||||
|
||||
var online_info = new JSONObject()
|
||||
.put("visible", true)
|
||||
.put("last_seen", time)
|
||||
.put("is_online", false)
|
||||
.put("app_id", 0)
|
||||
.put("is_mobile", false);
|
||||
.put("last_seen", bypassed.optInt("last_seen"))
|
||||
.put("is_online", bypassed.optBoolean("is_online"))
|
||||
.put("app_id", bypassed.optInt("app_id"))
|
||||
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
||||
|
||||
var last_seen = new JSONObject()
|
||||
.put("platform", 4)
|
||||
.put("time", time);
|
||||
.put("platform", bypassed.optInt("platform"))
|
||||
.put("time", bypassed.optInt("last_seen"));
|
||||
|
||||
json.put("last_seen", last_seen).put("online_info", online_info);
|
||||
profile.put("last_seen", last_seen).put("online_info", online_info);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject storiesads(JSONObject json, boolean isDeleteFix) throws JSONException {
|
||||
if (!adsstories()) {
|
||||
return json;
|
||||
}
|
||||
|
||||
if (json.has("ads")) {
|
||||
if (isDeleteFix) {
|
||||
json.optJSONObject("ads").optJSONObject("settings")
|
||||
.put("stories_interval", 0)
|
||||
.put("authors_interval", 0)
|
||||
.put("time_interval", 0)
|
||||
.put("stories_init", 0)
|
||||
.put("authors_init", 0)
|
||||
.put("time_init", 0);
|
||||
if (dev()) Log.d("StoriesAds", "Set ads settings at zero values");
|
||||
} else {
|
||||
json.remove("ads");
|
||||
if (dev()) Log.d("StoriesAds", "Removed ads block");
|
||||
}
|
||||
}
|
||||
|
||||
if (!json.has("items")) return json;
|
||||
|
||||
var items = json.optJSONArray("items");
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
var item = items.optJSONObject(i);
|
||||
if (item != null) {
|
||||
parseStoriesItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private static void parseStoriesItem(JSONObject item) throws JSONException {
|
||||
var stories = item.optJSONArray("stories");
|
||||
var newStories = new JSONArray();
|
||||
|
||||
if (stories == null) return;
|
||||
|
||||
for (int j = 0; j < stories.length(); j++) {
|
||||
var story = stories.optJSONObject(j);
|
||||
|
||||
if (!story.optBoolean("is_ads") && !story.optBoolean("is_promo")) {
|
||||
newStories.put(story);
|
||||
} else {
|
||||
if (dev())
|
||||
Log.d("StoriesAds", "Fetched ad, owner id " + story.optString("owner_id") + ", caption " + story.optString("caption"));
|
||||
}
|
||||
}
|
||||
|
||||
item.put("stories", newStories);
|
||||
}
|
||||
|
||||
public static Boolean isAds(JSONObject list, String type) throws JSONException {
|
||||
if (list == null || type == null || !ads()) return false;
|
||||
|
||||
if (list.has("ads")
|
||||
|| type.equals("ads")
|
||||
|| type.equals("carousel")
|
||||
|| type.equals("html5_ad")
|
||||
|| type.equals("ads_easy_promote")) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: ads");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.equals("promo_button")
|
||||
|| type.equals("app_widget")
|
||||
|| type.equals("app_video")
|
||||
|| type.equals("app_slider")
|
||||
|| type.equals("tags_suggestions")) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: promo");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return profiles;
|
||||
}
|
||||
|
||||
public static JSONArray newsfeedlist(JSONArray items) throws JSONException {
|
||||
for (int j = 0; j < items.length(); j++) {
|
||||
var list = items.optJSONObject(j);
|
||||
var name = list.optString("id");
|
||||
var selectedItems = getDefaultPrefs().getString("news_feed_selected_items", "");
|
||||
var filtersSet = getDefaultPrefs().getStringSet("news_feed_items_set", null);
|
||||
var mutableFiltersSet = Collections.synchronizedSet(new LinkedHashSet<String>());
|
||||
if (filtersSet != null)
|
||||
mutableFiltersSet.addAll(filtersSet);
|
||||
|
||||
if (!name.equals("kpop") && !name.equals("foryou") && !name.equals("qazaqstan")) {
|
||||
list.put("is_hidden", false).put("is_unavailable", false);
|
||||
if (dev()) Log.d("NewsfeedListInj", "Unlocked " + name + " in newsfeed list");
|
||||
synchronized (mutableFiltersSet) {
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
var item = items.optJSONObject(i);
|
||||
if (item == null) continue;
|
||||
|
||||
var id = item.optString("id");
|
||||
var title = item.optString("title");
|
||||
|
||||
if (TextUtils.isEmpty(id) || TextUtils.isEmpty(title)
|
||||
// this items not working
|
||||
|| id.equals("kpop") || id.equals("foryou")
|
||||
|| id.equals("qazaqstan") || id.equals("podcasts"))
|
||||
continue;
|
||||
|
||||
mutableFiltersSet.add(id + "|" + title);
|
||||
|
||||
var hide = selectedItems.contains(id);
|
||||
|
||||
Log.d("NewsfeedList", "Added list " + id + " to feed");
|
||||
item.put("is_hidden", hide)
|
||||
.put("is_unavailable", hide);
|
||||
if (dev()) Log.d("NewsfeedListInj", "Unlocked " + id + " in newsfeed list");
|
||||
}
|
||||
getDefaultPrefs().edit().putStringSet("news_feed_items_set", mutableFiltersSet)
|
||||
.apply();
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public static JSONArray newsfeedadtest(JSONArray items) throws JSONException {
|
||||
if (!getBoolValue("newadblock", true)) return items;
|
||||
var newItems = new JSONArray();
|
||||
|
||||
for (int j = 0; j < items.length(); j++) {
|
||||
var list = items.optJSONObject(j);
|
||||
var type = list.optString("type");
|
||||
|
||||
if (isAds(list, type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (authorsrecomm() && (type.equals("authors_rec")
|
||||
|| type.startsWith("recommended_") && (type.endsWith("audios")
|
||||
|| type.endsWith("artists")
|
||||
|| type.endsWith("playlists")
|
||||
|| type.endsWith("groups")))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: authorsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (postsrecomm() && (type.equals("inline_user_rec") || type.equals("live_recommended"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: postsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (friendsrecomm() && (type.equals("user_rec") || type.equals("friends_recomm"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: friendsrecomm");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (adsgroup() && list.optInt("marked_as_ads") == 1) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: marked_as_ads is true");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBadNews(list.optString("text"))) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: text filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCopyright(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: copyright filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCaption(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: caption filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NewsFeedFiltersUtils.injectFiltersReposts(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: repost ad");
|
||||
continue;
|
||||
}
|
||||
|
||||
newItems.put(list);
|
||||
}
|
||||
|
||||
return newItems;
|
||||
}
|
||||
|
||||
public static JSONObject music(JSONObject json) throws JSONException {
|
||||
var catalog = json.optJSONObject("catalog");
|
||||
|
||||
@@ -527,7 +436,7 @@ public class JsonInjectors {
|
||||
|
||||
Log.d("VKMusic", "title: " + title + " id: " + id + " url: " + url + " value: " + value);
|
||||
|
||||
if (url.contains(value)) {
|
||||
if (url.contains(value) && !value.isEmpty()) {
|
||||
catalog.put("default_section", id);
|
||||
if (dev()) Log.d("VKMusic", "Added " + title + " as default music section");
|
||||
}
|
||||
@@ -538,10 +447,10 @@ public class JsonInjectors {
|
||||
}
|
||||
|
||||
public static JSONObject fetchCatalogId(String section) {
|
||||
if (section == null) return null;
|
||||
if (section.isEmpty()) return null;
|
||||
|
||||
var request = new Request.a()
|
||||
.b("https://api.vk.com/method/catalog.getAudio"
|
||||
.b("https://" + getApi() + "/method/catalog.getAudio"
|
||||
+ "?v=5.119"
|
||||
+ "&https=1"
|
||||
+ "&need_blocks=1"
|
||||
@@ -565,7 +474,7 @@ public class JsonInjectors {
|
||||
|
||||
public static JSONObject fetchCatalogPodcast() {
|
||||
var request = new Request.a()
|
||||
.b("https://api.vk.com/method/catalog.getPodcasts"
|
||||
.b("https://" + getApi() + "/method/catalog.getPodcasts"
|
||||
+ "?v=5.119"
|
||||
+ "&https=1"
|
||||
+ "&need_blocks=1"
|
||||
@@ -585,8 +494,8 @@ public class JsonInjectors {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JSONObject friends(JSONObject json) throws JSONException {
|
||||
JSONObject catalog = json;
|
||||
public static JSONObject friends(JSONObject json) throws JSONException, ParseException, IOException {
|
||||
JSONObject catalog = onlineHookProfiles(json);
|
||||
boolean sectionexecute = true;
|
||||
boolean hasBirthday = false;
|
||||
|
||||
@@ -651,10 +560,7 @@ public class JsonInjectors {
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
public static boolean haveDonateButton() {
|
||||
int randomshower = new Random().nextInt(6);
|
||||
|
||||
return hasVerification() || randomshower != 1;
|
||||
return hasVerification() || new Random().nextInt(6) != 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.ui.dialogs.ServerDialog.sendRequest;
|
||||
import static ru.vtosters.lite.utils.CacheUtils.getInstance;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.setupFilters;
|
||||
import static ru.vtosters.lite.utils.Preferences.checkupdates;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.isDarkTheme;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.setNeededTheme;
|
||||
@@ -10,20 +11,26 @@ import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.vk.core.concurrent.VkExecutors;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.ui.dialogs.DisableBattery;
|
||||
import ru.vtosters.lite.ui.dialogs.InstallGMS;
|
||||
import ru.vtosters.lite.ui.dialogs.OTADialog;
|
||||
import ru.vtosters.lite.ui.dialogs.ServerDialog;
|
||||
import ru.vtosters.lite.ui.dialogs.Start;
|
||||
import ru.vtosters.lite.utils.DeletedMessagesHandler;
|
||||
|
||||
/**
|
||||
* @
|
||||
*/
|
||||
public class MainActivityInjector {
|
||||
|
||||
public static void inject(Activity activity) {
|
||||
setNeededTheme(activity);
|
||||
ServerDialog.activity = activity;
|
||||
sendRequest();
|
||||
if (checkupdates()) OTADialog.checkUpdates(activity);
|
||||
VkExecutors.x.f().a(DeletedMessagesHandler::reloadMessagesList); // ioScheduler
|
||||
Start.alert(activity);
|
||||
InstallGMS.alert(activity);
|
||||
DisableBattery.alert(activity);
|
||||
@@ -31,7 +38,14 @@ public class MainActivityInjector {
|
||||
.edit()
|
||||
.putBoolean("isdark", isDarkTheme())
|
||||
.commit();
|
||||
getInstance().autoCleaningCache();
|
||||
|
||||
|
||||
VkExecutors.x.q().a(() -> {
|
||||
getInstance().autoCleaningCache();
|
||||
}); // slowTasksScheduler
|
||||
|
||||
setupFilters();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannels.createChannels();
|
||||
}
|
||||
|
||||
@@ -7,16 +7,17 @@ import static ru.vtosters.lite.utils.Preferences.autoalltranslate;
|
||||
import static ru.vtosters.lite.utils.Preferences.autotranslate;
|
||||
import static ru.vtosters.lite.utils.Preferences.savemsgsett;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.vk.api.internal.MethodCall;
|
||||
|
||||
import ru.vtosters.lite.translators.BaseTranslator;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ru.vtosters.lite.translators.BaseTranslator;
|
||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||
|
||||
public class MessagesHook {
|
||||
public static String injectOwnText(String oldText) {
|
||||
@@ -73,6 +74,14 @@ public class MessagesHook {
|
||||
});
|
||||
}
|
||||
|
||||
public static Context fixCTX(Context ctx) {
|
||||
if (ctx == null){
|
||||
return LifecycleUtils.getCurrentActivity();
|
||||
} else {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
||||
public static void injRequest(MethodCall.a req) {
|
||||
var time = expireTime();
|
||||
|
||||
|
||||
@@ -1,58 +1,31 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static java.lang.Long.MAX_VALUE;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCaption;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.checkCopyright;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.injectFiltersReposts;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isAds;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isAuthorRecommendations;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isBadNews;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isFriendsRecommendations;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isGroupAds;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isMusicBlock;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isNewsBlock;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isPostRecommendations;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.isRecomsGroup;
|
||||
import static ru.vtosters.lite.utils.Preferences.ads;
|
||||
import static ru.vtosters.lite.utils.Preferences.adsslider;
|
||||
import static ru.vtosters.lite.utils.Preferences.authorsrecomm;
|
||||
import static ru.vtosters.lite.utils.Preferences.friendsrecomm;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
import static ru.vtosters.lite.utils.Preferences.vkme;
|
||||
|
||||
import com.vk.core.preference.Preference;
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.vk.core.preference.Preference;
|
||||
import com.vtosters.lite.api.ExtendedCommunityProfile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
|
||||
public class NewsfeedHook {
|
||||
public static boolean injectFilters(JSONObject obj) throws JSONException {
|
||||
if (getBoolValue("newadblock", true)) return true;
|
||||
|
||||
var optString = obj.optString("type", "");
|
||||
if (isAds(optString) || isAuthorRecommendations(optString) || isPostRecommendations(optString) || isFriendsRecommendations(optString) || isRecomsGroup(optString) || isMusicBlock(optString) || isNewsBlock(optString)) {
|
||||
return false;
|
||||
}
|
||||
optString = obj.optString("post_type", "");
|
||||
if (isAds(optString) || isAuthorRecommendations(optString) || isPostRecommendations(optString) || isFriendsRecommendations(optString) || isMusicBlock(optString) || isNewsBlock(optString)) {
|
||||
return false;
|
||||
}
|
||||
optString = obj.optString("filters", "");
|
||||
if (isAds(optString) || isAuthorRecommendations(optString) || isPostRecommendations(optString) || isFriendsRecommendations(optString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkCopyright(obj)) return false;
|
||||
|
||||
if (isBadNews(obj.optString("text", ""))) return false;
|
||||
|
||||
if (checkCaption(obj)) return false;
|
||||
|
||||
if (isGroupAds(obj)) return false;
|
||||
|
||||
return !injectFiltersReposts(obj);
|
||||
} // inject our filters to newsfeed, getpost and discover
|
||||
|
||||
public static long getUpdateNewsfeed(boolean refresh_timeout) {
|
||||
if (vkme()) {
|
||||
return MAX_VALUE;
|
||||
@@ -73,4 +46,90 @@ public class NewsfeedHook {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static String[] feedParams(){
|
||||
HashSet<String> params = new HashSet<>();
|
||||
params.add("post");
|
||||
params.add("photo");
|
||||
params.add("photo_tag");
|
||||
|
||||
if (!friendsrecomm() && !getBoolValue("donotgetads", false)) {
|
||||
params.add("friends_recomm");
|
||||
}
|
||||
|
||||
if (!ads()) {
|
||||
params.add("app_widget");
|
||||
params.add("promo_button");
|
||||
}
|
||||
|
||||
if (!authorsrecomm() && !getBoolValue("donotgetads", false)) {
|
||||
params.add("authors_rec");
|
||||
}
|
||||
|
||||
return params.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static boolean isWhitelistedFilter(ExtendedCommunityProfile eup) {
|
||||
return getDefaultPrefs().getStringSet("whitelisted_filters_groups", Collections.synchronizedSet(new LinkedHashSet<>())).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
|
||||
}
|
||||
|
||||
public static boolean isWhitelistedAd(ExtendedCommunityProfile eup) {
|
||||
return getDefaultPrefs().getStringSet("whitelisted_ad_groups", Collections.synchronizedSet(new LinkedHashSet<>())).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
|
||||
}
|
||||
|
||||
public static void setWhitelistedFilter(ExtendedCommunityProfile eup, boolean needWhitelist) {
|
||||
var mutableAdsSet = Collections.synchronizedSet(new LinkedHashSet<String>());
|
||||
var id = String.valueOf(AccountManagerUtils.getUserID(eup));
|
||||
|
||||
mutableAdsSet.addAll(getDefaultPrefs().getStringSet("whitelisted_filters_groups", mutableAdsSet));
|
||||
|
||||
if (needWhitelist) {
|
||||
mutableAdsSet.add(id);
|
||||
} else {
|
||||
mutableAdsSet.remove(id);
|
||||
}
|
||||
|
||||
getDefaultPrefs().edit().putStringSet("whitelisted_filters_groups", mutableAdsSet).apply();
|
||||
}
|
||||
|
||||
public static void setWhitelistedAd(ExtendedCommunityProfile eup, boolean needWhitelist) {
|
||||
var mutableAdsSet = Collections.synchronizedSet(new LinkedHashSet<String>());
|
||||
var id = String.valueOf(AccountManagerUtils.getUserID(eup));
|
||||
|
||||
mutableAdsSet.addAll(getDefaultPrefs().getStringSet("whitelisted_ad_groups", mutableAdsSet));
|
||||
|
||||
if (needWhitelist) {
|
||||
mutableAdsSet.add(id);
|
||||
} else {
|
||||
mutableAdsSet.remove(id);
|
||||
}
|
||||
|
||||
getDefaultPrefs().edit().putStringSet("whitelisted_ad_groups", mutableAdsSet).apply();
|
||||
}
|
||||
|
||||
public static void adsParams(HashSet<String> hashSet) {
|
||||
if (ads()) {
|
||||
hashSet.add("ads_disabled");
|
||||
if (getBoolValue("donotgetads", false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hashSet.add("ads_app");
|
||||
hashSet.add("ads_site");
|
||||
hashSet.add("ads_post");
|
||||
if (!adsslider()) {
|
||||
hashSet.add("ads_app_slider");
|
||||
}
|
||||
hashSet.add("ads_site_slider");
|
||||
hashSet.add("ads_app_video");
|
||||
hashSet.add("ads_post_pretty_cards");
|
||||
hashSet.add("ads_post_snippet_video");
|
||||
}
|
||||
|
||||
public static boolean isPowerSaveMode() {
|
||||
var pw = (PowerManager) getGlobalContext().getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
return !getBoolValue("force_disable_psm", false) && pw.isPowerSaveMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.hooks.JsonInjectors.setOnlineInfo;
|
||||
import static ru.vtosters.lite.hooks.JsonInjectors.setOnlineInfoUsers;
|
||||
import static ru.vtosters.lite.net.Request.makeRequest;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
import static ru.vtosters.lite.utils.Preferences.dev;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
import static ru.vtosters.lite.utils.Preferences.getPrefsFromFile;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.getApi;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -17,6 +20,8 @@ import org.json.JSONObject;
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class OnlineFormatterHook {
|
||||
private static String AppName;
|
||||
|
||||
@@ -59,37 +64,37 @@ public class OnlineFormatterHook {
|
||||
case 5027722:
|
||||
return "VK Messenger (Desktop)";
|
||||
case 6146827:
|
||||
return "VK Мессенджер (Android)";
|
||||
return AndroidUtils.getString("vk_messenger") + " (Android)"; // VK Мессенджер (Android)
|
||||
case 6482950:
|
||||
return "VK Мессенджер (iPhone)";
|
||||
return AndroidUtils.getString("vk_messenger") + " (iPhone)"; // VK Мессенджер (iPhone)
|
||||
case 6481715:
|
||||
return "VK Мессенджер Dev (iPhone)";
|
||||
return AndroidUtils.getString("vk_messenger") + " Dev (iPhone)"; // VK Мессенджер Dev (iPhone)
|
||||
case 7799655:
|
||||
return "VK Почта";
|
||||
return AndroidUtils.getString("vk_mail_online"); // VK Почта
|
||||
case 7598572:
|
||||
return "Сферум (Android)";
|
||||
return AndroidUtils.getString("sferum_online") + " (Android)"; // Сферум (Android)
|
||||
case 7571751:
|
||||
return "Сферум (iPhone)";
|
||||
return AndroidUtils.getString("sferum_online") + " (iPhone)"; // Сферум (iPhone)
|
||||
case 7556576:
|
||||
return "Сферум";
|
||||
return AndroidUtils.getString("sferum_online"); // Сферум
|
||||
case 7497650:
|
||||
return "VK ID";
|
||||
case 8094476:
|
||||
return "VK Звонки (Android)";
|
||||
return AndroidUtils.getString("vk_calls_online") + " (Android)"; // VK Звонки (Android)
|
||||
case 8093730:
|
||||
return "VK Звонки (iPhone)";
|
||||
return AndroidUtils.getString("vk_calls_online") + " (iPhone)"; // VK Звонки (iPhone)
|
||||
case 7793118:
|
||||
return "VK Звонки (Desktop)";
|
||||
return AndroidUtils.getString("vk_calls_online") + " (Desktop)"; // VK Звонки (Desktop)
|
||||
case 6767438:
|
||||
return "VK Музыка (Android)";
|
||||
return AndroidUtils.getString("vk_music_online") + " (Android)"; // VK Музыка (Android)
|
||||
case 8113297:
|
||||
return "VK Клипы (Android)";
|
||||
return AndroidUtils.getString("vk_clips_online") + " (Android)"; // VK Клипы (Android)
|
||||
case 8106428:
|
||||
return "VK Клипы (iPhone)";
|
||||
return AndroidUtils.getString("vk_clips_online") + " (iPhone)"; // VK Клипы (iPhone)
|
||||
case 5044491:
|
||||
return "Candy";
|
||||
case 8114066:
|
||||
return "VK Видео";
|
||||
return AndroidUtils.getString("vk_video_online"); // VK Видео
|
||||
case 4894723:
|
||||
return "Phoenix Lite";
|
||||
case 4994316:
|
||||
@@ -125,6 +130,12 @@ public class OnlineFormatterHook {
|
||||
return "Miranda NG (bridge)";
|
||||
case 8043814:
|
||||
return "Quise";
|
||||
case 7221936:
|
||||
return "Вечный Online";
|
||||
case 6892935:
|
||||
return "VK Helper";
|
||||
case 6248661:
|
||||
return "MemeVoices for VK";
|
||||
default:
|
||||
return getAppNameFromOnline(appid);
|
||||
}
|
||||
@@ -165,14 +176,66 @@ public class OnlineFormatterHook {
|
||||
return getString("custom_online") + " " + appname;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHook(JSONObject json, boolean isGlobalHook) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
public static JSONObject onlineHook(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (getBoolValue("onlinefix", false)) setOnlineInfo(json);
|
||||
|
||||
if (isGlobalHook && !getBoolValue("globalUsersOnline", false)) {
|
||||
return json;
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONArray onlineHookList(JSONArray jsonArr) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return jsonArr;
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(jsonArr);
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookProfiles", e.getMessage());
|
||||
}
|
||||
|
||||
setOnlineInfo(json);
|
||||
return jsonArr;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookProfiles(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(json.optJSONArray("profiles"));
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookProfiles", e.getMessage());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookItems(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(json.optJSONArray("items"));
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookItems", e.getMessage());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookRequestsAndRecommendations(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(json.optJSONObject("read_requests").optJSONArray("items"));
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookItems", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(json.optJSONObject("recommendations").optJSONArray("items"));
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookItems", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
setOnlineInfoUsers(json.optJSONArray("profiles"));
|
||||
} catch (Exception e) {
|
||||
Log.e("onlineHookItems", e.getMessage());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ClipData;
|
||||
@@ -15,10 +16,15 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.vk.core.dialogs.actionspopup.ActionsPopup;
|
||||
import com.vk.core.drawable.RecoloredDrawable;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.common.AttachmentWithMedia;
|
||||
import com.vk.dto.common.ImageSize;
|
||||
import com.vtosters.lite.R;
|
||||
import com.vtosters.lite.attachments.DocumentAttachment;
|
||||
import com.vtosters.lite.attachments.PhotoAttachment;
|
||||
|
||||
import org.json.JSONException;
|
||||
@@ -26,6 +32,7 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
@@ -36,18 +43,17 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class PhotoViewer {
|
||||
|
||||
static OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) {
|
||||
if (item.getItemId() == AndroidUtils.getIdentifier("search_photo", "id")) {
|
||||
searchPhoto(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
} else if (item.getItemId() == AndroidUtils.getIdentifier("copy_photo_url", "id")) {
|
||||
copyPhotoUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
} else if (item.getItemId() == AndroidUtils.getIdentifier("open_original_photo", "id")) {
|
||||
openUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
@@ -61,7 +67,19 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_menu_search_outline_28), ThemesUtils.getAccentColor()),
|
||||
false,
|
||||
() -> {
|
||||
searchPhoto(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
if (attachment instanceof PhotoAttachment) {
|
||||
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
} else if (attachment instanceof DocumentAttachment) {
|
||||
var documentAttachment = (DocumentAttachment) attachment;
|
||||
if (documentAttachment.J != null)
|
||||
try {
|
||||
searchPhoto(getUrlFromDocumentAttachment(documentAttachment));
|
||||
} catch (Exception e) {
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
else
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
@@ -71,7 +89,19 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_copy_outline_28), Color.WHITE),
|
||||
false,
|
||||
() -> {
|
||||
copyPhotoUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
if (attachment instanceof PhotoAttachment) {
|
||||
copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
} else if (attachment instanceof DocumentAttachment) {
|
||||
var documentAttachment = (DocumentAttachment) attachment;
|
||||
if (documentAttachment.J != null)
|
||||
try {
|
||||
copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment));
|
||||
} catch (Exception e) {
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
else
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
@@ -82,14 +112,39 @@ public class PhotoViewer {
|
||||
ThemesUtils.getAccentColor()),
|
||||
false,
|
||||
() -> {
|
||||
openUrl(getUrlFromAttachment((PhotoAttachment) attachment));
|
||||
if (attachment instanceof PhotoAttachment) {
|
||||
openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
|
||||
} else if (attachment instanceof DocumentAttachment) {
|
||||
var documentAttachment = (DocumentAttachment) attachment;
|
||||
if (documentAttachment.J != null)
|
||||
try {
|
||||
openUrl(getUrlFromDocumentAttachment(documentAttachment));
|
||||
} catch (Exception e) {
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
else
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private static String getUrlFromAttachment(PhotoAttachment attachment) {
|
||||
private static String getUrlFromDocumentAttachment(DocumentAttachment attachment) {
|
||||
List<ImageSize> imageSizes = null;
|
||||
if (attachment.J != null) {
|
||||
imageSizes = attachment.J.t1();
|
||||
}
|
||||
return getUrlFromImageSizeList(imageSizes != null ? imageSizes : null);
|
||||
}
|
||||
|
||||
private static String getUrlFromPhotoAttachment(PhotoAttachment attachment) {
|
||||
var imageSizes = attachment.D.Q.t1();
|
||||
return getUrlFromImageSizeList(imageSizes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getUrlFromImageSizeList(List<ImageSize> imageSizes) {
|
||||
if (imageSizes.isEmpty())
|
||||
return null;
|
||||
var max = imageSizes.get(0);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
import static ru.vtosters.lite.utils.Preferences.preferences;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
|
||||
import com.facebook.drawee.generic.RoundingParams;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class PicRoundingHook{
|
||||
private static final int pref = preferences.getInt("pic_rounding", 0);
|
||||
|
||||
public static RoundingParams inject() {
|
||||
if (pref > 0) {
|
||||
return customParams();
|
||||
} else {
|
||||
return new RoundingParams().a(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static RoundingParams inject(float orig) {
|
||||
if (pref > 0) {
|
||||
return customParams();
|
||||
} else {
|
||||
return new RoundingParams().b(orig);
|
||||
}
|
||||
}
|
||||
|
||||
public static RoundingParams inject(float orig, float orig2, float orig3, float orig4) {
|
||||
if (pref > 0) {
|
||||
return new RoundingParams().a(AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref));
|
||||
} else {
|
||||
return new RoundingParams().a(orig, orig2, orig3, orig4);
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(float orig, Canvas canvas, Paint paint){
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), paint);
|
||||
} else {
|
||||
canvas.drawCircle(orig, orig, orig, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(Canvas canvas, Paint paint, float orig){
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), paint);
|
||||
} else {
|
||||
canvas.drawCircle(canvas.getClipBounds().exactCenterX(), canvas.getClipBounds().exactCenterY(), orig, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(Canvas canvas, float orig, float orig2, float orig3, Paint paint){
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), paint);
|
||||
} else {
|
||||
canvas.drawCircle(orig, orig2, orig3, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(Bitmap bitmap, Bitmap bitmap2, Paint b2) {
|
||||
Shader.TileMode tileMode = Shader.TileMode.CLAMP;
|
||||
b2.setShader(new BitmapShader(bitmap2, tileMode, tileMode));
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
float width = bitmap2.getWidth() / 2;
|
||||
|
||||
if (pref > 0) {
|
||||
canvas.drawRoundRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), b2);
|
||||
} else {
|
||||
canvas.drawCircle(width, width, width, b2);
|
||||
}
|
||||
}
|
||||
|
||||
private static RoundingParams customParams(){
|
||||
return RoundingParams.b(AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isRandomProxyEnabled;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isVikaProxyEnabled;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isZaboronaEnabled;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.dp2px;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
import static ru.vtosters.lite.utils.LifecycleUtils.restartApplication;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.apiproxy;
|
||||
import static ru.vtosters.lite.utils.ProxyUtils.isZaboronaEnabled;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getAlertStyle;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getTextAttr;
|
||||
@@ -14,7 +14,6 @@ import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.RadioButton;
|
||||
@@ -27,11 +26,12 @@ import com.vk.auth.ui.VkAuthTextView;
|
||||
import com.vk.navigation.Navigator;
|
||||
|
||||
import ru.vtosters.lite.ui.fragments.ProxySettingsFragment;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class ProxyHook {
|
||||
public static void hookAuth(View v) {
|
||||
VkAuthTextView button = (VkAuthTextView) v;
|
||||
button.setText("Настроить прокси");
|
||||
button.setText(AndroidUtils.getString("proxy_setup"));
|
||||
button.setOnClickListener(v1 -> callProxyDialog(v1.getContext()));
|
||||
}
|
||||
|
||||
@@ -40,44 +40,60 @@ public class ProxyHook {
|
||||
RadioGroup rg = new RadioGroup(ctx);
|
||||
|
||||
RadioButton rgDefault = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
|
||||
RadioButton rgProxy = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
|
||||
RadioButton rgZaborona = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
|
||||
RadioButton rgRandomProxy = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
|
||||
RadioButton rgVika = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
|
||||
|
||||
rg.addView(rgDefault);
|
||||
rg.addView(rgProxy);
|
||||
rg.addView(rgVika);
|
||||
rg.addView(rgZaborona);
|
||||
rg.addView(rgRandomProxy);
|
||||
|
||||
rgDefault.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
|
||||
rgProxy.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
|
||||
rgZaborona.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
|
||||
rgRandomProxy.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
|
||||
rgVika.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
|
||||
|
||||
rg.setPadding(dp2px(18f), dp2px(12f), dp2px(18f), 0);
|
||||
|
||||
rgDefault.setText("Отключить");
|
||||
rgDefault.setText(AndroidUtils.getString("proxy_disable"));
|
||||
rgDefault.setTextColor(getTextAttr());
|
||||
|
||||
rgProxy.setText("Включить (Zaborona)");
|
||||
rgProxy.setTextColor(getTextAttr());
|
||||
rgZaborona.setText(AndroidUtils.getString("proxy_enable") + " (Zaborona)");
|
||||
rgZaborona.setTextColor(getTextAttr());
|
||||
|
||||
rgProxy.setChecked(isZaboronaEnabled());
|
||||
rgDefault.setChecked(!isZaboronaEnabled());
|
||||
rgRandomProxy.setText(AndroidUtils.getString("proxy_enable") + " (Random Socks Proxy)");
|
||||
rgRandomProxy.setTextColor(getTextAttr());
|
||||
|
||||
rgVika.setText(AndroidUtils.getString("proxy_enable") + " (Vika Mobile)");
|
||||
rgVika.setTextColor(getTextAttr());
|
||||
|
||||
rgZaborona.setChecked(isZaboronaEnabled());
|
||||
rgDefault.setChecked(!isZaboronaEnabled() && !isRandomProxyEnabled() && !isVikaProxyEnabled());
|
||||
rgRandomProxy.setChecked(isRandomProxyEnabled());
|
||||
rgVika.setChecked(isVikaProxyEnabled());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx, getAlertStyle());
|
||||
builder.setTitle("Прокси");
|
||||
builder.setMessage("Zaborona никак не связана с ВТостерс и мы не можем гарантировать её работу и безопасность");
|
||||
builder.setTitle(AndroidUtils.getString("vtlproxy"));
|
||||
builder.setMessage(AndroidUtils.getString("proxy_warning"));
|
||||
builder.setView(rg);
|
||||
builder.setPositiveButton("Применить", ((dialog, which) -> {
|
||||
if (rgProxy.isChecked()) {
|
||||
builder.setPositiveButton(AndroidUtils.getString("vtl_confirm"), ((dialog, which) -> { // Применить
|
||||
if (rgZaborona.isChecked()) {
|
||||
edit().putString("proxy", "zaborona").commit();
|
||||
restartApplication();
|
||||
} else if (rgRandomProxy.isChecked()) {
|
||||
edit().putString("proxy", "randomproxy").commit();
|
||||
restartApplication();
|
||||
} else if (rgVika.isChecked()) {
|
||||
edit().putString("proxy", "vika").commit();
|
||||
restartApplication();
|
||||
} else {
|
||||
if (isZaboronaEnabled()) {
|
||||
edit().putString("proxy", "noproxy").commit();
|
||||
restartApplication();
|
||||
}
|
||||
|
||||
edit().putString("proxy", "noproxy").commit();
|
||||
restartApplication();
|
||||
}
|
||||
}));
|
||||
|
||||
builder.setNeutralButton("Настройки прокси", ((dialog, which) -> {
|
||||
builder.setNeutralButton(AndroidUtils.getString("proxy_settings"), ((dialog, which) -> {
|
||||
Intent a2 = new Navigator(ProxySettingsFragment.class).b(ctx);
|
||||
a2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ctx.startActivity(a2);
|
||||
@@ -90,47 +106,4 @@ public class ProxyHook {
|
||||
alert.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getAccentColor());
|
||||
alert.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(getAccentColor());
|
||||
}
|
||||
|
||||
public static String linkReplacer(String link) {
|
||||
var vkapi = "api.vk.com";
|
||||
var oauth = "oauth.vk.com";
|
||||
var vkstatic = "static.vk.com";
|
||||
|
||||
var proxyapi = getPrefsValue("proxyapi");
|
||||
var proxyoauth = getPrefsValue("proxyoauth");
|
||||
var proxystatic = getPrefsValue("proxystatic");
|
||||
|
||||
if (!apiproxy() || link.isEmpty()) {
|
||||
return link;
|
||||
}
|
||||
|
||||
if (proxyapi.isEmpty() || proxyoauth.isEmpty() || proxystatic.isEmpty()) {
|
||||
Log.d("VTLite", "Proxy is not set" + " " + proxyapi + " " + proxyoauth + " " + proxystatic);
|
||||
return link;
|
||||
}
|
||||
|
||||
if (link.contains(vkapi)) {
|
||||
return link.replace(proxyapi, vkapi);
|
||||
}
|
||||
|
||||
if (link.contains(oauth)) {
|
||||
return link.replace(proxyoauth, oauth);
|
||||
}
|
||||
|
||||
if (link.contains(vkstatic)) {
|
||||
return link.replace(proxystatic, vkstatic);
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
public static String getAwayPhpCom() {
|
||||
var proxyapi = getPrefsValue("proxyapi");
|
||||
|
||||
if (apiproxy() & !proxyapi.isEmpty()) {
|
||||
return proxyapi;
|
||||
}
|
||||
|
||||
return "m.vk.com";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
|
||||
|
||||
import com.vk.im.engine.models.ProfilesSimpleInfo;
|
||||
import com.vk.im.engine.models.messages.MsgFromUser;
|
||||
import com.vk.im.engine.models.messages.NestedMsg;
|
||||
import com.vk.im.ui.views.ReplyView;
|
||||
|
||||
public class ReplyMsgHook {
|
||||
public static void injectWithDecrypt(ReplyView replyView, MsgFromUser msgFromUser, ProfilesSimpleInfo profilesSimpleInfo, boolean someBool) {
|
||||
msgFromUser.d(decryptMessage(msgFromUser)); // set new value of text in MsgFromUser class
|
||||
replyView.a(msgFromUser, profilesSimpleInfo, someBool);
|
||||
}
|
||||
|
||||
public static void injectWithDecrypt(ReplyView replyView, NestedMsg nestedMsg, ProfilesSimpleInfo profilesSimpleInfo, boolean someBool) {
|
||||
nestedMsg.d(decryptMessage(nestedMsg.f(), nestedMsg.t1())); // set new value of text in NestedMsg class
|
||||
replyView.a(nestedMsg, profilesSimpleInfo, someBool);
|
||||
}
|
||||
|
||||
public static void injectWithDecrypt(ReplyView replyView, NestedMsg nestedMsg, ProfilesSimpleInfo profilesSimpleInfo, boolean someBool, int i) {
|
||||
if ((i & 4) != 0) someBool = false;
|
||||
injectWithDecrypt(replyView, nestedMsg, profilesSimpleInfo, someBool);
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import com.vk.api.internal.MethodCall;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class RequestDumper{
|
||||
public static void addParams(MethodCall.a paramslist, String method, LinkedHashMap params, String apiver) {
|
||||
if (method.contains("execute.getGiftByStickerId")) {
|
||||
apiver = "5.187";
|
||||
public static void addParams(MethodCall.a paramslist, String method, LinkedHashMap<String, String> params, String apiver) {
|
||||
if (method.contains("execute.getGiftByStickerId") ) {
|
||||
apiver = "5.186";
|
||||
}
|
||||
|
||||
if (dev()) {
|
||||
|
||||
@@ -6,16 +6,14 @@ import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
import static ru.vtosters.lite.utils.Preferences.milkshake;
|
||||
import static ru.vtosters.lite.utils.Preferences.superapp;
|
||||
import static ru.vtosters.lite.utils.Preferences.useNewSettings;
|
||||
import static ru.vtosters.lite.utils.Preferences.videonewcatalog;
|
||||
import static ru.vtosters.lite.utils.Preferences.vkme;
|
||||
|
||||
import com.vk.apps.AppsFragment;
|
||||
import com.vk.discover.ThemedFeedFragment;
|
||||
import com.vk.discover.DiscoverFragment;
|
||||
import com.vk.fave.fragments.FaveTabFragment;
|
||||
import com.vk.menu.MenuFragment;
|
||||
import com.vk.menu.SearchMenuFragment;
|
||||
import com.vk.music.fragment.MusicCatalogFragment1;
|
||||
import com.vk.music.fragment.MusicFragment;
|
||||
import com.vk.newsfeed.HomeFragment;
|
||||
import com.vk.newsfeed.NewsfeedFragment;
|
||||
import com.vk.notifications.NotificationsContainerFragment;
|
||||
@@ -47,19 +45,19 @@ public class StartFragmentHook {
|
||||
case "groups":
|
||||
return milkshake() ? CommunitiesCatalogFragment.class : GroupsFragment1.class;
|
||||
case "music":
|
||||
return getBoolValue("musicnewcatalog", true) ? MusicCatalogFragment1.class : MusicFragment.class;
|
||||
return MusicCatalogFragment1.class;
|
||||
case "friends":
|
||||
return FriendsFragment.class;
|
||||
case "photos":
|
||||
return PhotosFragment.class;
|
||||
case "videos":
|
||||
return videonewcatalog() ? VideoCatalogFragment.class : VideosFragment.class;
|
||||
return VideoCatalogFragment.class;
|
||||
case "settings":
|
||||
return useNewSettings();
|
||||
case "apps":
|
||||
return AppsFragment.class;
|
||||
case "discover":
|
||||
return ThemedFeedFragment.class;
|
||||
return DiscoverFragment.class;
|
||||
case "notifications":
|
||||
return NotificationsContainerFragment.class;
|
||||
case "money":
|
||||
|
||||
@@ -4,7 +4,6 @@ import static ru.vtosters.lite.utils.Preferences.feedcache;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
import static ru.vtosters.lite.utils.Preferences.milkshake;
|
||||
import static ru.vtosters.lite.utils.Preferences.miniapps;
|
||||
import static ru.vtosters.lite.utils.Preferences.musicnewcatalog;
|
||||
import static ru.vtosters.lite.utils.Preferences.postsredesign;
|
||||
import static ru.vtosters.lite.utils.Preferences.superapp;
|
||||
|
||||
@@ -133,8 +132,6 @@ public class TogglesHook {
|
||||
case FEATURE_MILKSHAKE_FORCE_DISABLED:
|
||||
case FEATURE_MILKSHAKE_ACTIVATION_DISABLED:
|
||||
return !milkshake();
|
||||
case FEATURE_MUSIC_NEW_CATALOG:
|
||||
return musicnewcatalog();
|
||||
case AB_NEWS_VIDEO_LAYOUT_TEXT:
|
||||
case FEATURE_COMPACT_REPOST:
|
||||
case EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
import static ru.vtosters.lite.utils.Preferences.wbios;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class WritebarHook{
|
||||
public static int getWriteBar(){
|
||||
return AndroidUtils.getIdentifier((wbios() ? "write_bar_i" : "write_bar"), "layout");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package ru.vtosters.lite.hooks.ui;
|
||||
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.*;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.asId;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.getInstance;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.isVkxInstalled;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.lambdaplay;
|
||||
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.isCached;
|
||||
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.isVkxCached;
|
||||
import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected;
|
||||
import static ru.vtosters.lite.utils.Preferences.milkshake;
|
||||
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
import com.vk.music.bottomsheets.a.MusicAction;
|
||||
import com.vk.music.common.MusicPlaybackLaunchContext;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class MusicBottomSheetHook {
|
||||
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
public static ArrayList<MusicAction> hook(ArrayList<MusicAction> actions, MusicTrack musicTrack) {
|
||||
if (musicTrack.F1()) return actions;
|
||||
|
||||
var trackid = asId(musicTrack);
|
||||
|
||||
if (isVkxInstalled()) {
|
||||
actions.add(getPlayInVKXAction());
|
||||
}
|
||||
|
||||
if (isIntegrationEnabled()) {
|
||||
if (isVkxCached(trackid)){
|
||||
actions.add(getRemoveCacheTrackVkxAction());
|
||||
} else {
|
||||
actions.add(addToCacheTrackVkxAction());
|
||||
}
|
||||
}
|
||||
|
||||
if (isNetworkConnected()){
|
||||
actions.add(downloadAsMp3Action());
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
public static ArrayList<MusicAction> hookDownloadBTN(ArrayList<MusicAction> actions, MusicTrack musicTrack) {
|
||||
if (musicTrack.F1()) return actions;
|
||||
|
||||
var trackid = asId(musicTrack);
|
||||
|
||||
if (isCached(trackid)){
|
||||
actions.add(new MusicAction(
|
||||
AndroidUtils.getIdentifier("music_action_toggle_download", "id"),
|
||||
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
|
||||
AndroidUtils.getIdentifier("remove_from_cache", "string"),
|
||||
musicTrack.F1() ? R.color.music_action_button_gray_50_alpha : R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
));
|
||||
} else {
|
||||
actions.add(new MusicAction(
|
||||
AndroidUtils.getIdentifier("music_action_toggle_download", "id"),
|
||||
milkshake() ? R.drawable.ic_download_outline_28 : R.drawable.ic_download_24,
|
||||
AndroidUtils.getIdentifier("add_to_cache", "string"),
|
||||
musicTrack.F1() ? R.color.music_action_button_gray_50_alpha : R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public static ArrayList<MusicAction> hook(ArrayList<MusicAction> actions, Playlist playlist) {
|
||||
if (playlist.D) return actions;
|
||||
|
||||
if (isVkxInstalled()) actions.add(getPlayInVKXAction());
|
||||
|
||||
if (isIntegrationEnabled()){
|
||||
if (isVkxCached(playlist.a, playlist.b)){
|
||||
actions.add(getRemoveCacheTrackVkxAction());
|
||||
} else {
|
||||
actions.add(addToCacheTrackVkxAction());
|
||||
}
|
||||
} else {
|
||||
actions.add(addToCacheTrackAction());
|
||||
}
|
||||
|
||||
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public static boolean injectOnClick(int actionId, MusicTrack track, MusicPlaybackLaunchContext context, Playlist playlist) { // musictrack inj
|
||||
if (actionId == AndroidUtils.getIdentifier("play_in_vkx", "id"))
|
||||
return tryPlayInVKX(track, context, playlist);
|
||||
|
||||
if (actionId == AndroidUtils.getIdentifier("remove_from_cache_vkx", "id")) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.deleteTrackFromCache(track.d, track.e);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (actionId == AndroidUtils.getIdentifier("add_to_cache_vkx", "id")) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.addTrackToCache(track.d, track.e, (track.J != null) ? track.J : "");
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actionId == AndroidUtils.getIdentifier("download_mp3", "id")) {
|
||||
if (isIntegrationEnabled()) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.downloadTrack(track.d, track.e, (track.J != null) ? track.J : "");
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
AudioDownloader.downloadAudio(track);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj
|
||||
if (actionId == AndroidUtils.getIdentifier("play_in_vkx", "id"))
|
||||
return tryPlayInVKX(null, null, playlist);
|
||||
|
||||
if (actionId == AndroidUtils.getIdentifier("add_to_cache", "id")) {
|
||||
executor.submit(() -> AudioDownloader.cachePlaylist(playlist));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actionId == AndroidUtils.getIdentifier("remove_from_cache_vkx", "id")) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.deletePlaylistFromCache(playlist.a, playlist.b);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (actionId == AndroidUtils.getIdentifier("add_to_cache_vkx", "id")) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actionId == AndroidUtils.getIdentifier("download_mp3", "id")) {
|
||||
if (isIntegrationEnabled()) {
|
||||
getInstance().runOnService(service -> {
|
||||
try {
|
||||
service.downloadPlaylist(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
executor.submit(() -> AudioDownloader.downloadPlaylist(playlist));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean tryPlayInVKX(MusicTrack track, MusicPlaybackLaunchContext context, Playlist playlist) {
|
||||
var trackList = new ArrayList<MusicTrack>();
|
||||
|
||||
if (playlist != null) {
|
||||
trackList.addAll(playlist.R);
|
||||
} else {
|
||||
trackList.add(track);
|
||||
}
|
||||
|
||||
LibVKXClientImpl.LibVKXAction action = iLibVkxService -> lambdaplay(trackList, track, iLibVkxService, context);
|
||||
return getInstance().runOnService(action);
|
||||
}
|
||||
|
||||
private static MusicAction getPlayInVKXAction() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("play_in_vkx", "id"),
|
||||
milkshake() ? R.drawable.ic_music_outline_24 : R.drawable.ic_music_24,
|
||||
AndroidUtils.getIdentifier("play_in_vkx", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static MusicAction getRemoveCacheTrackAction() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("remove_from_cache", "id"),
|
||||
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
|
||||
AndroidUtils.getIdentifier("remove_from_cache", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static MusicAction addToCacheTrackAction() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("add_to_cache", "id"),
|
||||
milkshake() ? R.drawable.ic_add_outline_28 : R.drawable.ic_add_24,
|
||||
AndroidUtils.getIdentifier("add_to_cache", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static MusicAction getRemoveCacheTrackVkxAction() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("remove_from_cache_vkx", "id"),
|
||||
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
|
||||
AndroidUtils.getIdentifier("remove_from_cache_vkx", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static MusicAction addToCacheTrackVkxAction() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("add_to_cache_vkx", "id"),
|
||||
milkshake() ? R.drawable.ic_add_outline_28 : R.drawable.ic_add_24,
|
||||
AndroidUtils.getIdentifier("add_to_cache_vkx", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
private static MusicAction downloadAsMp3Action() {
|
||||
return new MusicAction(
|
||||
AndroidUtils.getIdentifier("download_mp3", "id"),
|
||||
milkshake() ? R.drawable.ic_download_outline_28 : R.drawable.ic_download_24,
|
||||
AndroidUtils.getIdentifier("download_mp3", "string"),
|
||||
R.color.caption_gray,
|
||||
R.string.music_talkback_empty,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ru.vtosters.lite.hooks.ui;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import ru.vtosters.lite.utils.Preferences;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class SystemThemeChangerHook {
|
||||
private static int latestTheme = 0;
|
||||
|
||||
public static void onThemeChanged(Configuration configuration) {
|
||||
if (Preferences.systemtheme()) {
|
||||
var theme = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
if (theme == latestTheme) return;
|
||||
|
||||
switch (theme) {
|
||||
case Configuration.UI_MODE_NIGHT_YES:
|
||||
// Night mode is active, we're using dark theme
|
||||
ThemesUtils.applyTheme(ThemesUtils.getDarkTheme());
|
||||
latestTheme = theme;
|
||||
break;
|
||||
case Configuration.UI_MODE_NIGHT_NO:
|
||||
case Configuration.UI_MODE_NIGHT_UNDEFINED:
|
||||
// Night mode is not active, we're using light theme
|
||||
ThemesUtils.applyTheme(ThemesUtils.getLightTheme());
|
||||
latestTheme = theme;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package ru.vtosters.lite.music;
|
||||
|
||||
import android.util.Log;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -8,63 +8,119 @@ import java.io.InputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import java8.util.concurrent.CompletableFuture;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import ru.vtosters.lite.music.converter.ts.FFMpeg;
|
||||
import ru.vtosters.lite.music.converter.ts.TSMerger;
|
||||
import ru.vtosters.lite.music.downloader.ThumbnailDownloader;
|
||||
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class M3UDownloader {
|
||||
public class M3UDownloader implements ITrackDownloader {
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
private static OkHttpClient client = new OkHttpClient();
|
||||
public static M3UDownloader getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public static void execute(String url, File outDir, Callback callback) {
|
||||
public void downloadTrack(MusicTrack track, File outDir, Callback callback, boolean cache) {
|
||||
var request = new Request.a()
|
||||
.b(url)
|
||||
.b(track.D)
|
||||
.a();
|
||||
client.a(request).a(new okhttp3.Callback() {
|
||||
@Override
|
||||
public void a(Call call, IOException e) {
|
||||
|
||||
callback.onFailure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) throws IOException {
|
||||
parse(response.a().g(), outDir, callback);
|
||||
parse(response.a().g(), outDir, callback, track, cache);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void parse(String payload, File outDir, Callback callback) {
|
||||
try {
|
||||
VKM3UParser parser = new VKM3UParser(payload);
|
||||
List<TransportStream> tses = parser.getTransportStreams();
|
||||
callback.onProgress(5);
|
||||
for (TransportStream ts : tses) {
|
||||
String tsURL = parser.getBaseUrl() + ts.getName();
|
||||
Log.d("M3UDownloader", "Downloading " + tsURL);
|
||||
InputStream is = IOUtils.openStream(tsURL);
|
||||
byte[] content;
|
||||
if (TransportStream.METHOD_AES128.equals(ts.getMethod())) {
|
||||
String key = IOUtils.readAllLines(IOUtils.openStream(ts.getKeyURL()));
|
||||
content = IOUtils.decodeStream(is, key);
|
||||
} else {
|
||||
content = IOUtils.readAllBytes(is);
|
||||
}
|
||||
File tsDump = new File(outDir, ts.getName());
|
||||
IOUtils.writeToFile(tsDump, content);
|
||||
callback.onSizeReceived((long) content.length * tses.size(), parser.getHeapSize());
|
||||
callback.onProgress(10 + Math.round(80.0f * tses.indexOf(ts) / tses.size()));
|
||||
private void parse(String payload, File outDir, Callback callback, MusicTrack track, boolean cache) {
|
||||
if (cache)
|
||||
try {
|
||||
ThumbnailDownloader.downloadThumbnails(track);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
VKM3UParser parser = new VKM3UParser(payload);
|
||||
List<TransportStream> tses = parser.getTransportStreams();
|
||||
AtomicInteger progress = new AtomicInteger(0);
|
||||
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
|
||||
var tsesDir = new File(outDir, String.valueOf(track.d));
|
||||
tsesDir.mkdirs();
|
||||
var resultTs = new File(tsesDir, "result.ts");
|
||||
|
||||
String title = null;
|
||||
|
||||
if (track.f != null) {
|
||||
title = track.f;
|
||||
if (!track.g.isEmpty()) {
|
||||
title += " (" + track.g + ")";
|
||||
}
|
||||
} catch (IOException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeyException e) {
|
||||
e.printStackTrace();
|
||||
callback.onFailure();
|
||||
return;
|
||||
}
|
||||
callback.onSuccess();
|
||||
|
||||
var fileName = IOUtils.getValidFileName((cache ? "track" : track.C + " - " + title) + ".mp3");
|
||||
var resultMp3 = new File(outDir, fileName);
|
||||
|
||||
callback.onProgress(5);
|
||||
for (TransportStream ts : tses) {
|
||||
String tsURL = parser.getBaseUrl() + ts.getName();
|
||||
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
InputStream is = IOUtils.openStream(tsURL);
|
||||
byte[] content;
|
||||
if (TransportStream.METHOD_AES128.equals(ts.getMethod())) {
|
||||
String key = IOUtils.readAllLines(IOUtils.openStream(ts.getKeyURL()));
|
||||
content = IOUtils.decodeStream(is, key);
|
||||
} else {
|
||||
content = IOUtils.readAllBytes(is);
|
||||
}
|
||||
File tsDump = new File(tsesDir, ts.getName());
|
||||
IOUtils.writeToFile(tsDump, content);
|
||||
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / tses.size()));
|
||||
callback.onSizeReceived((long) content.length * tses.size(), parser.getHeapSize());
|
||||
} catch (IOException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeyException e) {
|
||||
callback.onFailure();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
var future = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
future.exceptionally(throwable -> {
|
||||
callback.onFailure();
|
||||
return null;
|
||||
})
|
||||
.thenApply(v -> TSMerger.merge(tsesDir, resultTs))
|
||||
.thenApply(mergeResult -> mergeResult && FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track))
|
||||
.thenApply(convertResult -> {
|
||||
if (convertResult)
|
||||
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / tses.size()));
|
||||
else callback.onFailure();
|
||||
return convertResult;
|
||||
})
|
||||
.thenRun(() -> IOUtils.deleteRecursive(tsesDir))
|
||||
.thenRun(callback::onSuccess);
|
||||
}
|
||||
|
||||
// Initialization-on-demand
|
||||
private static class Holder {
|
||||
private static final M3UDownloader INSTANCE = new M3UDownloader();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,76 @@
|
||||
package ru.vtosters.lite.music;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class MP3Downloader {
|
||||
|
||||
private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s©right=https://vtosters.app/";
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
public static void execute(MusicTrack track) {
|
||||
var token = AndroidUtils.getPreferences().getString("vk_admin_token", "");
|
||||
if (token.isEmpty()) {
|
||||
Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
var request = new Request.a()
|
||||
.b(String.format(URL_FORMAT, token, track.y1()))
|
||||
.a();
|
||||
client.a(request).a(new Callback() {
|
||||
@Override
|
||||
public void a(Call call, IOException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) throws IOException {
|
||||
var payload = response.a().g();
|
||||
Log.d("MP3Downloader", payload);
|
||||
try {
|
||||
JSONObject jsonObj = new JSONObject(payload);
|
||||
if (jsonObj.has("response")) {
|
||||
JSONObject responseNode = new JSONObject(payload).getJSONArray("response")
|
||||
.getJSONObject(0);
|
||||
if (responseNode.has("url")) {
|
||||
var url = responseNode.optString("url");
|
||||
Log.d("MP3Downloader", url);
|
||||
if (url != null) {
|
||||
if (url.endsWith(".mp3")) {
|
||||
downloadMP3(track.toString(), url);
|
||||
} else if (VKM3UParser.isM3U8(url)) {
|
||||
AudioDownloader.downloadM3U8(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void downloadMP3(String title, String url) {
|
||||
var downloadManager = AndroidUtils.getGlobalContext().getSystemService(DownloadManager.class);
|
||||
var request = new DownloadManager.Request(Uri.parse(url))
|
||||
.setAllowedOverRoaming(true)
|
||||
.setTitle("Downloading " + title)
|
||||
.setDestinationInExternalFilesDir(AndroidUtils.getGlobalContext(),
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(),
|
||||
title + ".mp3")
|
||||
.setVisibleInDownloadsUi(true);
|
||||
downloadManager.enqueue(request);
|
||||
}
|
||||
}
|
||||
//package ru.vtosters.lite.music;
|
||||
//
|
||||
//import android.text.TextUtils;
|
||||
//import android.util.Log;
|
||||
//import android.widget.Toast;
|
||||
//
|
||||
//import com.vk.dto.music.MusicTrack;
|
||||
//
|
||||
//import org.json.JSONException;
|
||||
//import org.json.JSONObject;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.io.IOException;
|
||||
//
|
||||
//import okhttp3.Call;
|
||||
//import okhttp3.Callback;
|
||||
//import okhttp3.OkHttpClient;
|
||||
//import okhttp3.Request;
|
||||
//import okhttp3.Response;
|
||||
//import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
//import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
||||
//import ru.vtosters.lite.utils.AndroidUtils;
|
||||
//
|
||||
//public class MP3Downloader implements ITrackDownloader {
|
||||
// private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s©right=";
|
||||
//
|
||||
// private static final OkHttpClient client = new OkHttpClient();
|
||||
//
|
||||
// public static void downloadMP3(MusicTrack track) {
|
||||
// var token = AndroidUtils.getPreferences().getString("vk_admin_token", "");
|
||||
// if (token.isEmpty()) {
|
||||
// Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var request = new Request.a()
|
||||
// .b(String.format(URL_FORMAT, token, track.y1()))
|
||||
// .a();
|
||||
// client.a(request).a(new Callback() {
|
||||
// @Override
|
||||
// public void a(Call call, IOException e) {
|
||||
// Log.d("MP3Downloader", e + "");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void a(Call call, Response response) throws IOException {
|
||||
// var payload = response.a().g();
|
||||
// Log.d("MP3Downloader", payload);
|
||||
// try {
|
||||
// JSONObject jsonObj = new JSONObject(payload);
|
||||
// if (!jsonObj.has("response"))
|
||||
// return;
|
||||
// JSONObject responseNode = new JSONObject(payload).getJSONArray("response")
|
||||
// .getJSONObject(0);
|
||||
// if (!responseNode.has("url"))
|
||||
// return;
|
||||
// var url = responseNode.optString("url");
|
||||
// Log.d("MP3Downloader", url);
|
||||
// if (TextUtils.isEmpty(url))
|
||||
// return;
|
||||
// if (VKM3UParser.isM3U8(url)) {
|
||||
// AudioDownloader.downloadM3U8(track);
|
||||
// return;
|
||||
// }
|
||||
// } catch (JSONException e) {
|
||||
// Log.d("MP3Downloader", e + "");
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void downloadTrack(String url, File outDir, ru.vtosters.lite.music.Callback callback) {
|
||||
//
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -6,6 +6,7 @@ import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
import static ru.vtosters.lite.utils.Preferences.autocache;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
@@ -19,11 +20,13 @@ import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
import ru.vtosters.lite.net.NetCall;
|
||||
import ru.vtosters.lite.net.NetCallback;
|
||||
import ru.vtosters.lite.net.NetClient;
|
||||
import ru.vtosters.lite.net.NetRequest;
|
||||
import ru.vtosters.lite.net.NetResponse;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Scrobbler {
|
||||
private static final String key = "5965d63402414776c54c266db0211746";
|
||||
@@ -85,6 +88,8 @@ public class Scrobbler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autocache()) AudioDownloader.cacheTrack(musictrack);
|
||||
|
||||
scrobbleTrack(duration, artist, title, uid);
|
||||
}
|
||||
|
||||
@@ -103,7 +108,7 @@ public class Scrobbler {
|
||||
|
||||
public static void auth(String login, String pass) {
|
||||
if (login == null || pass == null) {
|
||||
sendToast("Логин или пароль не заданы");
|
||||
sendToast(AndroidUtils.getString("lastfm_credentials_empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,7 +124,7 @@ public class Scrobbler {
|
||||
@Override
|
||||
public void onFailure(NetCall call, IOException e) {
|
||||
Log.d("Scrobbler", "Auth failed", e);
|
||||
sendToast("Ошибка входа");
|
||||
sendToast(AndroidUtils.getString("auth_error_vtl"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,7 +160,7 @@ public class Scrobbler {
|
||||
edit().remove("username").apply();
|
||||
edit().remove("sessionKey").apply();
|
||||
scrobbled.clear();
|
||||
sendToast("Вы успешно вышли из учетной записи");
|
||||
sendToast(AndroidUtils.getString("lastfm_logout_success"));
|
||||
}
|
||||
|
||||
public static boolean needToBeScrobble(String uid) {
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getCacheDir;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.*;
|
||||
import static ru.vtosters.lite.utils.IOUtils.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
||||
|
||||
public class CacheDatabaseDelegate {
|
||||
private static final int DB_VERSION = 0x1;
|
||||
private static final String DB_NAME = "vt_lite_cache.db";
|
||||
private static final String TABLE_NAME = "tracks";
|
||||
private static final String COLUMN_ID = "id";
|
||||
private static final String COLUMN_TRACK_ID = "trackId";
|
||||
private static final String COLUMN_ALBUM_ID = "albumId";
|
||||
private static final String COLUMN_TITLE = "title";
|
||||
private static final String COLUMN_SUBTITLE = "subtitle";
|
||||
private static final String COLUMN_ARTIST = "artist";
|
||||
private static final String COLUMN_ALBUM_TITLE = "albumTitle";
|
||||
private static final String COLUMN_EXPLICIT = "explicit";
|
||||
private static final String COLUMN_HAS_ARTWORK = "hasArtwork";
|
||||
private static final String COLUMN_DURATION = "duration";
|
||||
private static DbHelper helperInstance;
|
||||
|
||||
protected static DbHelper getHelper() {
|
||||
if (helperInstance == null) helperInstance = new DbHelper(getGlobalContext());
|
||||
return helperInstance;
|
||||
}
|
||||
|
||||
// You MUST call this method ONLY AFTER TRACK IS DOWNLOADED!
|
||||
public static void insertTrack(MusicTrack track) {
|
||||
try {
|
||||
var json = track.J();
|
||||
var albumNode = json.optJSONObject("album");
|
||||
|
||||
var hasArtwork = false;
|
||||
var albumId = -1;
|
||||
var albumName = "";
|
||||
|
||||
if (albumNode != null) {
|
||||
hasArtwork = albumNode.has("thumb");
|
||||
albumId = albumNode.getInt("id");
|
||||
albumName = albumNode.getString("title");
|
||||
}
|
||||
|
||||
var formula = String.format(
|
||||
"INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES ('%s', %s, '%s', '%s', '%s', '%s', %s, %s, %s)",
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK,
|
||||
track.y1(),
|
||||
albumId,
|
||||
URLEncoder.encode(json.getString("title"), "UTF-8"),
|
||||
URLEncoder.encode(json.optString("subtitle"), "UTF-8"),
|
||||
URLEncoder.encode(json.getString("artist"), "UTF-8"),
|
||||
URLEncoder.encode(albumName, "UTF-8"),
|
||||
json.optInt("is_explicit"),
|
||||
json.getInt("duration"),
|
||||
hasArtwork ? 1 : 0
|
||||
);
|
||||
getHelper().getReadableDatabase().execSQL(formula);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
ToastUtils.a(getString("track_bd_insert_err") + " [" + e.getMessage() + "]", true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void insertTracks(List<MusicTrack> tracks) {
|
||||
for (var track : tracks) insertTrack(track);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
getHelper().getReadableDatabase().execSQL(String.format("DELETE FROM %s", TABLE_NAME));
|
||||
deleteRecursive(getCacheDir());
|
||||
sendToast(getString("cached_tracks_deleted"));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static JSONObject reconstructFromDb(Cursor cursor) {
|
||||
try {
|
||||
var trackId = cursor.getString(getColumnIndex(cursor, COLUMN_TRACK_ID));
|
||||
var splits = trackId.split("_");
|
||||
var id = Integer.parseInt(splits[1]);
|
||||
var ownerId = Integer.parseInt(splits[0]);
|
||||
|
||||
return new JSONObject()
|
||||
.put("url", FileCacheImplementation.getServerPath())
|
||||
.put("id", id)
|
||||
.put("owner_id", ownerId)
|
||||
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_TITLE)), "UTF-8"))
|
||||
.put("subtitle", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_SUBTITLE)), "UTF-8"))
|
||||
.put("artist", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ARTIST)), "UTF-8"))
|
||||
.put("duration", cursor.getInt(getColumnIndex(cursor, COLUMN_DURATION)))
|
||||
.put("is_explicit", cursor.getInt(getColumnIndex(cursor, COLUMN_EXPLICIT)))
|
||||
.put("access_key", 2281488)
|
||||
.putOpt("album", reconstructAlbumFromDb(cursor, trackId));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JSONObject reconstructAlbumFromDb(Cursor cursor, String trackId) {
|
||||
try {
|
||||
if (!FileCacheImplementation.getTrackThumbnail(trackId, 300).exists()
|
||||
&& !FileCacheImplementation.getTrackThumbnail(trackId, 600).exists()
|
||||
&& !FileCacheImplementation.getTrackThumbnail(trackId, 1200).exists()
|
||||
) {
|
||||
var ownerId = Integer.parseInt(trackId.split("_")[1]);
|
||||
return new JSONObject()
|
||||
.put("id", cursor.getInt(getColumnIndex(cursor, COLUMN_ALBUM_ID)))
|
||||
.put("owner_id", ownerId)
|
||||
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ALBUM_TITLE)), "UTF-8"))
|
||||
.putOpt("thumb", reconstructAlbumThumbFromDb(cursor, trackId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JSONObject reconstructAlbumThumbFromDb(Cursor cursor, String trackId) {
|
||||
try {
|
||||
if (cursor.getInt(getColumnIndex(cursor, COLUMN_HAS_ARTWORK)) != 0)
|
||||
new JSONObject()
|
||||
.put("height", 600)
|
||||
.put("width", 600)
|
||||
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
|
||||
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)))
|
||||
.putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 1200)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int getColumnIndex(Cursor cursor, String name) {
|
||||
return cursor.getColumnIndexOrThrow(name);
|
||||
}
|
||||
|
||||
public static boolean hasTracks() {
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var formula = String.format(
|
||||
"SELECT COUNT(%s) FROM %s",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME
|
||||
);
|
||||
var cursor = getHelper().getReadableDatabase().rawQuery(formula, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToFirst();
|
||||
int count = cursor.getInt(0);
|
||||
cursor.close();
|
||||
return count > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks() {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s",
|
||||
TABLE_NAME
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksAsPlaylist() {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s WHERE %s > -1 GROUP BY %s HAVING COUNT(*) > 1 ORDER by id DESC",
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
COLUMN_ALBUM_ID
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksByAlbum(String id) {
|
||||
return getTracks(String.format(
|
||||
"SELECT COUNT(%s) FROM %s WHERE %s = %s",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
id
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksByAlbumOne(String id) {
|
||||
return getTracks(String.format(
|
||||
"SELECT * FROM %s WHERE %s = %s LIMIT 1",
|
||||
TABLE_NAME,
|
||||
COLUMN_ALBUM_ID,
|
||||
id
|
||||
));
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks(String query) {
|
||||
List<MusicTrack> list = new ArrayList<>();
|
||||
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery(query, new String[0])) {
|
||||
while (cursor.moveToNext()) {
|
||||
list.add(new MusicTrack(reconstructFromDb(cursor)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean isCached(String trackId) {
|
||||
var formula = String.format(
|
||||
"SELECT COUNT(%s) FROM %s WHERE %s = '%s'",
|
||||
COLUMN_ID,
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID,
|
||||
trackId
|
||||
);
|
||||
try {
|
||||
Cursor size = getHelper().getReadableDatabase().rawQuery(formula, null);
|
||||
if (size != null) {
|
||||
size.moveToFirst();
|
||||
int count = size.getInt(0);
|
||||
size.close();
|
||||
return count != 0;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isVkxCached(int playlist_id, int owner_id) {
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>(){
|
||||
@Override
|
||||
public Boolean run(ILibVkxService service){
|
||||
try {
|
||||
return service.isPlaylistCached(playlist_id, owner_id);
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean defaultValue(){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isVkxCached(String trackId) {
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>(){
|
||||
@Override
|
||||
public Boolean run(ILibVkxService service){
|
||||
try {
|
||||
var splits = trackId.split("_");
|
||||
var id = Integer.parseInt(splits[1]);
|
||||
var owner = Integer.parseInt(splits[0]);
|
||||
return service.isTrackCached(id, owner);
|
||||
} catch (RemoteException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean defaultValue(){
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void removeTrackFromCache(String trackId) {
|
||||
deleteRecursive(FileCacheImplementation.getTrackFolder(trackId));
|
||||
|
||||
var formula = String.format(
|
||||
"DELETE FROM %s WHERE %s = '%s'",
|
||||
TABLE_NAME,
|
||||
COLUMN_TRACK_ID,
|
||||
trackId
|
||||
);
|
||||
getHelper().getWritableDatabase().execSQL(formula);
|
||||
}
|
||||
|
||||
public static long getTrackCount() {
|
||||
if (!LibVKXClient.isIntegrationEnabled())
|
||||
return DatabaseUtils.queryNumEntries(getHelper().getReadableDatabase(), TABLE_NAME);
|
||||
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<Long>() {
|
||||
@Override
|
||||
public Long run(ILibVkxService service) {
|
||||
try {
|
||||
return (long) service.getCache().size();
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return defaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long defaultValue() {
|
||||
return 0L;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class DbHelper extends SQLiteOpenHelper {
|
||||
public DbHelper(Context context) {
|
||||
super(context, DB_NAME, null, DB_VERSION);
|
||||
}
|
||||
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
var formula = String.format(
|
||||
"CREATE TABLE %s (%s INTEGER PRIMARY KEY AUTOINCREMENT, %s TEXT, %s INTEGER, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER, %s INTEGER, %s INTEGER)",
|
||||
TABLE_NAME,
|
||||
COLUMN_ID, COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK
|
||||
);
|
||||
db.execSQL(formula);
|
||||
}
|
||||
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class FileCacheImplementation {
|
||||
|
||||
public static void clear() {
|
||||
CacheDatabaseDelegate.clear();
|
||||
IOUtils.deleteRecursive(getCacheDir(), false);
|
||||
}
|
||||
|
||||
public static File getCacheDir() {
|
||||
return AndroidUtils.getGlobalContext().getDir("vt_tracks", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static File getTrackFolder(String trackId) {
|
||||
var dir = new File(getCacheDir(), trackId);
|
||||
dir.mkdirs();
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static File getThumbnailsFolder(String trackId) {
|
||||
var dir = new File(getTrackFolder(trackId), "thumbs");
|
||||
dir.mkdirs();
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static File getTrackFile(String trackId) {
|
||||
return new File(getTrackFolder(trackId), "track.mp3");
|
||||
}
|
||||
|
||||
public static boolean isTrackExist(String trackId) {
|
||||
return CacheDatabaseDelegate.isCached(trackId);
|
||||
}
|
||||
|
||||
public static File getTrackFile(MusicTrack track) {
|
||||
return getTrackFile(track.y1());
|
||||
}
|
||||
|
||||
public static File getTrackThumbnail(String trackId, int factor) {
|
||||
return new File(getThumbnailsFolder(trackId), "thumb_" + factor + ".png");
|
||||
}
|
||||
|
||||
public static String getFileUri(File file) {
|
||||
if (!file.exists()) return null;
|
||||
return Uri.fromFile(file).toString();
|
||||
}
|
||||
|
||||
public static String getServerPath() {
|
||||
return "https://vtosters.app";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package ru.vtosters.lite.music.cache.helpers;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
|
||||
public class PlaylistHelper {
|
||||
|
||||
public static Playlist createCachedPlaylistMetadata() {
|
||||
try {
|
||||
var obj = new JSONObject()
|
||||
.put("id", -1)
|
||||
.put("owner_id", AccountManagerUtils.getUserId())
|
||||
.put("type", 0)
|
||||
.put("album_type", "playlist")
|
||||
.put("title", getString("cached_tracks_title"))
|
||||
.put("description", getString("cached_tracks_desc"))
|
||||
.put("count", 0);
|
||||
return new Playlist(obj);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Playlist createCachedPlaylistMetadata(String id) {
|
||||
var track = TracklistHelper.getTracksOne(id).get(0);
|
||||
var albumLink = track.I;
|
||||
var thumb = albumLink.u1();
|
||||
return createAlbum(
|
||||
thumb != null ? thumb.h(600) : "",
|
||||
albumLink.getTitle(),
|
||||
track.C,
|
||||
track.y1(),
|
||||
albumLink.getId()
|
||||
);
|
||||
}
|
||||
|
||||
public static Playlist createAlbum(String photo, String title, String artist, String trackId, int albumId) {
|
||||
try {
|
||||
var jPhoto = new JSONObject();
|
||||
if (!photo.isEmpty())
|
||||
jPhoto.put("height", 600)
|
||||
.put("width", 600)
|
||||
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
|
||||
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)));
|
||||
var obj = new JSONObject()
|
||||
.put("id", -2)
|
||||
.put("owner_id", albumId)
|
||||
.put("type", 1)
|
||||
.put("album_type", "main_only")
|
||||
.put("title", title)
|
||||
.put("description", albumId + "/ " + getString("cached_album_warning"))
|
||||
.put("main_artists", new JSONArray()
|
||||
.put(new JSONObject().put("name", artist)
|
||||
.put("id", "-1")
|
||||
.put("domain", "-1")))
|
||||
.putOpt("photo", jPhoto)
|
||||
.put("count", 0);
|
||||
return new Playlist(obj);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Playlist> getAlbumPlaylists() {
|
||||
List<Playlist> list = new ArrayList<>();
|
||||
for (var track : CacheDatabaseDelegate.getTracksAsPlaylist()) {
|
||||
var albumLink = track.I;
|
||||
var thumb = albumLink.u1();
|
||||
list.add(createAlbum(
|
||||
thumb != null ? thumb.h(600) : "",
|
||||
albumLink.getTitle(),
|
||||
track.C,
|
||||
track.y1(),
|
||||
albumLink.getId()
|
||||
));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package ru.vtosters.lite.music.cache.helpers;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.List;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
|
||||
public class TracklistHelper {
|
||||
|
||||
public static List<MusicTrack> getTracks() {
|
||||
return CacheDatabaseDelegate.getTracks();
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracks(String id) {
|
||||
return CacheDatabaseDelegate.getTracksByAlbum(id);
|
||||
}
|
||||
|
||||
public static List<MusicTrack> getTracksOne(String id) {
|
||||
return CacheDatabaseDelegate.getTracksByAlbumOne(id);
|
||||
}
|
||||
|
||||
public static JSONArray tracksToIds(List<MusicTrack> tracks) {
|
||||
var arr = new JSONArray();
|
||||
for (MusicTrack track : tracks) {
|
||||
arr.put(track.y1());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static JSONArray tracksToJsons(List<MusicTrack> tracks) {
|
||||
var arr = new JSONArray();
|
||||
for (MusicTrack track : tracks) {
|
||||
var json = track.J();
|
||||
var folder = FileCacheImplementation.getThumbnailsFolder(LibVKXClient.asId(track));
|
||||
try {
|
||||
addCachedThumbnails(json, folder);
|
||||
} catch (JSONException | MalformedURLException e) {
|
||||
Log.d("TracklistHelper", e+"");
|
||||
}
|
||||
arr.put(json);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static void addCachedThumbnails(JSONObject target, File thumbnailsDir)
|
||||
throws JSONException, MalformedURLException {
|
||||
var sizes = new JSONArray();
|
||||
var files = thumbnailsDir.listFiles();
|
||||
if (files == null || files.length == 0)
|
||||
return;
|
||||
for (var file : files) {
|
||||
var filename = file.getName();
|
||||
var width = Integer.parseInt(filename.substring(6, filename.length() - 4));
|
||||
var src = file.toURI().toURL().toString();
|
||||
Log.d("tracksToJsons", src);
|
||||
var size = new JSONObject()
|
||||
.put("width", width)
|
||||
.put("src", src);
|
||||
sizes.put(size);
|
||||
}
|
||||
target.put("album", new JSONObject().put("thumb", new JSONObject().put("sizes", sizes)));
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package ru.vtosters.lite.music.cache.injectors;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.api.audio.AudioGetPlaylist;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableOnSubscribe;
|
||||
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||
import ru.vtosters.lite.utils.NetworkUtils;
|
||||
|
||||
public class PlaylistInjector {
|
||||
|
||||
public final static String CHANNEL_NAME = "VTCH";
|
||||
|
||||
public static void injectDownloadPlaylist(Playlist playlist) {
|
||||
AudioDownloader.downloadPlaylist(playlist);
|
||||
}
|
||||
|
||||
public static boolean eligibleForOfflineCaching() {
|
||||
Log.d("PlaylistInjector", "eligibleForOfflineCaching");
|
||||
|
||||
return !NetworkUtils.isNetworkConnected() && CacheDatabaseDelegate.hasTracks();
|
||||
}
|
||||
|
||||
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
|
||||
Log.d("PlaylistInjector", "injectGetPlaylist");
|
||||
|
||||
try {
|
||||
if (!eligibleForOfflineCaching())
|
||||
return null;
|
||||
var requestArgs = audioGetPlaylist.b();
|
||||
|
||||
var id = requestArgs.get("id");
|
||||
var ownerId = requestArgs.get("owner_id");
|
||||
var accessKey = requestArgs.get("access_key");
|
||||
boolean isVirtualPlaylist = accessKey != null && (accessKey.equals("cache"));
|
||||
boolean isAlbumVirtualPlaylist = accessKey != null && (accessKey.equals("cacheAlbum"));
|
||||
|
||||
Log.d("PlaylistInjector", "id = " + id + " / owner = " + ownerId + " / access = " + accessKey + " / " + isVirtualPlaylist + " + " + isAlbumVirtualPlaylist);
|
||||
|
||||
if (TextUtils.isEmpty(id) || (!isVirtualPlaylist && !isAlbumVirtualPlaylist))
|
||||
return null;
|
||||
|
||||
return Observable.a((ObservableOnSubscribe<AudioGetPlaylist.c>) observableEmitter -> {
|
||||
AudioGetPlaylist.c response = new AudioGetPlaylist.c();
|
||||
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
LibVKXClient.getInstance().runOnService((service) -> {
|
||||
try {
|
||||
List<String> cache;
|
||||
ArrayList<MusicTrack> tracks = new ArrayList<>();
|
||||
|
||||
if (isAlbumVirtualPlaylist) {
|
||||
response.b = new Playlist(new JSONObject(service.getPlaylistDefJson(id, ownerId)));
|
||||
cache = service.getTracksInPlaylist(id, ownerId);
|
||||
} else {
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
||||
cache = service.getCache();
|
||||
}
|
||||
|
||||
for (String json : cache) {
|
||||
tracks.add(new MusicTrack(new JSONObject(json)));
|
||||
}
|
||||
|
||||
response.c = tracks;
|
||||
} catch (RemoteException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
response.c = new ArrayList<>();
|
||||
}
|
||||
observableEmitter.b(response);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVirtualPlaylist) {
|
||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks();
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata();
|
||||
} else {
|
||||
response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks(requestArgs.get("owner_id"));
|
||||
response.b = PlaylistHelper.createCachedPlaylistMetadata(requestArgs.get("owner_id"));
|
||||
}
|
||||
|
||||
observableEmitter.b(response);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
package ru.vtosters.lite.music.cache.injectors;
|
||||
|
||||
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.hasTracks;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.catalog2.core.CatalogParser;
|
||||
import com.vk.catalog2.core.api.dto.CatalogResponse;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableOnSubscribe;
|
||||
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
|
||||
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.NetworkUtils;
|
||||
|
||||
public class TracklistInjector {
|
||||
|
||||
public static boolean eligibleForOfflineCaching() {
|
||||
return !NetworkUtils.isNetworkConnected();
|
||||
}
|
||||
|
||||
public static Observable<CatalogResponse> createOfflineRx(CatalogParser parser) {
|
||||
Log.d("TracklistInjector", "createOfflineRx");
|
||||
|
||||
return Observable.a((ObservableOnSubscribe<CatalogResponse>) observableEmitter -> {
|
||||
if (LibVKXClient.isIntegrationEnabled()) {
|
||||
LibVKXClient.getInstance().runOnService((service) -> {
|
||||
try {
|
||||
try {
|
||||
Log.d("TracklistInjector", "Using vkx cache catalog");
|
||||
observableEmitter.b(parser.c(new JSONObject(service.getCacheCatalog())));
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
List<MusicTrack> tracks = new ArrayList<>();
|
||||
List<String> cache = service.getCache();
|
||||
|
||||
for (String json : cache) {
|
||||
Log.d("TracklistInjector", "added " + json);
|
||||
tracks.add(new MusicTrack(new JSONObject(json)));
|
||||
}
|
||||
|
||||
Log.d("TracklistInjector", "Using own cache catalog from vkx");
|
||||
observableEmitter.b(parser.c(createVirtualCatalog(tracks)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!hasTracks()){
|
||||
observableEmitter.b(parser.c(getEmptyCatalog()));
|
||||
} else {
|
||||
observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks())));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static JSONObject getEmptyCatalog() throws JSONException{
|
||||
return new JSONObject("{\"catalog\":{\"default_section\":\"cache\",\"sections\":[{\"id\":\"cache\",\"title\":\"" + getString("cached_content_title") + "\",\"blocks\":[{\"data_type\":\"placeholder\",\"id\":\"0\",\"layout\":{\"name\":\"placeholder_big\"},\"placeholder_ids\":[\"_synth_trackEmpty\"]}]}]},\"audios\":[],\"playlists\":[],\"placeholders\":[{\"id\":\"_synth_trackEmpty\",\"title\":\"" + getString("cached_content_empty") + "\",\"text\":\"" + getString("cached_content_empty_desc") + "\",\"buttons\":[]}]}");
|
||||
}
|
||||
|
||||
public static void injectIntoExistingCatalog(JSONObject catalogNode) {
|
||||
Log.d("TracklistInjector", "injectIntoExistingCatalog");
|
||||
|
||||
if (!hasTracks()) return;
|
||||
try {
|
||||
var sectionsNode = catalogNode.getJSONObject("catalog")
|
||||
.getJSONArray("sections");
|
||||
var firstSection = sectionsNode.getJSONObject(0);
|
||||
if (!firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all"))
|
||||
return;
|
||||
|
||||
var foundPlaylistBlock = false;
|
||||
var suitableIndex = 0;
|
||||
|
||||
var blocks = firstSection.getJSONArray("blocks");
|
||||
for (int i = 0; i < blocks.length(); i++) {
|
||||
var block = blocks.getJSONObject(i);
|
||||
var dataType = block.getString("data_type");
|
||||
if ("music_audios".equals(dataType)) {
|
||||
suitableIndex = i - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!"music_playlists".equals(dataType))
|
||||
continue;
|
||||
|
||||
var playlistIds = block.getJSONArray("playlists_ids");
|
||||
var newPlaylistIds = new JSONArray()
|
||||
.put(AccountManagerUtils.getUserId() + "_-1");
|
||||
|
||||
for (int j = 0; j < playlistIds.length(); j++)
|
||||
newPlaylistIds.put(playlistIds.get(j));
|
||||
|
||||
block.put("playlists_ids", newPlaylistIds);
|
||||
|
||||
// need test
|
||||
blocks.remove(i);
|
||||
blocks.put(i, block);
|
||||
|
||||
foundPlaylistBlock = true;
|
||||
}
|
||||
|
||||
if (foundPlaylistBlock) {
|
||||
var playlists = catalogNode.getJSONArray("playlists")
|
||||
.put(0, PlaylistHelper.createCachedPlaylistMetadata().J());
|
||||
catalogNode.put("playlists", playlists);
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = new JSONArray()
|
||||
.put(0, AccountManagerUtils.getUserId() + "_-1");
|
||||
|
||||
var playlistsBlock = new JSONObject()
|
||||
.put("id", getRandomId())
|
||||
.put("data_type", "music_playlists")
|
||||
.put("url", "synth:cache:playlists")
|
||||
.put("playlists_ids", ids);
|
||||
|
||||
var layout = new JSONObject()
|
||||
.put("name", "large_slider")
|
||||
.put("owner_id", AccountManagerUtils.getUserId());
|
||||
|
||||
playlistsBlock.put("layout", layout);
|
||||
|
||||
blocks.put(suitableIndex, playlistsBlock);
|
||||
|
||||
firstSection.put("blocks", blocks);
|
||||
|
||||
var playlists = new JSONArray()
|
||||
.put(0, PlaylistHelper.createCachedPlaylistMetadata().J());
|
||||
catalogNode.put("playlists", playlists);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static JSONObject createVirtualCatalog(List<MusicTrack> tracks)
|
||||
throws JSONException {
|
||||
Log.d("TracklistInjector", "createVirtualCatalog");
|
||||
|
||||
var clickAction = new JSONObject()
|
||||
.put("type", "open_url")
|
||||
.put("target", "internal")
|
||||
.put("url", "https://vkx.app/index.html?app=vt_ctg");
|
||||
|
||||
var catalogBanner = new JSONObject()
|
||||
.put("id", 1234)
|
||||
.put("image_mode", "cover")
|
||||
.put("title", getString("vkx_integration_title"))
|
||||
.put("text", getString("vkx_integration_desc"))
|
||||
.put("click_action", new JSONObject().put("action", clickAction));
|
||||
var catalogBanners = new JSONArray()
|
||||
.put(catalogBanner);
|
||||
|
||||
var audiosBlock = new JSONObject()
|
||||
.put("id", getRandomId())
|
||||
.put("data_type", "music_audios")
|
||||
.put("url", "synth:cache:list")
|
||||
.put("audios_ids", TracklistHelper.tracksToIds(tracks));
|
||||
|
||||
var layout = new JSONObject()
|
||||
.put("name", "list")
|
||||
.put("owner_id", AccountManagerUtils.getUserId());
|
||||
|
||||
audiosBlock.put("layout", layout);
|
||||
|
||||
var blocks = new JSONArray()
|
||||
.put(audiosBlock);
|
||||
|
||||
// var vkxPostirony = new JSONObject()
|
||||
// .put("id", getRandomId())
|
||||
// .put("data_type", "catalog_banners")
|
||||
// .put("url", "synth:cache:vkx")
|
||||
// .put("catalog_banner_ids", new JSONArray().put(1234));
|
||||
//
|
||||
// var vkxlayout = new JSONObject()
|
||||
// .put("name", "large_list")
|
||||
// .put("owner_id", Globals.getUserId())
|
||||
// .put("infinite_repeat", true);
|
||||
//
|
||||
// vkxPostirony.put("layout", vkxlayout);
|
||||
//
|
||||
// if (eligibleForVkxAd())
|
||||
// blocks.put(vkxPostirony);
|
||||
var randomId = getRandomId();
|
||||
|
||||
var section = new JSONObject()
|
||||
.put("id", randomId)
|
||||
.put("title", getString("saved_tracks_title"))
|
||||
.put("url", "synth:cache")
|
||||
.put("blocks", blocks);
|
||||
|
||||
var sections = new JSONArray()
|
||||
.put(section);
|
||||
|
||||
var catalog = new JSONObject()
|
||||
.put("sections", sections)
|
||||
.put("default_section", randomId);
|
||||
|
||||
return new JSONObject()
|
||||
.put("catalog", catalog)
|
||||
.put("audios", TracklistHelper.tracksToJsons(tracks))
|
||||
.put("catalog_banners", catalogBanners);
|
||||
}
|
||||
|
||||
private static String getRandomId() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package ru.vtosters.lite.music.callback;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
|
||||
public class MusicCallbackBuilder {
|
||||
private static NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager();
|
||||
|
||||
public static Callback buildOneTrackCallback(int notificationId, NotificationCompat.Builder notification) {
|
||||
return new Callback() {
|
||||
long startTime = 0;
|
||||
long elapsedTime = 0;
|
||||
|
||||
@Override
|
||||
public void onProgress(int progress) {
|
||||
if (elapsedTime > 1000) {
|
||||
notification.setProgress(100, progress, false);
|
||||
notificationManager.notify(notificationId, notification.build());
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
elapsedTime = 0;
|
||||
} else {
|
||||
elapsedTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
try {
|
||||
notification
|
||||
.setContentText(getString("player_download_finished"))
|
||||
.setProgress(0, 0, false)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done);
|
||||
notificationManager.notify(notificationId, notification.build());
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.e("AudioDownloader", "native libs");
|
||||
Log.e("AudioDownloader", e.getMessage());
|
||||
onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
notification.setContentText(getString("load_audio_error")).setProgress(0, 0, false);
|
||||
notificationManager.notify(notificationId, notification.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeReceived(long size, long header) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Callback buildPlaylistCallback(int playlistSize, NotificationCompat.Builder notification, int notificationId) {
|
||||
return new Callback() {
|
||||
@Override
|
||||
public void onProgress(int progress) {
|
||||
if (progress == playlistSize) {
|
||||
onSuccess();
|
||||
} else {
|
||||
notification.setContentText(String.format(getString("playlist_download_notf"), progress, playlistSize));
|
||||
notification.setProgress(playlistSize, progress, false);
|
||||
}
|
||||
notificationManager.notify(notificationId, notification.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
notification.setContentText(getString("player_download_finished"));
|
||||
notification.setProgress(0, 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
notification.setContentText(getString("load_audio_error")).setProgress(0, 0, false);
|
||||
notificationManager.notify(notificationId, notification.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeReceived(long size, long header) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package ru.vtosters.lite.music.converter.playlist;
|
||||
|
||||
import static ru.vtosters.lite.hooks.DateHook.getLocale;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
|
||||
import com.vk.core.network.Network;
|
||||
import com.vk.core.util.DeviceIdProvider;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.dto.music.Playlist;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java8.util.concurrent.CompletableFuture;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
/**
|
||||
* stuff for converting playlists DTO
|
||||
*/
|
||||
public class PlaylistConverter {
|
||||
public static List<MusicTrack> getPlaylist(Playlist oldPlaylist) {
|
||||
var tracks = requestPlaylist(0, oldPlaylist.b, oldPlaylist.Q, oldPlaylist.a);
|
||||
return tracks.stream().filter(track -> !track.D.isEmpty()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<MusicTrack> requestPlaylist(int offset, int ownerId, String accessKey, int id) {
|
||||
var audioCount = 1000;
|
||||
var requestUrl = "https://" + getApi() + "/method/execute.getPlaylist" +
|
||||
"?v=5.119" +
|
||||
"&https=1" +
|
||||
"&audio_count=" + audioCount +
|
||||
"&need_playlist=1" +
|
||||
"&device_id=" + DeviceIdProvider.d(AndroidUtils.getGlobalContext()) +
|
||||
"&owner_id=" + ownerId +
|
||||
"&access_key=" + accessKey +
|
||||
"&func_v=5" +
|
||||
"&id=" + id +
|
||||
"&lang=" + getLocale() +
|
||||
"&audio_offset=" + offset +
|
||||
"&need_owner=1" +
|
||||
"&access_token=" + AccountManagerUtils.getUserToken();
|
||||
|
||||
var request = new Request.a()
|
||||
.b(requestUrl)
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||
|
||||
try {
|
||||
var response = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return new OkHttpClient().a(request).execute().a().g();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
).get();
|
||||
var jsonObj = new JSONObject(response);
|
||||
if (!jsonObj.has("response"))
|
||||
return null;
|
||||
var playlist = new JSONObject(response).getJSONObject("response").getJSONArray("audios");
|
||||
var tracks = new ArrayList<MusicTrack>();
|
||||
for (int i = 0; i < playlist.length(); i++) {
|
||||
var track = new MusicTrack(playlist.getJSONObject(i));
|
||||
tracks.add(track);
|
||||
}
|
||||
|
||||
if (tracks.size() == audioCount) {
|
||||
tracks.addAll(requestPlaylist(offset + audioCount, ownerId, accessKey, id));
|
||||
}
|
||||
|
||||
return tracks;
|
||||
} catch (JSONException | ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+59
-38
@@ -1,21 +1,78 @@
|
||||
package ru.vtosters.lite.music;
|
||||
package ru.vtosters.lite.music.converter.ts;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.arthenica.ffmpegkit.FFmpegKit;
|
||||
import com.arthenica.ffmpegkit.ReturnCode;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.dto.music.Artist;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class FFMpeg {
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
/**
|
||||
* @param in Merged .ts file via {@link ru.vtosters.lite.music.converter.ts.TSMerger}
|
||||
* @param out Output .mp3 file
|
||||
* @param track Track to be converted
|
||||
* @return True if success, else false
|
||||
*/
|
||||
public static boolean convert(File in, String out, MusicTrack track) {
|
||||
checkFFMpegLibs();
|
||||
|
||||
|
||||
// convert .ts file to .mp3 file via ffmpeg
|
||||
var sb = new StringBuilder();
|
||||
sb.append("-y -i \"").append(in.getAbsolutePath()).append("\"");
|
||||
sb.append(" -map 0 -dn ");
|
||||
//sb.append(" -loglevel error");
|
||||
sb.append(" -hide_banner");
|
||||
sb.append(" -write_id3v2 1");
|
||||
|
||||
String title = null;
|
||||
|
||||
if (track.f != null) {
|
||||
title = track.f;
|
||||
if (!track.g.isEmpty()) {
|
||||
title += " (" + track.g + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (title != null) {
|
||||
sb.append(" -metadata title=\"").append(IOUtils.getValidFileName(title)).append("\"");
|
||||
}
|
||||
|
||||
if (track.L != null) {
|
||||
sb.append(" -metadata artist=\"").append(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))).append("\"");
|
||||
} else if (track.C != null) {
|
||||
sb.append(" -metadata artist=\"").append(track.C).append("\"");
|
||||
}
|
||||
if (track.I != null) {
|
||||
sb.append(" -metadata album=\"").append(track.I.getTitle()).append("\"");
|
||||
}
|
||||
|
||||
sb.append(" -c copy \"").append(out).append("\"");
|
||||
|
||||
var command = sb.toString();
|
||||
|
||||
var session = FFmpegKit.execute(command);
|
||||
var rc = session.getReturnCode();
|
||||
|
||||
if (ReturnCode.isSuccess(rc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.e("FFMpeg", "FFmpegKit failed with return code " + rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void copyAssets() {
|
||||
try {
|
||||
@@ -41,44 +98,8 @@ public class FFMpeg {
|
||||
private static void checkFFMpegLibs() {
|
||||
var lib = new File(AndroidUtils.getGlobalContext().getFilesDir(), "libffmpegkit_abidetect.so");
|
||||
if (!lib.exists()) {
|
||||
ToastUtils.a("Копирование библиотеки ffmpegkit");
|
||||
ToastUtils.a(AndroidUtils.getString("ffmpegkit_copy"));
|
||||
copyAssets();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean convert(String in, String out) {
|
||||
checkFFMpegLibs();
|
||||
|
||||
// write the list of all .ts files to a txt file in "in" directory
|
||||
var list = new File(in);
|
||||
var listFile = new File(list, "list.txt");
|
||||
try {
|
||||
listFile.createNewFile();
|
||||
var writer = new java.io.FileWriter(listFile);
|
||||
for (var file : list.listFiles()) {
|
||||
if (file.getName().endsWith(".ts")) {
|
||||
writer.write("file " + file.getName() + "\n");
|
||||
}
|
||||
}
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// concatenate .ts files to a single .ts file
|
||||
FFmpegKit.execute("-f concat -safe 0 -i " + in + "/list.txt -c copy " + in + "/all.ts");
|
||||
|
||||
// convert .ts file to .mp3 file via ffmpeg
|
||||
var session = FFmpegKit.execute("-i " + in + "/all.ts -f mp3 -acodec mp3 -q:a 0 -y '" + out + "'");
|
||||
var rc = session.getReturnCode();
|
||||
|
||||
if (ReturnCode.isSuccess(rc)) {
|
||||
// remove temp files
|
||||
IOUtils.deleteRecursive(new File(in));
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.e("FFMpeg", "FFmpegKit failed with return code " + rc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ru.vtosters.lite.music.converter.ts;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class TSMerger {
|
||||
/**
|
||||
* Merge multiple .ts files into one .ts file
|
||||
* @param in Directory with .ts files
|
||||
* @return True if success, else false
|
||||
*/
|
||||
public static boolean merge(File in, File out) {
|
||||
var tsesList = in.listFiles();
|
||||
|
||||
if (tsesList == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var mergedByteArrayStream = new ByteArrayOutputStream();
|
||||
|
||||
Arrays.sort(tsesList, (o1, o2) -> {
|
||||
var index1 = Integer.parseInt(o1.getName().split("-")[1]);
|
||||
var index2 = Integer.parseInt(o2.getName().split("-")[1]);
|
||||
return index1 - index2;
|
||||
});
|
||||
|
||||
for (var file : tsesList) {
|
||||
if (file.getName().endsWith(".ts")) {
|
||||
try {
|
||||
mergedByteArrayStream.write(IOUtils.readAllBytes(file));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
try {
|
||||
Files.write(out.toPath(), mergedByteArrayStream.toByteArray());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
try (FileOutputStream fos = new FileOutputStream(out)) {
|
||||
fos.write(mergedByteArrayStream.toByteArray());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
import static ru.vtosters.lite.hooks.DateHook.getLocale;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
|
||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.core.network.Network;
|
||||
import com.vk.core.util.DeviceIdProvider;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import java8.util.concurrent.CompletableFuture;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class AudioGet{
|
||||
public static List<MusicTrack> getAudios(){
|
||||
var tracks = requestAudios(0);
|
||||
return tracks != null ? tracks.stream().filter(track -> !track.D.isEmpty()).collect(Collectors.toList()) : null;
|
||||
}
|
||||
|
||||
private static List<MusicTrack> requestAudios(int offset){
|
||||
var audioCount = 1000;
|
||||
var requestUrl = "https://" + getApi() + "/method/audio.get" +
|
||||
"?v=5.119" +
|
||||
"&https=1" +
|
||||
"&count=" + audioCount +
|
||||
"&device_id=" + DeviceIdProvider.d(AndroidUtils.getGlobalContext()) +
|
||||
"&owner_id=" + getUserId() +
|
||||
"&access_key=" + getUserToken() +
|
||||
"&lang=" + getLocale() +
|
||||
"&offset=" + offset +
|
||||
"&access_token=" + getUserToken();
|
||||
|
||||
var request = new Request.a()
|
||||
.b(requestUrl)
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||
|
||||
try {
|
||||
var response = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return new OkHttpClient().a(request).execute().a().g();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
).get();
|
||||
var jsonObj = new JSONObject(response);
|
||||
if (!jsonObj.has("response"))
|
||||
return null;
|
||||
Log.d("AudioGet", jsonObj.toString());
|
||||
var audios = new JSONObject(response).getJSONObject("response").getJSONArray("items");
|
||||
var tracks = new ArrayList<MusicTrack>();
|
||||
for (int i = 0; i < audios.length(); i++) {
|
||||
var track = new MusicTrack(audios.getJSONObject(i));
|
||||
tracks.add(track);
|
||||
}
|
||||
|
||||
if (tracks.size() == audioCount) {
|
||||
tracks.addAll(requestAudios(offset + audioCount));
|
||||
}
|
||||
|
||||
return tracks;
|
||||
} catch (JSONException | ExecutionException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
|
||||
public class PlaylistDownloader {
|
||||
private static NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager();
|
||||
|
||||
public static void downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) {
|
||||
File outDir = new File(path);
|
||||
if (!outDir.exists())
|
||||
if (outDir.mkdirs())
|
||||
Log.v("PlaylistDownloader", "Directory created");
|
||||
else
|
||||
Log.e("PlaylistDownloader", "Directory creation failed");
|
||||
|
||||
var downloadedTracks = new AtomicInteger(0);
|
||||
|
||||
for (MusicTrack musicTrack : playlist) {
|
||||
TrackDownloader.downloadTrack(musicTrack, path, new Callback() {
|
||||
@Override
|
||||
public void onProgress(int progress) {}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
var currentProgress = downloadedTracks.incrementAndGet();
|
||||
callback.onProgress(currentProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
callback.onFailure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeReceived(long size, long header) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void cachePlaylist(List<MusicTrack> playlist, Callback callback) {
|
||||
var downloadedTracks = new AtomicInteger(0);
|
||||
|
||||
for (MusicTrack musicTrack : playlist) {
|
||||
TrackDownloader.cacheTrack(musicTrack, new Callback() {
|
||||
@Override
|
||||
public void onProgress(int progress) {}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
var currentProgress = downloadedTracks.incrementAndGet();
|
||||
callback.onProgress(currentProgress);
|
||||
CacheDatabaseDelegate.insertTrack(musicTrack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
callback.onFailure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeReceived(long size, long header) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
public class ThumbnailDownloader {
|
||||
|
||||
public static void downloadThumbnails(MusicTrack track)
|
||||
throws IOException {
|
||||
var obj = track.J();
|
||||
var trackId = LibVKXClient.asId(track);
|
||||
|
||||
if (!obj.has("album")) return;
|
||||
var album = obj.optJSONObject("album");
|
||||
|
||||
if (!album.has("thumb")) return;
|
||||
var thumb = album.optJSONObject("thumb");
|
||||
|
||||
if (thumb.has("sizes")) {
|
||||
var sizes = thumb.optJSONArray("sizes");
|
||||
for (int i = 0; i < sizes.length(); i++) {
|
||||
var size = sizes.optJSONObject(i);
|
||||
var width = size.optInt("width");
|
||||
var src = size.optString("src");
|
||||
downloadThumbnail(src, width, trackId);
|
||||
}
|
||||
} else {
|
||||
var names = thumb.names();
|
||||
for (int i = 0; i < names.length(); i++) {
|
||||
var name = names.optString(i);
|
||||
downloadThumbnail(thumb.optString(name), Integer.parseInt(name.substring(6)), trackId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadThumbnail(String url, int res, String trackId)
|
||||
throws IOException {
|
||||
var data = IOUtils.readAllBytes(new URL(url).openStream());
|
||||
IOUtils.writeToFile(FileCacheImplementation.getTrackThumbnail(trackId, res), data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
import ru.vtosters.lite.music.M3UDownloader;
|
||||
import ru.vtosters.lite.music.cache.FileCacheImplementation;
|
||||
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
|
||||
|
||||
public class TrackDownloader {
|
||||
public static void downloadTrack(MusicTrack track, String path, Callback callback) {
|
||||
download(track, path, callback, M3UDownloader.getInstance(), false);
|
||||
}
|
||||
|
||||
public static void cacheTrack(MusicTrack track, Callback callback) {
|
||||
var path = FileCacheImplementation.getTrackFolder(LibVKXClient.asId(track)).getAbsolutePath();
|
||||
download(track, path, callback, M3UDownloader.getInstance(), true);
|
||||
}
|
||||
|
||||
private static void download(MusicTrack track, String path, Callback callback, ITrackDownloader downloader, boolean cache) {
|
||||
File outDir = new File(path);
|
||||
if (!outDir.exists())
|
||||
if (outDir.mkdir())
|
||||
Log.v("TrackDownloader", "Directory created");
|
||||
else
|
||||
Log.e("TrackDownloader", "Directory creation failed");
|
||||
downloader.downloadTrack(track, outDir, callback, cache);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.vtosters.lite.music.interfaces;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
|
||||
/**
|
||||
* Interface for track downloaders
|
||||
*/
|
||||
public interface ITrackDownloader {
|
||||
/**
|
||||
* Download track
|
||||
* @param track Music track
|
||||
* @param outDir Directory where to download the track
|
||||
* @param callback Callback to call when download status changes
|
||||
*/
|
||||
void downloadTrack(MusicTrack track, File outDir, Callback callback, boolean cache);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ru.vtosters.lite.music.notification;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class MusicNotificationBuilder {
|
||||
private static NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager();
|
||||
|
||||
public static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) {
|
||||
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setContentTitle(getString("audio_downloading"))
|
||||
.setContentText(track.toString())
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(true)
|
||||
.setProgress(100, 0, false);
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
public static NotificationCompat.Builder buildPlaylistDownloadNotification(String playlistName, int id) {
|
||||
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setContentTitle(getString("playlist_downloading") + " «" + playlistName + "»")
|
||||
.setContentText(playlistName)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(true)
|
||||
.setProgress(100, 0, false);
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
public static NotificationCompat.Builder buildAllAudiosDownloadNotification(int id) {
|
||||
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setContentTitle(getString("notf_audio_dl_user") + " " + AccountManagerUtils.getUsername())
|
||||
.setContentText(getString("all_audio"))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setAutoCancel(true)
|
||||
.setProgress(100, 0, false);
|
||||
notificationManager.notify(id, notificationBuilder.build());
|
||||
return notificationBuilder;
|
||||
}
|
||||
|
||||
public static void notifySavingToCache(MusicTrack track) {
|
||||
Notification.Builder builder;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder = new Notification.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID);
|
||||
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
} else builder = new Notification.Builder(AndroidUtils.getGlobalContext());
|
||||
|
||||
builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28)
|
||||
.setShowWhen(false)
|
||||
.setContentTitle(getString("cached_track") + ": " + track.toString());
|
||||
|
||||
notificationManager.notify(track.y1().hashCode(), builder.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ru.vtosters.lite.proxy;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
|
||||
public class GithubList{
|
||||
private static final OkHttpClient mClient = new OkHttpClient();
|
||||
|
||||
public static ArrayList<String> getProxy(String url, String proxyname) throws IOException{
|
||||
Request link = new Request.a()
|
||||
.b(url)
|
||||
.a();
|
||||
|
||||
var response = mClient.a(link).execute().a().g();
|
||||
|
||||
if (!response.contains(":")) {
|
||||
Log.d(proxyname, "No proxy found");
|
||||
return null;
|
||||
}
|
||||
|
||||
Log.d(proxyname, response);
|
||||
|
||||
return new ArrayList<>(Arrays.asList(response.split("\n")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package ru.vtosters.lite.proxy;
|
||||
|
||||
import static java.lang.System.clearProperty;
|
||||
import static ru.vtosters.lite.proxy.api.VikaMobile.getApiHost;
|
||||
import static ru.vtosters.lite.proxy.api.VikaMobile.getOauthHost;
|
||||
import static ru.vtosters.lite.proxy.api.VikaMobile.getStaticHost;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import ru.vtosters.lite.proxy.http.CustomHttp;
|
||||
import ru.vtosters.lite.proxy.https.CustomHttps;
|
||||
import ru.vtosters.lite.proxy.socks.CustomSocks;
|
||||
import ru.vtosters.lite.proxy.socks.Zaborona;
|
||||
|
||||
public class ProxyUtils{
|
||||
public static String getApi(){
|
||||
var proxyapi = getPrefsValue("proxyapi");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
proxyapi = getApiHost();
|
||||
}
|
||||
|
||||
if (isAnyProxyEnabled() & !proxyapi.isEmpty()) {
|
||||
return proxyapi;
|
||||
}
|
||||
|
||||
return "api.vk.com";
|
||||
}
|
||||
|
||||
public static String getOauth(){
|
||||
var oauth = getPrefsValue("proxyoauth");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
oauth = getOauthHost();
|
||||
}
|
||||
|
||||
if (isAnyProxyEnabled() & !oauth.isEmpty()) {
|
||||
return oauth;
|
||||
}
|
||||
|
||||
return "oauth.vk.com";
|
||||
}
|
||||
|
||||
public static String getStatic(){
|
||||
var staticapi = getPrefsValue("proxystatic");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
staticapi = getStaticHost();
|
||||
}
|
||||
|
||||
if (isAnyProxyEnabled() & !staticapi.isEmpty()) {
|
||||
return staticapi;
|
||||
}
|
||||
|
||||
return "static.vk.com";
|
||||
}
|
||||
|
||||
public static Boolean isAnyProxyEnabled(){
|
||||
return isApiProxyEnabled() || isVikaProxyEnabled();
|
||||
}
|
||||
|
||||
public static Boolean isZaboronaEnabled(){
|
||||
return getPrefsValue("proxy").equals("zaborona");
|
||||
}
|
||||
|
||||
public static Boolean isRandomProxyEnabled(){
|
||||
return getPrefsValue("proxy").equals("randomproxy");
|
||||
}
|
||||
|
||||
public static Boolean isApiProxyEnabled(){
|
||||
return getPrefsValue("proxy").equals("apiproxy");
|
||||
}
|
||||
|
||||
public static Boolean isVikaProxyEnabled(){
|
||||
return getPrefsValue("proxy").equals("vika");
|
||||
}
|
||||
|
||||
public static void forceProxyApplying(){
|
||||
Log.d("Proxy", "Setting proxy...");
|
||||
try {
|
||||
var context = getGlobalContext().getApplicationContext();
|
||||
Class<?> applicationClass = Class.forName("android.app.Application");
|
||||
@SuppressLint("DiscouragedPrivateApi") @SuppressWarnings("JavaReflectionMemberAccess")
|
||||
Field mLoadedApkField = applicationClass.getDeclaredField("mLoadedApk");
|
||||
mLoadedApkField.setAccessible(true);
|
||||
Object mLoadedApkObject = mLoadedApkField.get(context);
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
Class<?> loadedApkClass = Class.forName("android.app.LoadedApk");
|
||||
@SuppressLint("DiscouragedPrivateApi") Field mReceiversField = loadedApkClass.getDeclaredField("mReceivers");
|
||||
mReceiversField.setAccessible(true);
|
||||
ArrayMap<?, ?> receivers = (ArrayMap<?, ?>) mReceiversField.get(mLoadedApkObject);
|
||||
if (receivers != null) {
|
||||
for (Object receiverMap : receivers.values()) {
|
||||
for (Object receiver : ((ArrayMap<?, ?>) receiverMap).keySet()) {
|
||||
Class<?> receiverClass = receiver.getClass();
|
||||
|
||||
if (receiverClass.getName().contains("ProxyChangeListener")) {
|
||||
Method onReceiveMethod = receiverClass.getDeclaredMethod("onReceive", Context.class, Intent.class);
|
||||
Intent intent = new Intent(android.net.Proxy.PROXY_CHANGE_ACTION);
|
||||
onReceiveMethod.invoke(receiver, context, intent);
|
||||
} else {
|
||||
for (Field field : receiverClass.getDeclaredFields()) {
|
||||
if (field.getType().getName().contains("ProxyChangeListener")) {
|
||||
Method onReceiveMethod = receiverClass.getDeclaredMethod("onReceive", Context.class, Intent.class);
|
||||
Intent intent = new Intent(android.net.Proxy.PROXY_CHANGE_ACTION);
|
||||
onReceiveMethod.invoke(receiver, context, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("Proxy", "Setting proxy successful!");
|
||||
} catch (Exception e) {
|
||||
Log.d("Proxy", "Setting proxy failed!");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasProxy(String list){
|
||||
return list.contains(":"); // proxy format: ip:port
|
||||
}
|
||||
|
||||
public static void setProxy() throws IOException{
|
||||
switch(getPrefsValue("proxy")) {
|
||||
case "zaborona":
|
||||
Zaborona.loadProxy();
|
||||
break;
|
||||
case "randomproxy":
|
||||
RandomProxy.loadProxy();
|
||||
break;
|
||||
case "socks":
|
||||
CustomSocks.loadProxy();
|
||||
break;
|
||||
case "http":
|
||||
CustomHttp.loadProxy();
|
||||
break;
|
||||
case "https":
|
||||
CustomHttps.loadProxy();
|
||||
break;
|
||||
default:
|
||||
resetProxy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetProxy(){
|
||||
clearProperty("https.proxyHost");
|
||||
clearProperty("https.proxyPort");
|
||||
clearProperty("https.proxyUser");
|
||||
clearProperty("https.proxyPassword");
|
||||
clearProperty("http.proxyHost");
|
||||
clearProperty("http.proxyPort");
|
||||
clearProperty("http.proxyUser");
|
||||
clearProperty("http.proxyPassword");
|
||||
clearProperty("socksProxyHost");
|
||||
clearProperty("socksPortHost");
|
||||
} // Reset all proxies
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ru.vtosters.lite.proxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.resetProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.Preferences.preferences;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ru.vtosters.lite.proxy.socks.Hookzof;
|
||||
import ru.vtosters.lite.proxy.socks.Jetkai;
|
||||
import ru.vtosters.lite.proxy.socks.Roosterkid;
|
||||
import ru.vtosters.lite.proxy.socks.TheSpeedX;
|
||||
|
||||
public class RandomProxy{
|
||||
public static String type = preferences.getString("random_type", "none");
|
||||
public static String host = preferences.getString("random_host", "");
|
||||
public static boolean showProxyResult = false;
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
if (host.isEmpty()) {
|
||||
getProxy();
|
||||
loadProxy();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case "socks":
|
||||
System.setProperty("socksProxyHost", host.split(":")[0]);
|
||||
System.setProperty("socksProxyPort", host.split(":")[1]);
|
||||
break;
|
||||
case "http":
|
||||
System.setProperty("http.proxyHost", host.split(":")[0]);
|
||||
System.setProperty("http.proxyPort", host.split(":")[1]);
|
||||
break;
|
||||
case "https":
|
||||
System.setProperty("https.proxyHost", host.split(":")[0]);
|
||||
System.setProperty("https.proxyPort", host.split(":")[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupNewProxy() throws IOException{
|
||||
edit().putString("random_type", "none").putString("random_host", "").commit();
|
||||
|
||||
resetProxy();
|
||||
forceProxyApplying();
|
||||
|
||||
showProxyResult = true;
|
||||
getProxy();
|
||||
edit().putString("proxy", "randomproxy").commit();
|
||||
}
|
||||
|
||||
public static void getProxy() throws IOException{
|
||||
var random = new java.util.Random();
|
||||
var randomInt = random.nextInt(7);
|
||||
switch(randomInt) {
|
||||
case 0:
|
||||
Hookzof.loadProxy();
|
||||
break;
|
||||
case 1:
|
||||
TheSpeedX.loadProxy();
|
||||
break;
|
||||
case 2:
|
||||
Jetkai.loadProxy();
|
||||
break;
|
||||
case 3:
|
||||
Roosterkid.loadProxy();
|
||||
break;
|
||||
case 4:
|
||||
ru.vtosters.lite.proxy.http.Jetkai.loadProxy();
|
||||
break;
|
||||
case 5:
|
||||
ru.vtosters.lite.proxy.http.TheSpeedX.loadProxy();
|
||||
break;
|
||||
case 6:
|
||||
ru.vtosters.lite.proxy.https.Jetkai.loadProxy();
|
||||
break;
|
||||
case 7:
|
||||
ru.vtosters.lite.proxy.https.Roosterkid.loadProxy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package ru.vtosters.lite.proxy.api;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isAnyProxyEnabled;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.isVikaProxyEnabled;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class ApiProxy{
|
||||
public static String linkReplacer(String link){
|
||||
var vkapi = "api.vk.com";
|
||||
var oauth = "oauth.vk.com";
|
||||
var vkstatic = "static.vk.com";
|
||||
|
||||
var proxyapi = getPrefsValue("proxyapi");
|
||||
var proxyoauth = getPrefsValue("proxyoauth");
|
||||
var proxystatic = getPrefsValue("proxystatic");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
proxyapi = VikaMobile.getApiHost();
|
||||
proxyoauth = VikaMobile.getOauthHost();
|
||||
proxystatic = VikaMobile.getStaticHost();
|
||||
}
|
||||
|
||||
if (!isAnyProxyEnabled() || link.isEmpty()) {
|
||||
return link;
|
||||
}
|
||||
|
||||
if (proxyapi.isEmpty() || proxyoauth.isEmpty() || proxystatic.isEmpty()) {
|
||||
Log.d("VTLite", "Proxy is not set" + " " + proxyapi + " " + proxyoauth + " " + proxystatic);
|
||||
return link;
|
||||
}
|
||||
|
||||
if (link.contains(vkapi)) {
|
||||
return link.replaceAll(vkapi, proxyapi);
|
||||
}
|
||||
|
||||
if (link.contains(oauth)) {
|
||||
return link.replaceAll(oauth, proxyoauth);
|
||||
}
|
||||
|
||||
if (link.contains(vkstatic)) {
|
||||
return link.replaceAll(vkstatic, proxystatic);
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
public static String staticFix(String str) {
|
||||
var string = getPrefsValue("proxystatic");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
string = VikaMobile.getStaticHost();
|
||||
}
|
||||
|
||||
if (isAnyProxyEnabled() && !string.isEmpty()) {
|
||||
return str.replaceAll(string, "static.vk.com");
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String getAwayPhpCom(){
|
||||
var proxyapi = getPrefsValue("proxyapi");
|
||||
|
||||
if (isVikaProxyEnabled()) {
|
||||
proxyapi = VikaMobile.getApiHost();
|
||||
}
|
||||
|
||||
if (isAnyProxyEnabled() && !proxyapi.isEmpty()) {
|
||||
return proxyapi;
|
||||
}
|
||||
|
||||
return "m.vk.com";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.vtosters.lite.proxy.api;
|
||||
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
|
||||
|
||||
public class VikaMobile{
|
||||
public static String getApiHost(){
|
||||
return isAlternativeHost() ? "vk-api-proxy.vikamobile.ru" : "vk-api-proxy.symbian.live";
|
||||
}
|
||||
|
||||
public static String getOauthHost(){
|
||||
return isAlternativeHost() ? "vk-oauth-proxy.vikamobile.ru" : "vk-oauth-proxy.symbian.live";
|
||||
}
|
||||
|
||||
public static String getStaticHost(){
|
||||
return isAlternativeHost() ? "vk-static-proxy.vikamobile.ru" : "vk-static-proxy.symbian.live";
|
||||
}
|
||||
|
||||
public static Boolean isAlternativeHost(){
|
||||
return getBoolValue("altervika", false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ru.vtosters.lite.proxy.http;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
|
||||
public class CustomHttp{
|
||||
public static void loadProxy(){
|
||||
System.setProperty("http.proxyHost", proxyHostHTTP());
|
||||
System.setProperty("http.proxyPort", proxyPortHTTP());
|
||||
System.setProperty("http.proxyUser", proxyUserHTTP());
|
||||
System.setProperty("http.proxyPassword", proxyPassHTTP());
|
||||
forceProxyApplying();
|
||||
}
|
||||
|
||||
private static String proxyHostHTTP(){
|
||||
var string = getPrefsValue("proxyHostHTTP");
|
||||
return string.isEmpty() ? "192.168.0.1" : string;
|
||||
}
|
||||
|
||||
private static String proxyPortHTTP(){
|
||||
var string = getPrefsValue("proxyPortHTTP");
|
||||
return string.isEmpty() ? "8888" : string;
|
||||
}
|
||||
|
||||
private static String proxyUserHTTP(){
|
||||
var string = getPrefsValue("proxyUserHTTP");
|
||||
return string.isEmpty() ? "" : string;
|
||||
}
|
||||
|
||||
private static String proxyPassHTTP(){
|
||||
var string = getPrefsValue("proxyPassHTTP");
|
||||
return string.isEmpty() ? "" : string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ru.vtosters.lite.proxy.http;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Jetkai{
|
||||
private static final String TAG = "Jetkai_Http";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-http.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "http").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("http.proxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("http.proxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.http;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class TheSpeedX{
|
||||
private static final String TAG = "TheSpeedX_Http";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "http").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("http.proxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("http.proxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ru.vtosters.lite.proxy.https;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
|
||||
public class CustomHttps{
|
||||
public static void loadProxy(){
|
||||
System.setProperty("https.proxyHost", proxyHostHTTPS());
|
||||
System.setProperty("https.proxyPort", proxyPortHTTPS());
|
||||
System.setProperty("https.proxyUser", proxyUserHTTPS());
|
||||
System.setProperty("https.proxyPassword", proxyPassHTTPS());
|
||||
forceProxyApplying();
|
||||
}
|
||||
|
||||
private static String proxyHostHTTPS(){
|
||||
var string = getPrefsValue("proxyHostHTTPS");
|
||||
return string.isEmpty() ? "192.168.0.1" : string;
|
||||
}
|
||||
|
||||
private static String proxyPortHTTPS(){
|
||||
var string = getPrefsValue("proxyPortHTTPS");
|
||||
return string.isEmpty() ? "8888" : string;
|
||||
}
|
||||
|
||||
private static String proxyUserHTTPS(){
|
||||
var string = getPrefsValue("proxyUserHTTPS");
|
||||
return string.isEmpty() ? "" : string;
|
||||
}
|
||||
|
||||
private static String proxyPassHTTPS(){
|
||||
String string = getPrefsValue("proxyPassHTTPS");
|
||||
return string.isEmpty() ? "" : string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.https;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Jetkai{
|
||||
private static final String TAG = "Jetkai_Https";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-https.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "https").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("https.proxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("https.proxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ru.vtosters.lite.proxy.https;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Roosterkid{
|
||||
private static final String TAG = "Roosterkid_Https";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/roosterkid/openproxylist/main/HTTPS_RAW.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "https").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("https.proxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("https.proxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
|
||||
public class CustomSocks{
|
||||
public static void loadProxy(){
|
||||
System.setProperty("socksProxyHost", proxyHostSocks());
|
||||
System.setProperty("socksProxyPort", proxyPortSocks());
|
||||
forceProxyApplying();
|
||||
}
|
||||
|
||||
private static String proxyHostSocks(){
|
||||
var string = getPrefsValue("proxyHostSocks");
|
||||
return string.isEmpty() ? "192.168.0.1" : string;
|
||||
}
|
||||
|
||||
private static String proxyPortSocks(){
|
||||
var string = getPrefsValue("proxyPortSocks");
|
||||
return string.isEmpty() ? "8888" : string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Hookzof{
|
||||
private static final String TAG = "Hookzof_Socks";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "socks").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("socksProxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("socksProxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Jetkai{
|
||||
private static final String TAG = "Jetkai_Socks";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-socks5.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "socks").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("socksProxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("socksProxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class Roosterkid{
|
||||
private static final String TAG = "Roosterkid_Socks";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/roosterkid/openproxylist/main/SOCKS5_RAW.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "socks").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("socksProxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("socksProxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.GithubList.getProxy;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.edit;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.proxy.RandomProxy;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class TheSpeedX{
|
||||
private static final String TAG = "TheSpeedX_Socks";
|
||||
|
||||
public static void loadProxy() throws IOException{
|
||||
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt", TAG);
|
||||
|
||||
if (proxyList == null) {
|
||||
Log.d(TAG, "No proxy found");
|
||||
return;
|
||||
}
|
||||
|
||||
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
|
||||
|
||||
if (!hasProxy(proxy)) {
|
||||
Log.d(TAG, "No proxy port found");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
|
||||
|
||||
if (RandomProxy.showProxyResult) {
|
||||
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
|
||||
RandomProxy.showProxyResult = false;
|
||||
}
|
||||
|
||||
edit().putString("random_type", "socks").putString("random_host", proxy).commit();
|
||||
|
||||
System.setProperty("socksProxyHost", proxy.split(":")[0]);
|
||||
System.setProperty("socksProxyPort", proxy.split(":")[1]);
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.vtosters.lite.proxy.socks;
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
|
||||
|
||||
public class Zaborona{
|
||||
public static void loadProxy(){
|
||||
System.setProperty("socksProxyHost", "socks.zaboronahelp.pp.ua");
|
||||
System.setProperty("socksProxyPort", "1488");
|
||||
forceProxyApplying();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class LogWriterService extends Service {
|
||||
public LogWriterService() {
|
||||
}
|
||||
@@ -44,12 +46,12 @@ public class LogWriterService extends Service {
|
||||
writeLog(intent, file);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
makeText(getGlobalContext(), "Не удалось сохранить лог-файл", LENGTH_SHORT).show();
|
||||
makeText(getGlobalContext(), AndroidUtils.getString("logfile_error"), LENGTH_SHORT).show();
|
||||
}
|
||||
var notificationId = intent.getIntExtra("notificationId", 1);
|
||||
var manager = (NotificationManager) getGlobalContext().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.cancel(notificationId);
|
||||
makeText(getGlobalContext(), "Лог-файл был сохранен", LENGTH_SHORT).show();
|
||||
makeText(getGlobalContext(), AndroidUtils.getString("logfile_saved"), LENGTH_SHORT).show();
|
||||
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
|
||||
@@ -11,6 +11,8 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class APKInstallActivity extends AppCompatActivity {
|
||||
public static final int REQUEST_INSTALL_PERMISSION = 1;
|
||||
|
||||
@@ -65,7 +67,7 @@ public class APKInstallActivity extends AppCompatActivity {
|
||||
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "Ошибка установки обновления " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(this, AndroidUtils.getString("apk_install_error") + " " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
finish();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.vtosters.lite.ui.activities;
|
||||
|
||||
import static ru.vtosters.lite.proxy.ProxyUtils.getOauth;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -20,7 +22,7 @@ import ru.vtosters.lite.utils.LayoutUtils;
|
||||
|
||||
public class VKAdminTokenActivity extends AppCompatActivity {
|
||||
|
||||
private static final String URL = "https://oauth.vk.com/authorize?client_id=6121396&scope=1073737727&redirect_uri=https://oauth.vk.com/blank.html&display=page&response_type=token&revoke=1";
|
||||
private static final String URL = "https://" + getOauth() +"/authorize?client_id=6121396&scope=1073737727&redirect_uri=https://oauth.vk.com/blank.html&display=page&response_type=token&revoke=1";
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle bundle) {
|
||||
@@ -37,7 +39,7 @@ public class VKAdminTokenActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
var url = request.getUrl().toString();
|
||||
if (url.startsWith("https://oauth.vk.com/auth_redirect?")) {
|
||||
if (url.startsWith("https://" + getOauth() + "/auth_redirect?")) {
|
||||
var redirectUri = Uri.parse(URLDecoder.decode(url));
|
||||
var authorizeUrl = redirectUri.getQueryParameter("authorize_url");
|
||||
var accessToken = authorizeUrl.substring(authorizeUrl.indexOf("=") + 1, authorizeUrl.indexOf("&"));
|
||||
|
||||
@@ -34,10 +34,10 @@ public class CategorizedAdapter extends RecyclerView.Adapter<CategorizedAdapter.
|
||||
public static final int SELECTED_STATE = -0x1;
|
||||
public static final int DISABLED_STATE = -0x2;
|
||||
|
||||
private List mSelectedItems;
|
||||
private List mDisabledItems;
|
||||
private List mUnmovedItems;
|
||||
private IViewHolderBinder mBinder;
|
||||
private final List mSelectedItems;
|
||||
private final List mDisabledItems;
|
||||
private final List mUnmovedItems;
|
||||
private final IViewHolderBinder mBinder;
|
||||
|
||||
private int mMinSelectedItems = -1;
|
||||
private int mMaxSelectedItems = -1;
|
||||
|
||||
@@ -25,7 +25,7 @@ import ru.vtosters.lite.utils.LayoutUtils;
|
||||
public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.ImagineArrayAdapterItem> {
|
||||
|
||||
private List<ImagineArrayAdapterItem> mItems = new ArrayList<>();
|
||||
private OnClickListener mListener;
|
||||
private final OnClickListener mListener;
|
||||
private int mSelectedIndex = -1;
|
||||
|
||||
public ImagineArrayAdapter(@NonNull Context context, OnClickListener listener) {
|
||||
@@ -134,8 +134,8 @@ public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.Imagin
|
||||
}
|
||||
|
||||
public static class ImagineArrayAdapterItem {
|
||||
private Drawable mIcon;
|
||||
private String mText;
|
||||
private final Drawable mIcon;
|
||||
private final String mText;
|
||||
|
||||
public ImagineArrayAdapterItem(Drawable icon, String text) {
|
||||
this.mIcon = icon;
|
||||
@@ -167,6 +167,6 @@ public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.Imagin
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
public void onClick(int i);
|
||||
void onClick(int i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,10 @@ import com.vtosters.lite.R;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreviewAdapter.MessagePreviewViewHolder> {
|
||||
|
||||
private static final int ME = 0x0;
|
||||
private static final int ANONYMOUS = -0x1;
|
||||
|
||||
private final String[] mMessages = {
|
||||
"Доброе время", "Доброе утро, день, вечер. ночь", "Как живёшь?", "Не мёртвый, и то хорошо"
|
||||
};
|
||||
private final String[] mMessages = AndroidUtils.getArray("wallpaper_change_dialog");
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int i) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user