Compare commits
1462
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecce32bfe7 | ||
|
|
ae059dadfe | ||
|
|
2be8c6a11a | ||
|
|
0447c580e4 | ||
|
|
291097e852 | ||
|
|
8c7d103be7 | ||
|
|
427653a796 | ||
|
|
0044771a06 | ||
|
|
9eb0231e1a | ||
|
|
83178cbd6c | ||
|
|
9c0c27d16b | ||
|
|
70d597f6e8 | ||
|
|
990e2f3e09 | ||
|
|
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 | ||
|
|
3476e4eed4 | ||
|
|
be2b7c99fc | ||
|
|
1e16a43b1e | ||
|
|
e8dea04d5d | ||
|
|
7d3c4ce4a6 | ||
|
|
b49813e2b1 | ||
|
|
9bd5cafc2d | ||
|
|
433801a9d0 | ||
|
|
3dd4e4df38 | ||
|
|
3c90940695 | ||
|
|
c7e5c57248 | ||
|
|
0fbdccbecc | ||
|
|
c5a0d7d05c | ||
|
|
d365cd9068 | ||
|
|
6ea4822f41 | ||
|
|
cc16f4727f | ||
|
|
79c2fc9010 | ||
|
|
e9047274b3 | ||
|
|
5f57558c52 | ||
|
|
3c8b394a4b | ||
|
|
f5037dced3 | ||
|
|
274e1bd0df | ||
|
|
32daac436e | ||
|
|
820ca12c3e | ||
|
|
f3cda292e0 | ||
|
|
75c98b98dd | ||
|
|
cc6e3c2dd8 | ||
|
|
9a9a73e89a | ||
|
|
a6f52af5d1 | ||
|
|
fa12e8f7e0 | ||
|
|
3a7a05e630 | ||
|
|
e05fe77fb4 | ||
|
|
b86128ac66 | ||
|
|
3e9058002b | ||
|
|
76775f0ad4 | ||
|
|
086210718c | ||
|
|
bc1f11da61 | ||
|
|
63075ca216 | ||
|
|
4876a881d2 | ||
|
|
fef7082e66 | ||
|
|
4dc7223629 | ||
|
|
9fbb2fada4 | ||
|
|
a31d75ea41 | ||
|
|
efa28aa3d6 | ||
|
|
9dd3a26bab | ||
|
|
f49bc4d05d | ||
|
|
0ca4ba2697 | ||
|
|
09333dc64d | ||
|
|
91bfea9917 | ||
|
|
042595f238 | ||
|
|
174151b52b | ||
|
|
7fed263a0e | ||
|
|
79fd0350e4 | ||
|
|
4f760b5a4d | ||
|
|
f19a4275de | ||
|
|
7e36bbf83e | ||
|
|
0a7338bf89 | ||
|
|
c1197e461b | ||
|
|
7baaf08167 | ||
|
|
0d75bec938 | ||
|
|
0af00ab7d1 | ||
|
|
2011901666 | ||
|
|
b75ce047df | ||
|
|
bfd258ba54 | ||
|
|
e499d98050 | ||
|
|
7299c9e33a | ||
|
|
4d0573ffe5 | ||
|
|
83d13dce1e | ||
|
|
55dbb029c0 | ||
|
|
3df51d0685 | ||
|
|
9bc87a1d79 | ||
|
|
94667ac741 | ||
|
|
3a354b359a | ||
|
|
09443d6ec1 | ||
|
|
ca27776aaf | ||
|
|
3b6cdacf96 | ||
|
|
f6555f49d8 | ||
|
|
c7621c44d5 | ||
|
|
89997317bf | ||
|
|
c87be6c9b8 | ||
|
|
4ae5ea8e57 | ||
|
|
b967e72a04 | ||
|
|
cc66af3e3a | ||
|
|
f82ef2f64e | ||
|
|
646329f93b | ||
|
|
3f7b9eb796 | ||
|
|
a3d8a9ba70 | ||
|
|
8d05973848 | ||
|
|
2b553d263c | ||
|
|
265d3afbff | ||
|
|
858fced1aa | ||
|
|
a01d12b012 | ||
|
|
8bde61b059 | ||
|
|
05eec57058 | ||
|
|
c2a1a4bb5f | ||
|
|
5e01ebcff6 | ||
|
|
ffe0955ef1 | ||
|
|
f4ca7c9e66 | ||
|
|
57b19369a0 | ||
|
|
4019219293 | ||
|
|
5d6c001dfb | ||
|
|
1f2b1b1180 | ||
|
|
cb7862a0e2 | ||
|
|
1cd23341e2 | ||
|
|
f85dac2ff8 | ||
|
|
992b29894e | ||
|
|
9e67ded6bd | ||
|
|
24607e1ea5 | ||
|
|
8e260a9de4 | ||
|
|
de045f749a | ||
|
|
fde1628fac | ||
|
|
fdeaa05360 | ||
|
|
d0197142cd | ||
|
|
8257393909 | ||
|
|
9542317b23 | ||
|
|
de595c93f4 | ||
|
|
7596b4117a | ||
|
|
8b6ef6d3af | ||
|
|
1d71def416 | ||
|
|
18799ce11c | ||
|
|
b4e34e34b1 | ||
|
|
cd6f975e56 | ||
|
|
de73c6216b | ||
|
|
129ae95121 | ||
|
|
78cd5cac9a | ||
|
|
f7fab596ba | ||
|
|
85afaa5057 | ||
|
|
f1ed0fc221 | ||
|
|
b64b17038d | ||
|
|
e5b5bf9d48 | ||
|
|
c58e511040 | ||
|
|
a067a35040 | ||
|
|
dbee2a5a42 | ||
|
|
104ca05772 | ||
|
|
ad5eca635a | ||
|
|
6c86b49e97 | ||
|
|
ad8f2b5b5a | ||
|
|
3058c220a8 | ||
|
|
6531a21fb4 | ||
|
|
3366d28e7e | ||
|
|
39b08702d5 | ||
|
|
b90b27e8ce | ||
|
|
9b03299c7b | ||
|
|
a9343c6777 | ||
|
|
a03505f27f | ||
|
|
b9126aa184 | ||
|
|
613a101c82 | ||
|
|
43fe0577cd | ||
|
|
6208c966d0 | ||
|
|
636341b4f2 | ||
|
|
576ce7d6b0 | ||
|
|
e74001e17e | ||
|
|
48f6b35abe | ||
|
|
5beea5d273 | ||
|
|
842130628e | ||
|
|
ab26bab35d | ||
|
|
6ffcbe8329 | ||
|
|
ca3d1e9585 | ||
|
|
05a9e9a12c | ||
|
|
9e2f3bb048 | ||
|
|
91aef33725 | ||
|
|
32c5aa9ac2 | ||
|
|
e32c3ec74b | ||
|
|
3fea0e9c40 | ||
|
|
c9c5c96d63 | ||
|
|
2a91238b4f | ||
|
|
e4d740061d | ||
|
|
f96f2cb7d7 | ||
|
|
0482f75172 | ||
|
|
eaed8fc752 | ||
|
|
f871e63002 | ||
|
|
189d699d48 | ||
|
|
f3b6b3c1b0 | ||
|
|
519722d613 | ||
|
|
3aaca80da1 | ||
|
|
b889015368 | ||
|
|
1d3463df0c | ||
|
|
60ac497e6d | ||
|
|
f8192c7169 | ||
|
|
3f2e0c9b98 | ||
|
|
2a9ea90df5 | ||
|
|
aa40bbb390 | ||
|
|
4782401f0a | ||
|
|
0255cfb1b9 | ||
|
|
764b68d47f | ||
|
|
126154fc9b | ||
|
|
ec2df87e42 | ||
|
|
e963080385 | ||
|
|
23286a21f0 | ||
|
|
4e48faec16 | ||
|
|
666c562092 | ||
|
|
4f5cbe426a | ||
|
|
5635cd4139 | ||
|
|
4bb31d492a | ||
|
|
f85fe81881 | ||
|
|
5295e465e9 | ||
|
|
7f30e66c0c | ||
|
|
d55361fd72 | ||
|
|
63d1c1a3b0 | ||
|
|
4cc6bfabd0 | ||
|
|
802f639633 | ||
|
|
8a90602328 | ||
|
|
6f607e9ae8 | ||
|
|
dd0f238701 | ||
|
|
600a9da953 | ||
|
|
d14dec88bc | ||
|
|
6e216830d1 | ||
|
|
2b6012b1e7 | ||
|
|
ded283df35 | ||
|
|
86a5c60f7f | ||
|
|
1d964762ca | ||
|
|
4a2d2b23f2 | ||
|
|
a06d129c8d | ||
|
|
a0b1adbfe9 | ||
|
|
0093463fae | ||
|
|
9e572d7844 | ||
|
|
900dbcfe0a | ||
|
|
e3e06d1638 | ||
|
|
69a4cfa715 | ||
|
|
21d9c13fdf | ||
|
|
09bf8e26ce | ||
|
|
21c0ccd9fc | ||
|
|
aa95b368f8 | ||
|
|
4be234632c | ||
|
|
00d37e3721 | ||
|
|
4c4a3ae11c | ||
|
|
7e09e6d4e6 | ||
|
|
b92fa69628 | ||
|
|
d127a84b07 | ||
|
|
95916b4289 | ||
|
|
5cb5d06993 | ||
|
|
f0da269968 | ||
|
|
2fb021ad05 | ||
|
|
1981bc9d14 | ||
|
|
c60cce5467 | ||
|
|
2035d513f1 | ||
|
|
3849569794 | ||
|
|
9f2f6d6f60 | ||
|
|
586a82a15f | ||
|
|
eba922871f | ||
|
|
337aa7493e | ||
|
|
1ed9f871c3 | ||
|
|
083d96707c | ||
|
|
10e0bd50da | ||
|
|
2864056186 | ||
|
|
8876a66449 | ||
|
|
41a1d29c4b | ||
|
|
1bb10807c4 | ||
|
|
7f8100660a | ||
|
|
274a1b9fa8 | ||
|
|
03c5f83a42 | ||
|
|
f394bdfec5 | ||
|
|
19c9dd102f | ||
|
|
d38038f848 | ||
|
|
8ab5cdd860 | ||
|
|
2f30d070ec | ||
|
|
401fc44e91 | ||
|
|
028b3aad59 | ||
|
|
8a7c21c549 | ||
|
|
918967f502 | ||
|
|
95636ad4f4 | ||
|
|
561afe8765 | ||
|
|
929e481b3d | ||
|
|
69266ed31f | ||
|
|
742e0c46b3 | ||
|
|
319f3c421a | ||
|
|
3b5eb3cd10 | ||
|
|
55f71bfd8b | ||
|
|
1ac755a98b | ||
|
|
00d0b9d237 | ||
|
|
edd04586ea | ||
|
|
98b1cf8db3 | ||
|
|
d20296d4b8 | ||
|
|
7c8627f9f6 | ||
|
|
2e2be2fe4a | ||
|
|
673d5fe16c | ||
|
|
4a949e7d45 | ||
|
|
39391a4ace | ||
|
|
9a9552c369 | ||
|
|
6b9f80284c | ||
|
|
15dd2b2fde | ||
|
|
4b2a537e3b | ||
|
|
a94ca687eb | ||
|
|
06f0864cc3 | ||
|
|
1ddc2f6b14 | ||
|
|
ad6c350e69 | ||
|
|
dadbc37189 | ||
|
|
d646a0cd19 | ||
|
|
ae145691c5 | ||
|
|
a6cbf35350 | ||
|
|
aba12ad48a | ||
|
|
32ebf0e4f4 | ||
|
|
099de74ac1 | ||
|
|
dd490abf69 | ||
|
|
3468b9352e | ||
|
|
af0efb7184 | ||
|
|
592d25bf6d | ||
|
|
a53f1d7299 | ||
|
|
ea3707cebd | ||
|
|
b2ba9774bc | ||
|
|
7205c78839 | ||
|
|
26672cfbc2 | ||
|
|
941335ec02 | ||
|
|
8bbf0ebb1f | ||
|
|
9d16d4e57e | ||
|
|
cece5a2a55 | ||
|
|
fb907d68a4 | ||
|
|
dac2e10a52 | ||
|
|
0e472e985b | ||
|
|
e349193afb | ||
|
|
066beb14f1 | ||
|
|
15f8af1e3e | ||
|
|
599ee3dfab | ||
|
|
9d005cd2e0 | ||
|
|
c8747ea957 | ||
|
|
6bc33dbb31 | ||
|
|
303fe83ff9 | ||
|
|
cb122bb6c8 | ||
|
|
bb73aaa580 | ||
|
|
7e7730e825 | ||
|
|
8f291b058f | ||
|
|
0f9ab88e68 | ||
|
|
36c244be15 | ||
|
|
71d4cd09fc | ||
|
|
037f1809e8 | ||
|
|
3513844ca5 | ||
|
|
2e180d2c9c | ||
|
|
fd3eae0283 | ||
|
|
048a835b2b | ||
|
|
e144390d4f | ||
|
|
320892cdf5 | ||
|
|
1716aa51dd | ||
|
|
7157a0c664 | ||
|
|
34787ffb5b | ||
|
|
d44b363d1d | ||
|
|
3972ceba5b | ||
|
|
505a2d4553 | ||
|
|
84da0b422a | ||
|
|
ab19f6b71a | ||
|
|
9c8f962e5f | ||
|
|
ad706355cd | ||
|
|
efac38a871 | ||
|
|
049940f3bb | ||
|
|
5f68809d9c | ||
|
|
930792f983 | ||
|
|
5e7eb51127 | ||
|
|
99a387f9a0 | ||
|
|
679960045a | ||
|
|
1dc1436082 | ||
|
|
2ba602ae9f | ||
|
|
98aa6ef0f8 | ||
|
|
b8f5b0179a | ||
|
|
877670bb84 | ||
|
|
5859b93612 | ||
|
|
0f33dfc395 | ||
|
|
0330b8ad8e | ||
|
|
7d85d61eb5 | ||
|
|
28ee64fc30 | ||
|
|
19bc27059d | ||
|
|
457822de7d | ||
|
|
4c5d83fccc | ||
|
|
92c9ea2222 | ||
|
|
e6b7ea8c2c | ||
|
|
81bc85a0aa | ||
|
|
f9b02b1cce | ||
|
|
2cb6321776 | ||
|
|
bbf0ada834 | ||
|
|
8c63ff69c7 | ||
|
|
94c72eb490 | ||
|
|
d1f2a78a48 | ||
|
|
2983afcda5 | ||
|
|
fe87a5dd64 | ||
|
|
9daa346e31 | ||
|
|
816683ac8a | ||
|
|
b90fd41bf2 | ||
|
|
991f1f9eb8 | ||
|
|
2ae9df03e1 | ||
|
|
665e2dda05 | ||
|
|
a5134f1cd5 | ||
|
|
3680ff44a2 | ||
|
|
34ae44016d | ||
|
|
a0ebc913d1 | ||
|
|
1afa317e7b | ||
|
|
84c194d318 | ||
|
|
56cd2b2d39 | ||
|
|
b001517a63 | ||
|
|
cd389af2bd | ||
|
|
69a2f03656 | ||
|
|
047b547449 | ||
|
|
0912f57184 | ||
|
|
568f69db60 | ||
|
|
511c16771d | ||
|
|
321273c54a | ||
|
|
f6e0a7edb4 | ||
|
|
dd9b2a8a58 | ||
|
|
77f80883ae | ||
|
|
ec5b8b3455 | ||
|
|
cd611afbd3 | ||
|
|
943c612581 | ||
|
|
2ed8082df2 | ||
|
|
4806d97e29 | ||
|
|
c603c9844a | ||
|
|
a3b1220f18 | ||
|
|
18582c535a | ||
|
|
d002d05b2a | ||
|
|
c198a14e0c | ||
|
|
deda04cbf8 | ||
|
|
021e85d5b8 | ||
|
|
0921485959 | ||
|
|
82aacf32b7 | ||
|
|
e7b0ae0225 | ||
|
|
2fb7778717 | ||
|
|
a993993773 | ||
|
|
6f49e35b22 | ||
|
|
ea277c303f | ||
|
|
a7652e4e71 | ||
|
|
5cb7306613 | ||
|
|
1ddecb47af | ||
|
|
ac2c972b9b | ||
|
|
86798ffb59 | ||
|
|
a85a7d84b2 | ||
|
|
1ed90da1b8 | ||
|
|
a2b231f365 | ||
|
|
a618b2cc5b | ||
|
|
4e4c287111 | ||
|
|
060f24b6c6 | ||
|
|
d3840bf9fd | ||
|
|
ba216cb06b | ||
|
|
917f5b565f | ||
|
|
938c6ff633 | ||
|
|
56bf382618 | ||
|
|
eb3cc75a98 | ||
|
|
f3e3cdf79a | ||
|
|
ad808263d1 | ||
|
|
53616c8bc6 | ||
|
|
466105d69c | ||
|
|
64c9cff0bb | ||
|
|
8fbbf8b6be | ||
|
|
6c0d5619e4 | ||
|
|
c3c41f9d2e | ||
|
|
a154138fb5 | ||
|
|
0bf6821a4b | ||
|
|
470ac9dd4d | ||
|
|
b68b10f776 | ||
|
|
c06f2588a8 | ||
|
|
f6ade7a059 | ||
|
|
08d7851de3 | ||
|
|
78902110eb | ||
|
|
b7b9de4ba4 | ||
|
|
83014931dc | ||
|
|
46edd53cb6 | ||
|
|
87b1251f5a | ||
|
|
4ce73d9e55 | ||
|
|
4541ef61c1 | ||
|
|
4f3f26e3f5 | ||
|
|
1f235bfc4a | ||
|
|
548a029db2 | ||
|
|
6d1b964239 | ||
|
|
487cb6c076 | ||
|
|
5a372f6c60 | ||
|
|
81f954d25a | ||
|
|
ec3248f5c3 | ||
|
|
c959d5e5d6 | ||
|
|
49798694b2 | ||
|
|
3879555297 | ||
|
|
fc11e2d055 | ||
|
|
4905e8fed0 | ||
|
|
9a02f2a7c3 | ||
|
|
03014f245c | ||
|
|
13f247467c | ||
|
|
5116f507c5 | ||
|
|
228e6ea282 | ||
|
|
7663d4cee2 | ||
|
|
4e003d355e | ||
|
|
75982656b1 | ||
|
|
2f12cb188f | ||
|
|
46cd9c0582 | ||
|
|
8be76aa871 | ||
|
|
9d9c02bec5 | ||
|
|
30faa329e9 | ||
|
|
0a9322741c | ||
|
|
f68cc4a6bd | ||
|
|
0717fd960e | ||
|
|
0dcb2b8fd7 | ||
|
|
2581b5a1a2 | ||
|
|
36cd21f7f6 | ||
|
|
e09a6291fe | ||
|
|
f800dffb5f | ||
|
|
86ba91a77e | ||
|
|
6aade65b4b | ||
|
|
ba1aeeb4b1 | ||
|
|
4467abc181 | ||
|
|
75960a7631 | ||
|
|
8209aa6fdf | ||
|
|
38bcd40ad6 | ||
|
|
dbc358d0a0 | ||
|
|
dcb2244498 | ||
|
|
e3b6bd8219 | ||
|
|
07f1f62fb7 | ||
|
|
89f94df42b | ||
|
|
b3197aa921 | ||
|
|
cfc2c67efc | ||
|
|
d3b1e6e075 | ||
|
|
b221a8d55d | ||
|
|
9cc6d4f8e0 | ||
|
|
1a0a24df45 | ||
|
|
f74b2b1acf | ||
|
|
c7b5b7d6ca | ||
|
|
215d747dad | ||
|
|
c10b3e5619 | ||
|
|
f03d4892a2 | ||
|
|
6c84540a5d | ||
|
|
664e1fe428 | ||
|
|
328b2caecb | ||
|
|
0799757097 | ||
|
|
8c9fee306f | ||
|
|
c23624ab11 | ||
|
|
ea4315274b | ||
|
|
947183ff02 | ||
|
|
dbe80696d6 | ||
|
|
1296ab56a7 | ||
|
|
bda751e692 | ||
|
|
5135cfa347 | ||
|
|
c450ef7f87 | ||
|
|
bd470aba41 | ||
|
|
3435531cb2 | ||
|
|
e784f8a27b | ||
|
|
96e9579bbf | ||
|
|
36432e0c80 | ||
|
|
b58a918f27 | ||
|
|
e8753831b6 | ||
|
|
7068600d3d | ||
|
|
b9443f0d78 | ||
|
|
3ca54043d8 | ||
|
|
e672a6dcf4 | ||
|
|
453d90827b | ||
|
|
13f68a3b12 | ||
|
|
07a26092ae | ||
|
|
57b6ba04b3 | ||
|
|
cf744757b6 | ||
|
|
59afe0a412 | ||
|
|
488eb7c0ea | ||
|
|
2a7af94f10 | ||
|
|
1922863723 | ||
|
|
1259231653 | ||
|
|
ba8d3c42a1 | ||
|
|
ab851d3600 | ||
|
|
b80213a017 | ||
|
|
8cb4bb0a55 | ||
|
|
58cddd18e2 | ||
|
|
67c0a3eed4 | ||
|
|
e7c7dae411 | ||
|
|
f66d2af98a | ||
|
|
dc3e6293ee | ||
|
|
05397f69d0 | ||
|
|
9f341eb485 | ||
|
|
e944f5bdf5 | ||
|
|
a0a34d138f | ||
|
|
4b55279c84 | ||
|
|
b5a7852f2e | ||
|
|
b675834a6f | ||
|
|
09e509e48f | ||
|
|
e2b6a3a75e | ||
|
|
dc4c31d66a | ||
|
|
abeb31f4f0 | ||
|
|
c7b91b62e6 | ||
|
|
648369b7cd | ||
|
|
3f834cc83b | ||
|
|
4dd5f13b3a | ||
|
|
faea96a786 | ||
|
|
7882968a05 | ||
|
|
d6595f54f1 | ||
|
|
6f1ba0ca9f | ||
|
|
daee7c8665 | ||
|
|
dd591ee78b | ||
|
|
f72ff25ebe | ||
|
|
04cf3b1b8a | ||
|
|
0cd82deaa6 | ||
|
|
826b566540 | ||
|
|
a2018f85b4 | ||
|
|
f48e05badb | ||
|
|
21ad47b45b | ||
|
|
ea54706e8b | ||
|
|
db6c23d511 | ||
|
|
d06396a51d | ||
|
|
bc5cc9b6f6 | ||
|
|
b05d6fb22b | ||
|
|
3144dd07e8 | ||
|
|
247627fe08 | ||
|
|
32668111e1 | ||
|
|
c3848fdbba | ||
|
|
834367ea65 | ||
|
|
7f9e5aa11e | ||
|
|
5554b5197a | ||
|
|
a0e504a834 | ||
|
|
f5d4892ba6 | ||
|
|
32e807e130 | ||
|
|
d7ff759cde | ||
|
|
fc4f8056c5 | ||
|
|
7b073523fa | ||
|
|
7205be3749 | ||
|
|
e1915e19a3 | ||
|
|
4da341ccf8 | ||
|
|
6ef958a606 | ||
|
|
a5811047d9 | ||
|
|
75e562e92f | ||
|
|
75d537f36a | ||
|
|
b68812109c | ||
|
|
44d0f31f93 | ||
|
|
b5d5b87779 | ||
|
|
cf180f5146 | ||
|
|
430c1686c0 | ||
|
|
d1d4bb1f5b | ||
|
|
03561407a3 | ||
|
|
a9c2c6945b | ||
|
|
6ae59e0557 | ||
|
|
b1c2c785f3 | ||
|
|
aa0c2a7093 | ||
|
|
987379b7a2 | ||
|
|
7ee38e27d3 | ||
|
|
60be976861 | ||
|
|
14e1c736b3 | ||
|
|
fc6945a72d | ||
|
|
b73e8b5c99 | ||
|
|
7fad5f2db8 | ||
|
|
6119610d7f | ||
|
|
401685c1b6 | ||
|
|
bf0f78b391 | ||
|
|
0f40e271db | ||
|
|
990a392b83 | ||
|
|
9e1d0077f6 | ||
|
|
68c3619f7f | ||
|
|
0419316f32 | ||
|
|
fb8252eebe | ||
|
|
8a7763c356 | ||
|
|
6187eecac5 | ||
|
|
4d3f262d59 | ||
|
|
95feb481a9 | ||
|
|
1a42c24dab | ||
|
|
833e02bfb2 | ||
|
|
705d6bf639 | ||
|
|
1186cd4a8d | ||
|
|
4b19b59b3a | ||
|
|
066a1ed161 | ||
|
|
cc9a826f1d | ||
|
|
8dd7b2d368 | ||
|
|
d82609304a | ||
|
|
831ba0a774 | ||
|
|
15fe52d83f | ||
|
|
bce20b9ad6 | ||
|
|
291273aef5 | ||
|
|
8248418fe2 | ||
|
|
99629f0a3e | ||
|
|
9405ee352d | ||
|
|
4dd055b9b1 | ||
|
|
7769a76bea | ||
|
|
27b8b57625 | ||
|
|
682ed1c8c5 | ||
|
|
f6c30a4953 | ||
|
|
4c352713ca | ||
|
|
20efb909ae | ||
|
|
fc6f528541 | ||
|
|
1d97000bef | ||
|
|
ade116fa03 | ||
|
|
ec40c99c95 | ||
|
|
ab66804d87 | ||
|
|
126d8c7926 | ||
|
|
b936327eec | ||
|
|
7499aadec6 | ||
|
|
ed5f82cbd2 | ||
|
|
596b71ce56 | ||
|
|
af6aaf341e | ||
|
|
4f51cdec0a | ||
|
|
e4f68f4024 | ||
|
|
b2f78987f7 | ||
|
|
a567d044c6 | ||
|
|
5e6fff7b8d | ||
|
|
f4766d0a11 | ||
|
|
5b95b17703 | ||
|
|
534e33ee46 | ||
|
|
3d0b8cf0a2 | ||
|
|
764f026ab9 | ||
|
|
056fdc3b2c | ||
|
|
d4641d88c0 | ||
|
|
6a82aee5cc | ||
|
|
ffa64a0125 | ||
|
|
856eafe8de | ||
|
|
94a6b5aa96 | ||
|
|
42b02add89 | ||
|
|
034bf2dd2c | ||
|
|
0eccfbf5ea | ||
|
|
23c3bd8852 | ||
|
|
037444d813 | ||
|
|
44de228f9f | ||
|
|
7b25221fee | ||
|
|
363afa251a | ||
|
|
052e3114bf | ||
|
|
d73c5133d3 | ||
|
|
8d0b63ad72 | ||
|
|
5766d03767 | ||
|
|
7ee2f8ab46 | ||
|
|
1840c76a26 | ||
|
|
51e8219306 | ||
|
|
82d16c6a0c | ||
|
|
da087054cc | ||
|
|
5781df15d3 | ||
|
|
4c1cb174b8 | ||
|
|
fa1ef6dfa5 | ||
|
|
1389a39bea | ||
|
|
0491afacec | ||
|
|
de62447522 | ||
|
|
3ba9b8c4e4 | ||
|
|
c1760a48ca | ||
|
|
32b854baf8 | ||
|
|
5b91699c69 | ||
|
|
1f42bb9ec6 | ||
|
|
8330f7fe89 | ||
|
|
74bbb9eed9 | ||
|
|
78960e7827 | ||
|
|
3dfe10fc87 | ||
|
|
807c3b95c4 | ||
|
|
4f408e4cfa | ||
|
|
0d0caa9d83 | ||
|
|
d33e382b49 | ||
|
|
20b7e77be2 | ||
|
|
3652e8c593 | ||
|
|
755268de6b | ||
|
|
4473e68f7e | ||
|
|
65aac426b2 | ||
|
|
a186fd35be | ||
|
|
c651074d91 | ||
|
|
34335d723a | ||
|
|
6cd0cd4730 | ||
|
|
3125ba2f8a | ||
|
|
638ddd0518 | ||
|
|
429f8214b0 | ||
|
|
bdd5c80504 | ||
|
|
c11d763760 | ||
|
|
d2df533c1d | ||
|
|
db3944b8d6 | ||
|
|
0d0e5fdd37 | ||
|
|
2273d2f346 | ||
|
|
7df03fd5e6 | ||
|
|
7dd30d5169 | ||
|
|
c7820e2571 | ||
|
|
cf058d65fd | ||
|
|
3eabe1f659 | ||
|
|
f442f089bf | ||
|
|
82abf48b18 | ||
|
|
7729614f8b | ||
|
|
9b86678daf | ||
|
|
9d356e7d9d | ||
|
|
6ac6819d5d | ||
|
|
e859baf6e5 | ||
|
|
ac137e1fb6 | ||
|
|
997d861f83 | ||
|
|
91cde36777 | ||
|
|
ebf7a4070c | ||
|
|
c63a31ddd8 | ||
|
|
8a0000b678 | ||
|
|
2fc79dbdb8 | ||
|
|
037ea45448 | ||
|
|
e389f7d353 | ||
|
|
6cdba67118 | ||
|
|
3bb32dfd44 | ||
|
|
d7d3ef7549 | ||
|
|
95a029ab6f | ||
|
|
f284a96587 | ||
|
|
ca91b16647 | ||
|
|
35e5a97266 | ||
|
|
6678a361ba | ||
|
|
1e8bd2afc7 | ||
|
|
ad5f658423 | ||
|
|
24e81159f3 | ||
|
|
b6e60e3461 | ||
|
|
203b4b166b | ||
|
|
d586eea977 | ||
|
|
d33e25f95d | ||
|
|
b9508d9be4 | ||
|
|
5b39f797b1 | ||
|
|
6ceb91e8b6 | ||
|
|
94259ce68c | ||
|
|
0f035ebc13 | ||
|
|
dc605a7292 | ||
|
|
15ef4b5419 | ||
|
|
fc6cf416dc | ||
|
|
0a28430c5a | ||
|
|
3207290d77 | ||
|
|
b0fa321606 | ||
|
|
2bffa59a10 | ||
|
|
7f080970e4 | ||
|
|
f1c2527ca3 | ||
|
|
33d210af7f | ||
|
|
d7378e4674 | ||
|
|
8e40b622f7 | ||
|
|
92e4c55cb5 | ||
|
|
f498e7af73 | ||
|
|
bacf196a31 | ||
|
|
2f4603af26 | ||
|
|
203a7d00f2 | ||
|
|
f8c351cd0b | ||
|
|
93ab8d368b | ||
|
|
e38d93fa2b | ||
|
|
59518fa4ce | ||
|
|
c7b7ccfb8b | ||
|
|
e0f9ac1ea3 | ||
|
|
2fa9f76d16 | ||
|
|
7eceb08e8c | ||
|
|
41f558c34c | ||
|
|
dd5963b172 | ||
|
|
c1e3a03412 | ||
|
|
ffd60ded27 | ||
|
|
b5e02b04b7 | ||
|
|
4077621891 | ||
|
|
2d2a835bbe | ||
|
|
526c2a3b8a | ||
|
|
0bee2560d5 | ||
|
|
c4796fb9c7 | ||
|
|
504aa22064 | ||
|
|
7882532361 | ||
|
|
f4786f0479 | ||
|
|
2dc814eacb | ||
|
|
289d82e5bc | ||
|
|
1157d96589 | ||
|
|
e1044a288d | ||
|
|
2cba8fd9ef | ||
|
|
1d9285d371 | ||
|
|
f8d342ba93 | ||
|
|
eae02f4f4a | ||
|
|
f8c397e57f | ||
|
|
87c53c8a42 | ||
|
|
8615d33da9 | ||
|
|
59702a47f0 | ||
|
|
cd69714985 | ||
|
|
f4e51b79fd | ||
|
|
b6fde08278 | ||
|
|
4773f7faef | ||
|
|
33e4637b60 | ||
|
|
e676c5c4f1 | ||
|
|
58741f88f6 | ||
|
|
12077ba07a | ||
|
|
748cb47b4b | ||
|
|
c0470012eb | ||
|
|
cf3eacc14f | ||
|
|
083dc40ad9 | ||
|
|
72001373b9 | ||
|
|
1eb33b6cc5 | ||
|
|
908047fbe2 | ||
|
|
2ac791344a | ||
|
|
0b28a4d20b | ||
|
|
62fbdab39a | ||
|
|
282c82282e | ||
|
|
4b7eeed21c | ||
|
|
d6d50bdc41 | ||
|
|
3be46106a1 | ||
|
|
ea2cda888d | ||
|
|
1d59d4c5ed | ||
|
|
be47db75aa | ||
|
|
41b81641ce | ||
|
|
1faebbb490 | ||
|
|
7036c3e613 | ||
|
|
aa1fece7b5 | ||
|
|
896b420c28 | ||
|
|
ac4fe43aef | ||
|
|
98e5bfb2bb | ||
|
|
ae722e689e | ||
|
|
3abff8cad1 | ||
|
|
155fa80c95 | ||
|
|
a5a2179399 | ||
|
|
8aee5a78a4 | ||
|
|
06ce8a7e6f | ||
|
|
1cc4d7624a | ||
|
|
dd817960e1 | ||
|
|
92e0f3b55b | ||
|
|
03bdf92ae2 | ||
|
|
5492d69445 | ||
|
|
83779a1698 | ||
|
|
49c70e8162 | ||
|
|
654ba13dea | ||
|
|
e4c0b7be6c | ||
|
|
0ee809a721 | ||
|
|
683e17bee4 | ||
|
|
b9d35c2343 | ||
|
|
6b6c69fb19 | ||
|
|
960980022f | ||
|
|
f72da4bec1 | ||
|
|
08e35a811f | ||
|
|
a34c41870c | ||
|
|
25428464b7 | ||
|
|
97c65f9585 | ||
|
|
302fa7e6af | ||
|
|
ab067f242b | ||
|
|
30e5ff6f64 | ||
|
|
2838dc5e7e | ||
|
|
6f4f073ed3 | ||
|
|
564eb85575 | ||
|
|
a6de8e0be4 | ||
|
|
abc87c0254 | ||
|
|
aec302d7f2 | ||
|
|
d27d9227bc | ||
|
|
a1c23aeaaa | ||
|
|
6b05544716 | ||
|
|
161ea1d318 | ||
|
|
f500f34e78 | ||
|
|
e3716b759a | ||
|
|
abc37de666 | ||
|
|
e09994cd30 | ||
|
|
e1dfd9bd99 | ||
|
|
b4ac86ef35 | ||
|
|
3e40286e5d | ||
|
|
4a2d307410 | ||
|
|
878d68a655 | ||
|
|
a56c92f87f | ||
|
|
9095629f96 | ||
|
|
8755c2df34 | ||
|
|
888a1af9b4 | ||
|
|
e0450d3835 | ||
|
|
4e56fdf5aa | ||
|
|
c332d5c97f | ||
|
|
2977a19532 | ||
|
|
a9365a8ba8 | ||
|
|
a6ddcdb759 | ||
|
|
d9dccd53f0 | ||
|
|
3b2f964961 | ||
|
|
2d8bd820a5 | ||
|
|
56a979970c | ||
|
|
836676eb18 | ||
|
|
86a013b619 | ||
|
|
e57a76790f | ||
|
|
b4cec48bbb | ||
|
|
96676721c9 | ||
|
|
eeb8785d42 | ||
|
|
993a1a7f86 | ||
|
|
0b6f29dc70 | ||
|
|
26007f97f3 | ||
|
|
5a55cca59d | ||
|
|
cb425fd18e | ||
|
|
5889dc675e | ||
|
|
df8e7e4b54 | ||
|
|
e07d253a5e | ||
|
|
81f4e6503a | ||
|
|
d7ff250faf | ||
|
|
1d12a5f171 | ||
|
|
485d3d884a | ||
|
|
e78754e39e | ||
|
|
0b6c11f0d0 | ||
|
|
d25604ed66 | ||
|
|
4f3167064c | ||
|
|
f0d4bec116 | ||
|
|
76a1872f74 | ||
|
|
1158cb4aeb | ||
|
|
41ab384e1f | ||
|
|
3839e721db | ||
|
|
d1b84461e6 | ||
|
|
3c79cb3ef5 | ||
|
|
ea7641278a | ||
|
|
23032e8409 | ||
|
|
1d687a01c1 | ||
|
|
0b751964fa | ||
|
|
59eb43c33f | ||
|
|
dfdb056e07 | ||
|
|
cd38eba184 | ||
|
|
a4063b978f | ||
|
|
af51775844 | ||
|
|
f4039abae3 | ||
|
|
1b64f46588 | ||
|
|
ad40368639 | ||
|
|
d15159d9b0 | ||
|
|
c837f6eacc | ||
|
|
14b884daf7 | ||
|
|
63ca14bbe6 | ||
|
|
2605566f90 | ||
|
|
0bcd59d5ba | ||
|
|
a9744bb577 | ||
|
|
9b4dae4af5 | ||
|
|
319d657ab4 | ||
|
|
d9896257c5 | ||
|
|
b5d5e85ee2 | ||
|
|
9dcaddd7fd | ||
|
|
d1bec2e815 | ||
|
|
96d7f28db9 | ||
|
|
90c4ca88e0 | ||
|
|
942fe202d7 | ||
|
|
e17f888598 | ||
|
|
b089c7d7f9 | ||
|
|
afc2a5b258 | ||
|
|
1f0adb7510 | ||
|
|
aba62966b0 | ||
|
|
aaff05685c | ||
|
|
9c1bbab253 | ||
|
|
def1c699e0 | ||
|
|
e6639a63b7 | ||
|
|
1b14761e9e | ||
|
|
07a070a4fe | ||
|
|
8b13fcfd68 | ||
|
|
4088ea68fa | ||
|
|
2e95ed13ce | ||
|
|
f79091bc9c | ||
|
|
ebfc19c1f7 | ||
|
|
bdb8f3cf6f | ||
|
|
4b998f2b97 | ||
|
|
aa1d254a49 | ||
|
|
897b3300e6 | ||
|
|
9c6c9489ff | ||
|
|
6d147b081e | ||
|
|
78432057ca | ||
|
|
21b0ed8925 | ||
|
|
1040e50b32 | ||
|
|
3806a05f4a | ||
|
|
82d45915c6 | ||
|
|
80e5145742 | ||
|
|
f1b7a52f0c | ||
|
|
924952128f | ||
|
|
2fcd04a55f | ||
|
|
deb6e86854 | ||
|
|
086fa37e86 | ||
|
|
e87ecf2f4f | ||
|
|
3dc01dd945 | ||
|
|
da21999192 | ||
|
|
161efaeb52 | ||
|
|
1759fbe8d5 | ||
|
|
b1525266a8 | ||
|
|
28ede39c26 | ||
|
|
6b25eb37ba | ||
|
|
e12dcdf86a | ||
|
|
4bab1490ae | ||
|
|
2374785ae7 | ||
|
|
b338da4359 | ||
|
|
d808f453d9 | ||
|
|
b869f713eb | ||
|
|
2f9cf4751d | ||
|
|
6c7e796e17 | ||
|
|
ff40c7e576 | ||
|
|
0ccc8b2607 | ||
|
|
8febcfdfb7 | ||
|
|
9d05d077b5 | ||
|
|
63ad926078 | ||
|
|
e241848426 | ||
|
|
a82b652bc1 | ||
|
|
e59aecdc75 | ||
|
|
e56ba6b41a | ||
|
|
cfd12483ae | ||
|
|
fd18837f1b | ||
|
|
4f3e37349d | ||
|
|
20653847f4 | ||
|
|
3e294a3ba4 | ||
|
|
20ca6f96f9 | ||
|
|
70a30a5f33 | ||
|
|
b9f170e668 | ||
|
|
809dfbe338 | ||
|
|
af25a82eca | ||
|
|
50b1fcadb4 | ||
|
|
aefe1b2f07 | ||
|
|
28bc30bd83 | ||
|
|
0aeffcd79d | ||
|
|
f3863811bd | ||
|
|
e4aab2fd15 | ||
|
|
5f72a6b855 | ||
|
|
31a0444409 | ||
|
|
4522935545 | ||
|
|
67c13bd416 | ||
|
|
28d5af5fa0 | ||
|
|
15d7e71e7f | ||
|
|
49bac975d2 | ||
|
|
3878c9a1e2 | ||
|
|
a5053c4418 | ||
|
|
4cb844dea8 | ||
|
|
04afad2fd3 | ||
|
|
149efe3199 | ||
|
|
6f05867563 | ||
|
|
4442fd3265 | ||
|
|
6e49856474 | ||
|
|
cc379b71de | ||
|
|
c2a1bb4a5a | ||
|
|
cc58c33e37 | ||
|
|
012f03f7e7 | ||
|
|
28a7a3c389 | ||
|
|
6417279d65 | ||
|
|
07db680ad3 | ||
|
|
d7c97b6589 | ||
|
|
af12289d7e | ||
|
|
afeafba67d | ||
|
|
f56e49da5c | ||
|
|
d233897c73 | ||
|
|
273f1c3d07 | ||
|
|
d34812b77b | ||
|
|
375a60bafc | ||
|
|
156bd57461 | ||
|
|
dacc1b8eeb | ||
|
|
b3bb023852 | ||
|
|
8ca8272bc4 | ||
|
|
fc36f7ad77 | ||
|
|
496bdf5b54 | ||
|
|
877d625945 | ||
|
|
458710e9f0 | ||
|
|
fa6e7e44a0 | ||
|
|
bc12220d7a | ||
|
|
9d7b879e1f | ||
|
|
e4ff8a169b | ||
|
|
5a62aea978 | ||
|
|
b4d9de74c3 | ||
|
|
aa37d8b4b3 | ||
|
|
e334bc6440 | ||
|
|
9f6b9923b0 | ||
|
|
85415ae7fc | ||
|
|
f5382e9649 | ||
|
|
3ca786b820 | ||
|
|
c13908f096 | ||
|
|
d11de94e0b | ||
|
|
e2c10f7e87 | ||
|
|
b05c717981 | ||
|
|
38db5dfee4 | ||
|
|
151eeb5ec1 | ||
|
|
0d150765b8 | ||
|
|
cd6ee3048c | ||
|
|
0ed9e62283 | ||
|
|
fdb89fbf64 | ||
|
|
3fd11831f6 | ||
|
|
cd81fc8b38 | ||
|
|
b512b7d5f7 | ||
|
|
883cb34bdb | ||
|
|
c683947d20 | ||
|
|
483204a811 | ||
|
|
0b5b7bcae2 | ||
|
|
e1d517f9c2 | ||
|
|
b80b869823 | ||
|
|
386a6cbb6c | ||
|
|
9ce4c0ee90 | ||
|
|
1351b5ff90 | ||
|
|
84960a6107 | ||
|
|
5d3fdc0abd | ||
|
|
0d491f0d41 | ||
|
|
955ae91110 | ||
|
|
85fa63d2a1 | ||
|
|
6d8f02435e | ||
|
|
fc0600dfd8 | ||
|
|
1494579353 | ||
|
|
834d7e1da1 | ||
|
|
08b64834cf | ||
|
|
8265c5adec | ||
|
|
be4f2398a9 | ||
|
|
b8f2d36f5e | ||
|
|
49a2526528 | ||
|
|
a755d3d55e | ||
|
|
41a6ddb62a | ||
|
|
e32acd17ba | ||
|
|
0de0add858 | ||
|
|
9a90ca4c53 | ||
|
|
86b1fa6276 | ||
|
|
08d5fb50a7 | ||
|
|
6fca7c7940 | ||
|
|
fad14b26b6 | ||
|
|
d33fe6e185 | ||
|
|
a1db85e50f | ||
|
|
e67f1a649c | ||
|
|
e88fe21802 | ||
|
|
21875b8dd5 | ||
|
|
8f4c6a5b32 | ||
|
|
ba696abe0c | ||
|
|
7f32d8d136 | ||
|
|
e0af365ef3 | ||
|
|
caa85cef49 | ||
|
|
0cb6902519 | ||
|
|
7226473dcb | ||
|
|
222fdf51b5 | ||
|
|
381ba0f69a | ||
|
|
e4170b5f16 | ||
|
|
81294ac9be | ||
|
|
5ae0110153 | ||
|
|
930c2ef9bb | ||
|
|
f109fb33d0 | ||
|
|
eae4f99b34 | ||
|
|
241f0bec43 | ||
|
|
01ea68560f | ||
|
|
03ae42c98a | ||
|
|
ab060b0981 | ||
|
|
89ef96f2b4 | ||
|
|
b7a1af9f64 | ||
|
|
102db1f46d | ||
|
|
cc35404c7f | ||
|
|
c07fccf607 |
@@ -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
|
||||
@@ -68,27 +69,30 @@
|
||||
* Результаты опросов без участия
|
||||
* Возможность указывать папку для загрузки фото/видео/музыки/всего
|
||||
* Интеграция с <a href="https://www.last.fm/">LAST.FM</a> (а также возможность скробблинга)
|
||||
* Новый раздел музыки
|
||||
* Возможность скачивать видео
|
||||
* Возможность скачивать видео, очищать историю просмотра видео
|
||||
* Возможность найти фотографию через поисковые системы
|
||||
* Интеграция с <a href="https://vkx.app/">VK X</a>
|
||||
* Возможность кешировать треки в приложении
|
||||
* Сообщения
|
||||
* Обои в сообщениях
|
||||
* Возможность выключения кнопки записи голосовых сообщений
|
||||
* Возможность скрыть блок рекомендаций в сообщениях
|
||||
* Режим **VK Me**
|
||||
* Возможность вернуть вкладку уведомлений
|
||||
* Возможность вернуть вкладку уведомлений для режима VK Me
|
||||
* Встроенный переводчик текста для сообщений
|
||||
* Возможность выбрать язык перевода
|
||||
* Автоматический перевод ваших сообщений на выбранный язык
|
||||
* Автоматический перевод ваших сообщений на выбранный язык
|
||||
* Альтернативный дизайн эмодзи меню
|
||||
* Возможность использовать системные эмодзи
|
||||
* Возможнсть использовать Enter с аппаратной клавиатуры
|
||||
* Панель ввода из приложения ВК для IOS
|
||||
* Активность
|
||||
* Возможность видеть удаленные сообщения
|
||||
* Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
|
||||
* Нечиталка/неписалка для ЛС/бесед/ботов/групп
|
||||
* Возможность отключить информацию о том, что вы просмотрели историю/прослушали голосовое сообщение
|
||||
* Возможность видеть онлайн пользователей, если у вас скрыт онлайн через VK Messenger
|
||||
* Возможность сохранять настройки бесшумных сообщений/сообщений-бомбочек
|
||||
* Отключить уведомление о том, что вы сделали скриншот в беседе
|
||||
* Отключение редиректа через away.php
|
||||
* Отключение отправки метрики
|
||||
@@ -115,6 +119,7 @@
|
||||
* Возможность скрыть блок рекомендаций друзей в разделе «Друзья»
|
||||
* Возможность скрыть подписи в настройках
|
||||
* Прокси
|
||||
* Поддержка сторонних прокси, таких как: 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.
@@ -1 +0,0 @@
|
||||
5df53281cbfd1b6caf676bd10d2a403361a2904b
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
package com.vk.core.util;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.getDownloadsDir;
|
||||
import static ru.vtosters.lite.utils.Preferences.getMusicDir;
|
||||
import static ru.vtosters.lite.utils.Preferences.getPhotosDir;
|
||||
import static ru.vtosters.lite.utils.Preferences.getVideosDir;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
@@ -13,6 +8,7 @@ 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;
|
||||
|
||||
@@ -25,6 +21,8 @@ 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;
|
||||
@@ -51,49 +49,62 @@ public class DownloadUtils {
|
||||
ToastUtils.a(context.getString(R.string.error) + " [" + e2.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void b(final Context context, String str, String str2, boolean z) {
|
||||
if (TextUtils.isEmpty(str2)) {
|
||||
|
||||
private static void b(final Context context, String str, String uri, boolean z) {
|
||||
if (TextUtils.isEmpty(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String file = a(str, str2);
|
||||
File b2;
|
||||
boolean c2 = MediaUtils.c(MediaUtils.b(str2));
|
||||
String fileName = a(str, uri);
|
||||
File file;
|
||||
|
||||
if (file.endsWith(".jpg") || file.endsWith(".png") || file.endsWith(".jpeg")) {
|
||||
b2 = new File(getPhotosDir(), file);
|
||||
} else if (file.endsWith(".mp3") || file.endsWith(".wav") || file.endsWith(".ogg") || file.endsWith(".aac") || file.endsWith(".flac") || file.endsWith(".m4a")) {
|
||||
b2 = new File(getMusicDir(), file);
|
||||
} else if (file.endsWith(".mp4") || file.endsWith(".mkv") || file.endsWith(".avi") || file.endsWith(".flv") || file.endsWith(".wmv") || file.endsWith(".mov") || file.endsWith(".3gp")) {
|
||||
b2 = new File(getVideosDir(), 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 {
|
||||
b2 = new File(getDownloadsDir(), file);
|
||||
file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(), fileName);
|
||||
}
|
||||
|
||||
if (c2) {
|
||||
EncodedImage c3 = VKImageLoader.c(Uri.parse(str2));
|
||||
final InputStream h = c3 == null ? null : c3.h();
|
||||
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 (b2.getParentFile() != null) {
|
||||
b2.createNewFile();
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (b2.exists()) {
|
||||
VkExecutors.x.a().execute(() -> ToastUtils.a(context.getString(R.string.file_saved, b2.getAbsoluteFile())));
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
L.a(e2);
|
||||
}
|
||||
CameraUtils.a(context, b2, null);
|
||||
|
||||
CameraUtils.a(context, file, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(str2));
|
||||
request.setDestinationUri(Uri.fromFile(b2));
|
||||
request.setMimeType(FileUtils.f(new File(str)));
|
||||
|
||||
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);
|
||||
@@ -182,10 +193,12 @@ public class DownloadUtils {
|
||||
}
|
||||
Intent intent = new Intent("android.intent.action.VIEW");
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
String f2 = FileUtils.f(file);
|
||||
if (!TextUtils.isEmpty(f2)) {
|
||||
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(FileUtils.uriFromFile(file), f2);
|
||||
intent.setDataAndType(uri, mimeType);
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
@@ -206,9 +219,13 @@ public class DownloadUtils {
|
||||
|
||||
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.parse(file.getAbsolutePath()), "application/*");
|
||||
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;
|
||||
|
||||
@@ -35,5 +35,6 @@ public enum DialogAction {
|
||||
pinmsg,
|
||||
unpinmsg,
|
||||
ENCRYPT_SETT,
|
||||
ENCRYPT
|
||||
ENCRYPT,
|
||||
DOWNLOAD
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
@@ -36,8 +35,6 @@ import com.vtosters.lite.a0;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
@UiThread
|
||||
public class TargetImageView extends VKMultiImageView {
|
||||
private static final int I = Screen.a(2);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.vk.stories.view;
|
||||
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getColor2;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
@@ -29,7 +27,6 @@ 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.drawable.BorderDrawable;
|
||||
import com.vk.core.ui.themes.Themable;
|
||||
import com.vk.core.ui.themes.VKThemeHelper;
|
||||
import com.vk.core.util.DrawableUtils;
|
||||
@@ -50,8 +47,6 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class StoryCircleImageView extends BorderedCircleImageView implements Themable{
|
||||
private static final HashSet<Integer> B0 = new HashSet<>();
|
||||
private final PipelineDraweeControllerBuilder f0;
|
||||
|
||||
+1
-2
@@ -1,6 +1,6 @@
|
||||
package com.vtosters.lite.ui.bottomnavigation;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.*;
|
||||
import static ru.vtosters.lite.utils.Preferences.vkme;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getCSTDock;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -31,7 +31,6 @@ import com.vk.core.ui.themes.VKThemeHelper;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import me.grishka.appkit.utils.V;
|
||||
import ru.vtosters.lite.utils.Preferences;
|
||||
|
||||
public class BottomNavigationItemView extends FrameLayout implements MenuView.ItemView {
|
||||
private final TextView mSmallLabel;
|
||||
|
||||
@@ -1,58 +1,22 @@
|
||||
package ru.vtosters.lite.deviceinfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import static ru.vtosters.lite.utils.About.getBuildNumber;
|
||||
|
||||
public class Device {
|
||||
private String androidId;
|
||||
private String bluetoothMac;
|
||||
private String boardName;
|
||||
private String brandName;
|
||||
private String cpuAbi;
|
||||
private String deviceName;
|
||||
private String firmwareDisplay;
|
||||
private String firmwareId;
|
||||
private String manufacturerName;
|
||||
private String modelName;
|
||||
private String productName;
|
||||
private int sdkVersion;
|
||||
private String serialId;
|
||||
private String wifiMac;
|
||||
|
||||
public Device withSerialId(String str) {
|
||||
this.serialId = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withAndroidId(String str) {
|
||||
this.androidId = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withWifiMac(String str) {
|
||||
this.wifiMac = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withBluetoothMac(String str) {
|
||||
this.bluetoothMac = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withSdkVersion(int i) {
|
||||
this.sdkVersion = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withFirmwareId(String str) {
|
||||
this.firmwareId = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withFirmwareDisplay(String str) {
|
||||
this.firmwareDisplay = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withProductName(String str) {
|
||||
this.productName = str;
|
||||
return this;
|
||||
@@ -68,11 +32,6 @@ public class Device {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withCpuAbi(String str) {
|
||||
this.cpuAbi = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Device withManufacturerName(String str) {
|
||||
this.manufacturerName = str;
|
||||
return this;
|
||||
@@ -88,12 +47,7 @@ public class Device {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "Device{serialId='" + this.serialId + "', androidId='" + this.androidId + "', wifiMac='" + this.wifiMac + "', bluetoothMac='" + this.bluetoothMac + "'}";
|
||||
}
|
||||
|
||||
public String toDeviceName() {
|
||||
return "Device{sdkVersion=" + this.sdkVersion + ", productName='" + this.productName + "', deviceName='" + this.deviceName + "', boardName='" + this.boardName + "', cpuAbi='" + this.cpuAbi + "', manufacturerName='" + this.manufacturerName + "', brandName='" + this.brandName + "', modelName='" + this.modelName + "'}";
|
||||
return "Device information: " + "commit='" + getBuildNumber() + "', sdkVersion=" + this.sdkVersion + ", productName='" + this.productName + "', deviceName='" + this.deviceName + "', boardName='" + this.boardName + "', manufacturerName='" + this.manufacturerName + "', brandName='" + this.brandName + "', modelName='" + this.modelName + "'";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,195 +1,11 @@
|
||||
package ru.vtosters.lite.deviceinfo;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.os.EnvironmentCompat;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Collections;
|
||||
|
||||
public class DeviceInfoCollector {
|
||||
private static final String TAG = "DeviceInfoCollector";
|
||||
|
||||
public Device collect(Context context) {
|
||||
return new Device().withSerialId(serialId()).withAndroidId(androidId(context)).withWifiMac(wifiMac(context)).withBluetoothMac(bluetoothMac(context)).withSdkVersion(Build.VERSION.SDK_INT).withFirmwareId(Build.ID).withFirmwareDisplay(Build.DISPLAY).withProductName(Build.PRODUCT).withDeviceName(Build.DEVICE).withBoardName(Build.BOARD).withCpuAbi(Build.CPU_ABI).withManufacturerName(Build.MANUFACTURER).withBrandName(Build.BRAND).withModelName(Build.MODEL);
|
||||
}
|
||||
|
||||
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");
|
||||
if (isInvalidSerial(str)) {
|
||||
str = (String) method.invoke(cls, "ril.serialnumber");
|
||||
}
|
||||
if (isInvalidSerial(str)) {
|
||||
str = (String) method.invoke(cls, "ro.serialno");
|
||||
}
|
||||
if (isInvalidSerial(str)) {
|
||||
str = (String) method.invoke(cls, "sys.serialnumber");
|
||||
}
|
||||
if (isInvalidSerial(str)) {
|
||||
str = Build.SERIAL;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Serial ID obtaining failed: ", e);
|
||||
}
|
||||
str = null;
|
||||
if (str != null) {
|
||||
return str.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
private String androidId(Context context) {
|
||||
return Settings.Secure.getString(context.getContentResolver(), "android_id").toLowerCase();
|
||||
}
|
||||
|
||||
private String wifiMac(Context context) {
|
||||
String wifiMacFromManager = wifiMacFromManager(context);
|
||||
if (isInvalidMac(wifiMacFromManager)) {
|
||||
wifiMacFromManager = wifiMacFromNetworkInterfaces();
|
||||
}
|
||||
if (isInvalidMac(wifiMacFromManager)) {
|
||||
wifiMacFromManager = wifiMacFromFileSystem();
|
||||
}
|
||||
if (isInvalidMac(wifiMacFromManager)) {
|
||||
wifiMacFromManager = null;
|
||||
}
|
||||
if (wifiMacFromManager != null) {
|
||||
return wifiMacFromManager.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String wifiMacFromManager(Context context) {
|
||||
WifiInfo connectionInfo;
|
||||
try {
|
||||
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
|
||||
if (wifiManager == null || (connectionInfo = wifiManager.getConnectionInfo()) == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "WiFi MAC obtaining from manager failed: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String wifiMacFromNetworkInterfaces() {
|
||||
try {
|
||||
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
|
||||
if ("wlan0".equalsIgnoreCase(networkInterface.getName())) {
|
||||
byte[] hardwareAddress = networkInterface.getHardwareAddress();
|
||||
if (hardwareAddress == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = hardwareAddress.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(String.format("%02X:", hardwareAddress[i]));
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "WiFi MAC obtaining from network interfaces failed: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String wifiMacFromFileSystem() {
|
||||
try {
|
||||
FileReader fileReader = new FileReader("/sys/class/net/wlan0/address");
|
||||
try {
|
||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
||||
try {
|
||||
String readLine = bufferedReader.readLine();
|
||||
bufferedReader.close();
|
||||
fileReader.close();
|
||||
return readLine;
|
||||
} catch (Throwable th) {
|
||||
th.addSuppressed(th);
|
||||
}
|
||||
} catch (Throwable th2) {
|
||||
throw th2;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "WiFi MAC obtaining from file system failed: ", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String bluetoothMac(Context context) {
|
||||
String bluetoothMacFromAdapter = bluetoothMacFromAdapter();
|
||||
if (isInvalidMac(bluetoothMacFromAdapter)) {
|
||||
bluetoothMacFromAdapter = bluetoothMacFromContentResolver(context);
|
||||
}
|
||||
if (isInvalidMac(bluetoothMacFromAdapter)) {
|
||||
bluetoothMacFromAdapter = null;
|
||||
}
|
||||
if (bluetoothMacFromAdapter != null) {
|
||||
return bluetoothMacFromAdapter.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String bluetoothMacFromAdapter() {
|
||||
try {
|
||||
BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (defaultAdapter == null) {
|
||||
return null;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return null;
|
||||
}
|
||||
Field declaredField = defaultAdapter.getClass().getDeclaredField("mService");
|
||||
declaredField.setAccessible(true);
|
||||
Object obj = declaredField.get(defaultAdapter);
|
||||
if (obj != null) {
|
||||
return (String) obj.getClass().getMethod("getAddress").invoke(obj, new Object[0]);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "BT MAC obtaining from adapter failed: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String bluetoothMacFromContentResolver(Context context) {
|
||||
try {
|
||||
return Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "BT MAC obtaining from content resolver failed: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidSerial(String str) {
|
||||
return TextUtils.isEmpty(str) || EnvironmentCompat.MEDIA_UNKNOWN.equalsIgnoreCase(str);
|
||||
}
|
||||
|
||||
private boolean isInvalidMac(String str) {
|
||||
return TextUtils.isEmpty(str) || "02:00:00:00:00:00".equalsIgnoreCase(str) || "00:00:00:00:00:00".equalsIgnoreCase(str);
|
||||
public Device collect() {
|
||||
return new Device().withSdkVersion(Build.VERSION.SDK_INT).withProductName(Build.PRODUCT).withDeviceName(Build.DEVICE).withBoardName(Build.BOARD).withManufacturerName(Build.MANUFACTURER).withBrandName(Build.BRAND).withModelName(Build.MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,26 +5,28 @@ 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.engine.h.im_ic_write_msg;
|
||||
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.ProfilesSimpleInfo;
|
||||
import com.vk.im.engine.models.dialogs.Dialog;
|
||||
import com.vk.im.engine.models.messages.Msg;
|
||||
import com.vk.im.engine.models.messages.MsgFromUser;
|
||||
@@ -33,6 +35,7 @@ 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;
|
||||
@@ -41,20 +44,30 @@ 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;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class DNRInjector {
|
||||
public static void inject(Dialog dialog, List<DialogAction> list) {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
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);
|
||||
|
||||
@@ -78,6 +91,8 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
public static LinkedHashMap<DialogAction, Integer> injectToHashMap(LinkedHashMap<DialogAction, Integer> hashMap) {
|
||||
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"));
|
||||
@@ -94,6 +109,7 @@ public class DNRInjector {
|
||||
@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
|
||||
|
||||
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
|
||||
@@ -101,8 +117,10 @@ public class DNRInjector {
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@@ -119,9 +137,19 @@ public class DNRInjector {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
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 {
|
||||
@@ -148,7 +176,15 @@ public class DNRInjector {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
try {
|
||||
peerId = dialog.getId();
|
||||
} catch (Exception e) {
|
||||
Log.d("DialoggetId", "null");
|
||||
}
|
||||
}
|
||||
|
||||
if (peerId == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == DialogAction.MARK_AS_READ) {
|
||||
@@ -156,6 +192,17 @@ public class DNRInjector {
|
||||
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);
|
||||
@@ -191,13 +238,31 @@ public class DNRInjector {
|
||||
int peerId = 0;
|
||||
|
||||
if (dialog != null) {
|
||||
peerId = dialog.getId();
|
||||
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.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;
|
||||
@@ -240,37 +305,40 @@ public class DNRInjector {
|
||||
}
|
||||
|
||||
public static boolean onClickMsg(Context context, MsgAction action, Msg msg) {
|
||||
var text = ((MsgFromUser) msg).f();
|
||||
var peerId = msg.v1();
|
||||
var fullMsg = ((MsgFromUser) msg).j2(); // text + reply
|
||||
var reply = fullMsg.substring(text.length() + 1);
|
||||
|
||||
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);
|
||||
|
||||
var isTextExist = !text.isEmpty() && !text.equals(" ");
|
||||
var isReplyExist = !reply.isEmpty() && !reply.equals(" ");
|
||||
|
||||
|
||||
if (isReplyExist) {
|
||||
reply = decryptMessage(reply, peerId);
|
||||
}
|
||||
if (isReplyExist) {
|
||||
reply = decryptMessage(reply, peerId);
|
||||
}
|
||||
|
||||
if (isTextExist) {
|
||||
text = decryptMessage(text, peerId);
|
||||
}
|
||||
if (isTextExist) {
|
||||
text = decryptMessage(text, peerId);
|
||||
}
|
||||
|
||||
if (action == MsgAction.valueOf("TRANSLATE")) {
|
||||
if (isTextExist && isReplyExist) {
|
||||
Translate.showTranslatedText(context, text + "\n\n" + AndroidUtils.getString("translator_reply") + "\n" + reply);
|
||||
} else if (isReplyExist) {
|
||||
Translate.showTranslatedText(context, AndroidUtils.getString("translator_reply") + "\n" + reply);
|
||||
} else if (isTextExist) {
|
||||
Translate.showTranslatedText(context, text);
|
||||
} else {
|
||||
sendToast(AndroidUtils.getString("translator_no_text"));
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,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();
|
||||
|
||||
@@ -298,7 +366,7 @@ public class DNRInjector {
|
||||
|
||||
thread.start();
|
||||
|
||||
sendToast(AndroidUtils.getString("pin_dialog") + " " + (needToBePinned ? AndroidUtils.getString("dialog_pinned") : AndroidUtils.getString("dialog_unpinned")));
|
||||
sendToast(getString("pin_dialog") + " " + getString(needToBePinned ? "dialog_pinned" : "dialog_unpinned"));
|
||||
}
|
||||
|
||||
public static void setDnr(Dialog dialog, boolean value) {
|
||||
|
||||
@@ -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,121 +1,133 @@
|
||||
package ru.vtosters.lite.downloaders;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
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 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());
|
||||
|
||||
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(AndroidUtils.getString("link_audio_error"));
|
||||
ToastUtils.a(getString("link_audio_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
|
||||
var musicDir = new File(musicPath);
|
||||
if (!musicDir.exists()) {
|
||||
if (musicDir.mkdirs()) {
|
||||
Log.d("AudioDownloader", "Created music dir");
|
||||
} else {
|
||||
Log.e("AudioDownloader", "Failed to create music dir");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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(AndroidUtils.getString("player_download_finished"))
|
||||
.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", "native libs");
|
||||
Log.e("AudioDownloader", e.getMessage());
|
||||
onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
notification.setContentText(AndroidUtils.getString("load_audio_error")).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(AndroidUtils.getString("audio_downloading"))
|
||||
.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,605 @@
|
||||
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 final String VK_DOMAIN = "vk.com";
|
||||
private static SimpleDateFormat sdformat;
|
||||
private static SparseArray<MiniUser> usersArray;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -56,16 +58,35 @@ 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();
|
||||
|
||||
new VkAlertDialog.Builder(context)
|
||||
.setTitle(AndroidUtils.getString("update_error"))
|
||||
.setMessage(AndroidUtils.getString("update_error_text"))
|
||||
.setPositiveButton(AndroidUtils.getString("no"),
|
||||
(dialog, which) -> dialog.cancel()
|
||||
)
|
||||
.setNeutralButton(AndroidUtils.getString("yes"),
|
||||
(dialog, which) -> {
|
||||
var intent = new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse("https://github.com/vtosters/lite/releases/latest/download/VTLite.apk"));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package ru.vtosters.lite.downloaders;
|
||||
|
||||
import static ru.vtosters.lite.downloaders.VideoDownloader.downloadVideo;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.Preferences.getPhotosDir;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
@@ -23,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(getPhotosDir().getAbsolutePath(), "/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,13 +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 static ru.vtosters.lite.utils.Preferences.getVideosDir;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
@@ -15,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;
|
||||
@@ -106,21 +106,23 @@ 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.setDestinationInExternalPublicDir(getVideosDir().getAbsolutePath(), videoFile + ".mp4");
|
||||
request.allowScanningByMediaScanner();
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
|
||||
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
return;
|
||||
}
|
||||
|
||||
var builder = new AlertDialog.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.setDestinationInExternalPublicDir(getVideosDir().getAbsolutePath(), videoFile + ".mp4");
|
||||
((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
|
||||
}));
|
||||
builder.show();
|
||||
new VkAlertDialog.Builder(context)
|
||||
.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);
|
||||
}))
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void parseVideoLink(String url, Context ctx) {
|
||||
@@ -141,7 +143,7 @@ public class VideoDownloader {
|
||||
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();
|
||||
|
||||
|
||||
+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 = AndroidUtils.getString("music_dl_title");
|
||||
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = AndroidUtils.getString("music_dl_progress");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -52,6 +53,51 @@ public class EncryptProvider {
|
||||
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) {
|
||||
return decryptMessage(getBody(msg), getPeerId(msg));
|
||||
|
||||
@@ -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,22 +2,23 @@ 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;
|
||||
@@ -37,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 {
|
||||
@@ -65,10 +67,52 @@ public class FoafBase {
|
||||
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 + 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 {
|
||||
@@ -99,8 +143,15 @@ public class FoafBase {
|
||||
matcher2.find();
|
||||
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();
|
||||
|
||||
new VkAlertDialog.Builder(context)
|
||||
.setTitle(getIdentifier("addinf", "string"))
|
||||
.setMessage(getString("foafid") +
|
||||
" " + i + getString("foafregdate")
|
||||
+ " " + normalHumanDate + getString("foafdate")
|
||||
+ " " + daysPassedFromFoafDate(normalHumanDate))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
makeText(getGlobalContext(), getString("foaferr"), LENGTH_SHORT).show();
|
||||
@@ -119,7 +170,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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
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;
|
||||
@@ -6,6 +7,7 @@ import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
import static ru.vtosters.lite.utils.LifecycleUtils.getCurrentActivity;
|
||||
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;
|
||||
@@ -13,18 +15,17 @@ 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){
|
||||
public class CryptImHook {
|
||||
public static boolean isPrivateProcessor(int peerID) {
|
||||
for (IMProcessor processor : EncryptProvider.processors) {
|
||||
if (processor.isUsedToEncrypt(peerID)) {
|
||||
return !processor.isPublic();
|
||||
@@ -34,7 +35,7 @@ public class CryptImHook{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void hook(int peerId, Dialog d){
|
||||
public static void hook(int peerId, Dialog d) {
|
||||
List<IMProcessor> processors = EncryptProvider.processors;
|
||||
|
||||
IMProcessor enabled = null;
|
||||
@@ -55,7 +56,14 @@ public class CryptImHook{
|
||||
}
|
||||
|
||||
IMProcessor finalEnabled = enabled;
|
||||
new VkAlertDialog.Builder(getCurrentActivity())
|
||||
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) {
|
||||
@@ -73,11 +81,14 @@ public class CryptImHook{
|
||||
|
||||
dialog.dismiss();
|
||||
forceInvalidateDialogActions(d);
|
||||
}).create().show();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void hookPref(int peerId){
|
||||
public static void hookPref(int peerId) {
|
||||
IMProcessor enabled = null;
|
||||
String key = null;
|
||||
|
||||
for (IMProcessor processor : EncryptProvider.processors) {
|
||||
if (processor.isUsedToEncrypt(peerId)) {
|
||||
enabled = processor;
|
||||
@@ -89,8 +100,21 @@ public class CryptImHook{
|
||||
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);
|
||||
@@ -102,15 +126,17 @@ public class CryptImHook{
|
||||
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)
|
||||
new VkAlertDialog.Builder(ctx)
|
||||
.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();
|
||||
}).setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
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.content.Context;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class DeviceInfoHook{
|
||||
public class DeviceInfoHook {
|
||||
public static String getDeviceInfo(String input) {
|
||||
Log.d("DeviceInfoHook", "getDeviceInfo: " + input);
|
||||
if (getBoolValue("hideDeviceInfo", true)) return "";
|
||||
|
||||
@@ -82,7 +82,8 @@ public class DockBarInjector {
|
||||
} while (acquire.invoke(synchronizedPool) != null);
|
||||
}
|
||||
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
|
||||
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException |
|
||||
NoSuchMethodException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -286,7 +287,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 +295,7 @@ public class DockBarInjector {
|
||||
if (id == tab.id)
|
||||
return tab.fragmentClass;
|
||||
}
|
||||
|
||||
for (DockBarTab tab : sManager.getDisabledTabs()) {
|
||||
if (id == tab.id)
|
||||
return tab.fragmentClass;
|
||||
|
||||
@@ -29,7 +29,6 @@ import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class GcmHook {
|
||||
private static final String agent = String.format("Android-GCM/1.5 (%s %s)", Build.MODEL, Build.MODEL);
|
||||
public static ArrayList<String> langs = new ArrayList<>();
|
||||
private static KeyPair pair;
|
||||
private static int rid = 0;
|
||||
|
||||
@@ -38,12 +37,12 @@ public class GcmHook {
|
||||
}
|
||||
|
||||
public static String requestTokenV2(String orig) {
|
||||
if (musicFixNew()) return "null";
|
||||
if (musicFixNew()) return "null"; //not public
|
||||
return orig;
|
||||
}
|
||||
|
||||
public static String requestToken(String orig) {
|
||||
if (musicFixNew()) return "{null}";
|
||||
if (musicFixNew()) return "{null}"; //not public
|
||||
if (orig.equals("")) return requestToken();
|
||||
return orig;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
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.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.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.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;
|
||||
@@ -45,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();
|
||||
@@ -116,7 +108,7 @@ public class JsonInjectors {
|
||||
public static JSONObject vktesters(int id) throws JSONException {
|
||||
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();
|
||||
@@ -162,23 +154,17 @@ public class JsonInjectors {
|
||||
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 (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.equals("")) {
|
||||
|
||||
if (!isVerified(peerid) || text.equals("") || peerid == AccountManagerUtils.getUserId() || peerid == 0) {
|
||||
if (getBoolValue("convBarRecomm", false)) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -186,14 +172,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 {
|
||||
@@ -238,30 +237,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");
|
||||
}
|
||||
}
|
||||
@@ -269,109 +250,81 @@ 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 {
|
||||
@@ -411,76 +364,6 @@ public class JsonInjectors {
|
||||
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");
|
||||
|
||||
@@ -565,10 +448,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"
|
||||
@@ -592,7 +475,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"
|
||||
@@ -612,8 +495,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;
|
||||
|
||||
@@ -678,10 +561,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,7 +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.DeletedMessagesHandler.reloadMessagesList;
|
||||
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;
|
||||
@@ -11,21 +11,24 @@ 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);
|
||||
reloadMessagesList();
|
||||
VkExecutors.x.f().a(DeletedMessagesHandler::reloadMessagesList); // ioScheduler
|
||||
Start.alert(activity);
|
||||
InstallGMS.alert(activity);
|
||||
DisableBattery.alert(activity);
|
||||
@@ -33,7 +36,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,22 +7,23 @@ 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) {
|
||||
if (!autotranslate() || TextUtils.isEmpty(oldText))
|
||||
return oldText;
|
||||
var instance = BaseTranslator.getInstance();
|
||||
var instance = BaseTranslator.getInstance();
|
||||
|
||||
return replaceMentions(oldText, instance);
|
||||
}
|
||||
@@ -45,7 +46,8 @@ public class MessagesHook {
|
||||
var mentions = new ArrayList<String>();
|
||||
while (matcher.find()) {
|
||||
mentions.add(matcher.group());
|
||||
matcher.appendReplacement(textBuff, "vtl_mention" + mentionsCount + ""); ++mentionsCount;
|
||||
matcher.appendReplacement(textBuff, "vtl_mention" + mentionsCount + "");
|
||||
++mentionsCount;
|
||||
}
|
||||
matcher.appendTail(textBuff);
|
||||
|
||||
@@ -60,7 +62,7 @@ public class MessagesHook {
|
||||
matcherMentions.appendTail(retTextBuff);
|
||||
|
||||
return retTextBuff.toString();
|
||||
} catch(Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return instance.getTranslation(oldText);
|
||||
}
|
||||
@@ -73,6 +75,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,19 +1,9 @@
|
||||
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;
|
||||
@@ -21,43 +11,21 @@ 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;
|
||||
@@ -79,7 +47,7 @@ public class NewsfeedHook {
|
||||
return list;
|
||||
}
|
||||
|
||||
public static String[] feedParams(){
|
||||
public static String[] feedParams() {
|
||||
HashSet<String> params = new HashSet<>();
|
||||
params.add("post");
|
||||
params.add("photo");
|
||||
@@ -101,19 +69,61 @@ public class NewsfeedHook {
|
||||
return params.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static void adsParams(HashSet<String> hashSet) {
|
||||
if (ads() || adsslider()) {
|
||||
hashSet.add("ads_disabled");
|
||||
return;
|
||||
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");
|
||||
hashSet.add("ads_app");
|
||||
hashSet.add("ads_site");
|
||||
hashSet.add("ads_post");
|
||||
hashSet.add("ads_app_slider");
|
||||
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;
|
||||
@@ -127,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);
|
||||
}
|
||||
@@ -167,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;
|
||||
@@ -66,7 +67,19 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_menu_search_outline_28), ThemesUtils.getAccentColor()),
|
||||
false,
|
||||
() -> {
|
||||
searchPhoto(getUrlFromPhotoAttachment((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;
|
||||
}
|
||||
);
|
||||
@@ -76,7 +89,19 @@ public class PhotoViewer {
|
||||
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_copy_outline_28), Color.WHITE),
|
||||
false,
|
||||
() -> {
|
||||
copyPhotoUrl(getUrlFromPhotoAttachment((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;
|
||||
}
|
||||
);
|
||||
@@ -92,9 +117,13 @@ public class PhotoViewer {
|
||||
} else if (attachment instanceof DocumentAttachment) {
|
||||
var documentAttachment = (DocumentAttachment) attachment;
|
||||
if (documentAttachment.J != null)
|
||||
openUrl(getUrlFromDocumentAttachment(documentAttachment));
|
||||
try {
|
||||
openUrl(getUrlFromDocumentAttachment(documentAttachment));
|
||||
} catch (Exception e) {
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
else
|
||||
ToastUtils.a("Не удалось получить ссылку на фото");
|
||||
ToastUtils.a(getString("photo_get_error"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -102,7 +131,10 @@ public class PhotoViewer {
|
||||
}
|
||||
|
||||
private static String getUrlFromDocumentAttachment(DocumentAttachment attachment) {
|
||||
var imageSizes = attachment.J.t1();
|
||||
List<ImageSize> imageSizes = null;
|
||||
if (attachment.J != null) {
|
||||
imageSizes = attachment.J.t1();
|
||||
}
|
||||
return getUrlFromImageSizeList(imageSizes);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.preferences;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
@@ -12,7 +13,7 @@ import com.facebook.drawee.generic.RoundingParams;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class PicRoundingHook{
|
||||
public class PicRoundingHook {
|
||||
private static final int pref = preferences.getInt("pic_rounding", 0);
|
||||
|
||||
public static RoundingParams inject() {
|
||||
@@ -39,7 +40,7 @@ public class PicRoundingHook{
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(float orig, Canvas canvas, Paint paint){
|
||||
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 {
|
||||
@@ -47,7 +48,7 @@ public class PicRoundingHook{
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(Canvas canvas, Paint paint, float orig){
|
||||
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 {
|
||||
@@ -55,7 +56,7 @@ public class PicRoundingHook{
|
||||
}
|
||||
}
|
||||
|
||||
public static void inject(Canvas canvas, float orig, float orig2, float orig3, Paint 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 {
|
||||
@@ -76,7 +77,7 @@ public class PicRoundingHook{
|
||||
}
|
||||
}
|
||||
|
||||
private static RoundingParams customParams(){
|
||||
private static RoundingParams customParams() {
|
||||
return RoundingParams.b(AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref), AndroidUtils.dp2px(pref));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import ru.vtosters.lite.utils.Preferences;
|
||||
public class PostViewHook {
|
||||
|
||||
/**
|
||||
* For displaying child elements with custom direction in rtl container we should remove their, set layout direction and add again */
|
||||
* For displaying child elements with custom direction in rtl container we should remove their, set layout direction and add again
|
||||
*/
|
||||
private static void saveChildDirectionForRlt(ViewGroup container) {
|
||||
var children = new View[container.getChildCount()];
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
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;
|
||||
|
||||
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;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
|
||||
import com.vk.auth.ui.VkAuthTextView;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vk.navigation.Navigator;
|
||||
|
||||
import ru.vtosters.lite.ui.fragments.ProxySettingsFragment;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class ProxyHook {
|
||||
public static void hookAuth(View v) {
|
||||
@@ -41,97 +38,64 @@ 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(AndroidUtils.getString("proxy_disable"));
|
||||
rgDefault.setTextColor(getTextAttr());
|
||||
|
||||
rgProxy.setText(AndroidUtils.getString("proxy_enable") + " (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());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx, getAlertStyle());
|
||||
builder.setTitle(AndroidUtils.getString("vtlproxy"));
|
||||
builder.setMessage(AndroidUtils.getString("proxy_warning"));
|
||||
builder.setView(rg);
|
||||
builder.setPositiveButton(AndroidUtils.getString("vtl_confirm"), ((dialog, which) -> { // Применить
|
||||
if (rgProxy.isChecked()) {
|
||||
edit().putString("proxy", "zaborona").commit();
|
||||
restartApplication();
|
||||
} else {
|
||||
if (isZaboronaEnabled()) {
|
||||
edit().putString("proxy", "noproxy").commit();
|
||||
restartApplication();
|
||||
}
|
||||
rgVika.setText(AndroidUtils.getString("proxy_enable") + " (Vika Mobile)");
|
||||
rgVika.setTextColor(getTextAttr());
|
||||
|
||||
edit().putString("proxy", "noproxy").commit();
|
||||
}
|
||||
}));
|
||||
rgZaborona.setChecked(isZaboronaEnabled());
|
||||
rgDefault.setChecked(!isZaboronaEnabled() && !isRandomProxyEnabled() && !isVikaProxyEnabled());
|
||||
rgRandomProxy.setChecked(isRandomProxyEnabled());
|
||||
rgVika.setChecked(isVikaProxyEnabled());
|
||||
|
||||
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);
|
||||
}));
|
||||
|
||||
var alert = builder.create();
|
||||
|
||||
alert.show();
|
||||
|
||||
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";
|
||||
new VkAlertDialog.Builder(ctx)
|
||||
.setTitle(AndroidUtils.getString("vtlproxy"))
|
||||
.setMessage(AndroidUtils.getString("proxy_warning"))
|
||||
.setView(rg)
|
||||
.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 {
|
||||
edit().putString("proxy", "noproxy").commit();
|
||||
restartApplication();
|
||||
}
|
||||
}))
|
||||
.setNeutralButton(AndroidUtils.getString("proxy_settings"), ((dialog, which) -> {
|
||||
var intent = new Navigator(ProxySettingsFragment.class)
|
||||
.b(ctx)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ctx.startActivity(intent);
|
||||
}))
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.dev;
|
||||
|
||||
import android.util.Log;
|
||||
@@ -7,10 +8,10 @@ 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 class RequestDumper {
|
||||
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()) {
|
||||
|
||||
@@ -2,20 +2,17 @@ package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.ui.components.DockBarEditorManager.getInstance;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
|
||||
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;
|
||||
@@ -30,7 +27,6 @@ import com.vtosters.lite.fragments.p2.DocumentsViewFragment;
|
||||
import com.vtosters.lite.fragments.s2.GroupsFragment1;
|
||||
import com.vtosters.lite.fragments.t2.c.DialogsFragment;
|
||||
import com.vtosters.lite.fragments.y2.VideoCatalogFragment;
|
||||
import com.vtosters.lite.fragments.y2.VideosFragment;
|
||||
import com.vtosters.lite.general.fragments.GamesFragment;
|
||||
import com.vtosters.lite.general.fragments.PhotosFragment;
|
||||
|
||||
@@ -47,19 +43,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,16 @@
|
||||
package ru.vtosters.lite.hooks;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.wbios;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class WritebarHook {
|
||||
public static int getWriteBar() {
|
||||
return AndroidUtils.getIdentifier((wbios() ? "write_bar_i" : "write_bar"), "layout");
|
||||
}
|
||||
|
||||
public static int getIconsColors(int def) {
|
||||
return wbios() ? ThemesUtils.getAccentColor() : def;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package ru.vtosters.lite.hooks.ui;
|
||||
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.asId;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.getInstance;
|
||||
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.isIntegrationEnabled;
|
||||
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.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,120 @@ 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,87 +1,76 @@
|
||||
package ru.vtosters.lite.music;
|
||||
|
||||
import static ru.vtosters.lite.utils.Preferences.getMusicDir;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
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(), AndroidUtils.getString("update_vk_admin_token"), 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.endsWith(".mp3")) {
|
||||
downloadMP3(track.toString(), url);
|
||||
} else if (VKM3UParser.isM3U8(url)) {
|
||||
AudioDownloader.downloadM3U8(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d("MP3Downloader", e + "");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
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(),
|
||||
getMusicDir().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,6 +20,7 @@ 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;
|
||||
@@ -86,6 +88,8 @@ public class Scrobbler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (autocache()) AudioDownloader.cacheTrack(musictrack);
|
||||
|
||||
scrobbleTrack(duration, artist, title, uid);
|
||||
}
|
||||
|
||||
@@ -120,7 +124,7 @@ public class Scrobbler {
|
||||
@Override
|
||||
public void onFailure(NetCall call, IOException e) {
|
||||
Log.d("Scrobbler", "Auth failed", e);
|
||||
sendToast(AndroidUtils.getString("lastfm_auth_error"));
|
||||
sendToast(AndroidUtils.getString("auth_error_vtl"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,10 @@ public class VKM3UParser {
|
||||
init();
|
||||
}
|
||||
|
||||
public static boolean isM3U8(String str) {
|
||||
return str.endsWith(".ts") || str.endsWith(".tp") || str.endsWith(".mpeg-ts") || str.endsWith(".m2ts");
|
||||
}
|
||||
|
||||
private void init() {
|
||||
int i = 0;
|
||||
Scanner scanner = new Scanner(mData);
|
||||
@@ -51,10 +55,6 @@ public class VKM3UParser {
|
||||
return directive.substring(directive.indexOf(":") + 1);
|
||||
}
|
||||
|
||||
public static boolean isM3U8(String str) {
|
||||
return str.endsWith(".ts") || str.endsWith(".tp") || str.endsWith(".mpeg-ts") || str.endsWith(".m2ts");
|
||||
}
|
||||
|
||||
public int getHeapSize() {
|
||||
return mHeapSize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getCacheDir;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getString;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
|
||||
import static ru.vtosters.lite.utils.IOUtils.deleteRecursive;
|
||||
|
||||
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.getMessage());
|
||||
}
|
||||
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.cachePlaylist(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 final 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
-37
@@ -1,15 +1,18 @@
|
||||
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;
|
||||
@@ -17,6 +20,61 @@ 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 {
|
||||
String[] files = AndroidUtils.getGlobalContext().getAssets().list("ffmpeg");
|
||||
@@ -45,40 +103,4 @@ public class FFMpeg {
|
||||
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,65 @@
|
||||
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,82 @@
|
||||
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,76 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import ru.vtosters.lite.music.Callback;
|
||||
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
|
||||
|
||||
public class PlaylistDownloader {
|
||||
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,47 @@
|
||||
package ru.vtosters.lite.music.downloader;
|
||||
|
||||
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,21 @@
|
||||
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 final 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,31 @@
|
||||
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,84 @@
|
||||
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,76 @@
|
||||
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,21 @@
|
||||
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,34 @@
|
||||
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,49 @@
|
||||
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,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 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,34 @@
|
||||
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,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 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,49 @@
|
||||
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,22 @@
|
||||
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,48 @@
|
||||
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,48 @@
|
||||
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,48 @@
|
||||
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,48 @@
|
||||
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,11 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -11,24 +11,10 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public abstract class BaseTranslator {
|
||||
|
||||
private final LruCache<String, String> cache = new LruCache<>(200);
|
||||
|
||||
private static final int YANDEX = 0;
|
||||
private static final int GOOGLE = 1;
|
||||
private static final int DEEPL = 2;
|
||||
|
||||
@NonNull
|
||||
public abstract String translate(String text, String tl);
|
||||
|
||||
@NonNull
|
||||
public final String getTranslation(String text) {
|
||||
var val = cache.get(text);
|
||||
if (val != null)
|
||||
return val;
|
||||
var translation = translate(text, getToLanguage());
|
||||
cache.put(text, translation);
|
||||
return translation;
|
||||
}
|
||||
private final LruCache<String, String> cache = new LruCache<>(200);
|
||||
|
||||
public static String getToLanguage() {
|
||||
var lang = preferences.getString("lang_target", "system");
|
||||
@@ -51,4 +37,17 @@ public abstract class BaseTranslator {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract String translate(String text, String tl);
|
||||
|
||||
@NonNull
|
||||
public final String getTranslation(String text) {
|
||||
var val = cache.get(text);
|
||||
if (val != null)
|
||||
return val;
|
||||
var translation = translate(text, getToLanguage());
|
||||
cache.put(text, translation);
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ public class DeepLTranslator extends BaseTranslator {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static Long getTimestamp(int iNumber) {
|
||||
var now = System.currentTimeMillis();
|
||||
return Long.valueOf((((long) iNumber) + now) - (now % ((long) iNumber)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String translate(String text, String tl) {
|
||||
@@ -126,9 +131,4 @@ public class DeepLTranslator extends BaseTranslator {
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private static Long getTimestamp(int iNumber) {
|
||||
var now = System.currentTimeMillis();
|
||||
return Long.valueOf((((long) iNumber) + now) - (now % ((long) iNumber)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getSTextAttr;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getTextAttr;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.PreferenceManager;
|
||||
@@ -24,6 +22,7 @@ import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vtosters.lite.general.fragments.MaterialPreferenceToolbarFragment;
|
||||
import com.vtosters.lite.ui.MaterialSwitchPreference;
|
||||
|
||||
@@ -178,23 +177,20 @@ public class PreferencesUtil {
|
||||
margin.setMargins(dp2px(20f), 0, dp2px(20f), 0);
|
||||
editText.setLayoutParams(margin);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getContext());
|
||||
builder.setTitle(title);
|
||||
builder.setView(linearLayout);
|
||||
builder.setPositiveButton("OK", (dialog, which) -> {
|
||||
boolean change = editTextPrefChangeListener.onChanged(preference, editText.getText().toString());
|
||||
if (!change)
|
||||
return;
|
||||
new VkAlertDialog.Builder(fragment.getContext())
|
||||
.setTitle(title)
|
||||
.setView(linearLayout)
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
boolean change = editTextPrefChangeListener.onChanged(preference, editText.getText().toString());
|
||||
if (!change)
|
||||
return;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(getGlobalContext())
|
||||
.edit()
|
||||
.putString(key, editText.getText().toString())
|
||||
.apply();
|
||||
});
|
||||
|
||||
var alert = builder.create();
|
||||
alert.show();
|
||||
alert.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getAccentColor());
|
||||
PreferenceManager.getDefaultSharedPreferences(getGlobalContext())
|
||||
.edit()
|
||||
.putString(key, editText.getText().toString())
|
||||
.apply();
|
||||
})
|
||||
.show();
|
||||
return false;
|
||||
}); // preference.setOnPreferenceClickListener(listener)
|
||||
|
||||
|
||||
@@ -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("&"));
|
||||
|
||||
@@ -218,6 +218,10 @@ public class CategorizedAdapter extends RecyclerView.Adapter<CategorizedAdapter.
|
||||
return true;
|
||||
}
|
||||
|
||||
public interface IViewHolderBinder {
|
||||
void bind(CategorizedViewHolder holder, int pos);
|
||||
}
|
||||
|
||||
public static class CategorizedViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final LinearLayout mMovingItemContainer;
|
||||
@@ -262,8 +266,4 @@ public class CategorizedAdapter extends RecyclerView.Adapter<CategorizedAdapter.
|
||||
mMovingItemContainer.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IViewHolderBinder {
|
||||
void bind(CategorizedViewHolder holder, int pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package ru.vtosters.lite.ui.adapters;
|
||||
import static ru.vtosters.lite.utils.ThemesUtils.getTextAttr;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,28 +16,27 @@ import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.LayoutUtils;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.ImagineArrayAdapterItem> {
|
||||
|
||||
private List<ImagineArrayAdapterItem> mItems = new ArrayList<>();
|
||||
private final OnClickListener mListener;
|
||||
private int mSelectedIndex = -1;
|
||||
|
||||
public ImagineArrayAdapter(@NonNull Context context, OnClickListener listener) {
|
||||
public ImagineArrayAdapter(@NonNull Context context) {
|
||||
super(context, 0);
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public ImagineArrayAdapter(@NonNull Context context, List<ImagineArrayAdapterItem> items, OnClickListener listener) {
|
||||
public ImagineArrayAdapter(@NonNull Context context, List<ImagineArrayAdapterItem> items) {
|
||||
super(context, 0, items);
|
||||
mItems = items;
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
public void setSelected(int i) {
|
||||
@@ -89,33 +89,26 @@ public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.Imagin
|
||||
container.setGravity(Gravity.CENTER_VERTICAL);
|
||||
container.setPadding(0, AndroidUtils.dp2px(5.0f), 0, AndroidUtils.dp2px(5.0f));
|
||||
container.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
|
||||
container.setOnClickListener(v -> {
|
||||
if (position != mSelectedIndex) {
|
||||
mListener.onClick(position);
|
||||
}
|
||||
});
|
||||
|
||||
// if make radiobutton without layout inflation it can't be unclickable
|
||||
var checkedTextView = (CheckedTextView) LayoutInflater.from(getContext())
|
||||
.inflate(AndroidUtils.getIdentifier("select_dialog_singlechoice_material", "layout"), parent, false);
|
||||
// for amoled
|
||||
var tintList = new ColorStateList(
|
||||
new int[][] { new int[] { android.R.attr.state_checked }, new int[] { -android.R.attr.state_checked }},
|
||||
new int[] { ThemesUtils.getAccentColor(), ThemesUtils.getSTextAttr() });
|
||||
TextViewCompat.setCompoundDrawableTintList(checkedTextView, tintList);
|
||||
checkedTextView.setChecked(position == mSelectedIndex);
|
||||
container.addView(checkedTextView, LayoutUtils.createLinear(AndroidUtils.dp2px(50.0f), -2));
|
||||
|
||||
var item = mItems.get(position);
|
||||
|
||||
var imageView = new ImageView(getContext());
|
||||
imageView.setImageDrawable(item.getIcon());
|
||||
imageView.setPadding(
|
||||
AndroidUtils.dp2px(13.0f),
|
||||
0,
|
||||
0,
|
||||
0
|
||||
);
|
||||
var imageViewParams = LayoutUtils.createLinear(
|
||||
AndroidUtils.dp2px(50.0f),
|
||||
AndroidUtils.dp2px(50.0f)
|
||||
);
|
||||
container.addView(imageView, imageViewParams);
|
||||
var icon = new ImageView(getContext());
|
||||
icon.setImageDrawable(item.getIcon());
|
||||
icon.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
var iconParams = LayoutUtils.createLinear(AndroidUtils.dp2px(35.0f), AndroidUtils.dp2px(35.0f));
|
||||
iconParams.leftMargin = AndroidUtils.dp2px(10);
|
||||
container.addView(icon, iconParams);
|
||||
|
||||
var textView = new CheckedTextView(getContext());
|
||||
textView.setText(item.getText());
|
||||
@@ -165,8 +158,4 @@ public class ImagineArrayAdapter extends ArrayAdapter<ImagineArrayAdapter.Imagin
|
||||
return mText;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(int i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreview
|
||||
|
||||
private final String[] mMessages = AndroidUtils.getArray("wallpaper_change_dialog");
|
||||
|
||||
private static int getColorFromAttribute(Context context, int attrId) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(attrId, typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int i) {
|
||||
return i % 2 == 0 ? ANONYMOUS : ME;
|
||||
@@ -70,12 +76,6 @@ public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreview
|
||||
return mMessages.length;
|
||||
}
|
||||
|
||||
private static int getColorFromAttribute(Context context, int attrId) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(attrId, typedValue, true);
|
||||
return typedValue.data;
|
||||
}
|
||||
|
||||
protected static class MessagePreviewViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView mMessage;
|
||||
|
||||
@@ -26,9 +26,10 @@ import com.aefyr.tsg.g2.TelegramStickersService;
|
||||
import java.io.File;
|
||||
|
||||
import ru.vtosters.lite.tgs.TGPref;
|
||||
import ru.vtosters.lite.ui.components.IItemMovingListener;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class StickerPackAdapter extends RecyclerView.Adapter<StickerPackAdapter.StickerPackViewHolder> {
|
||||
public class StickerPackAdapter extends RecyclerView.Adapter<StickerPackAdapter.StickerPackViewHolder> implements IItemMovingListener {
|
||||
private static final TelegramStickersService sService = TelegramStickersService.getInstance(getGlobalContext());
|
||||
|
||||
@SuppressLint("UseCompatLoadingForDrawables")
|
||||
@@ -62,6 +63,26 @@ public class StickerPackAdapter extends RecyclerView.Adapter<StickerPackAdapter.
|
||||
return sService.getPacksListReference().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDismiss(int index) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemMove(int fromPosition, int toPosition) {
|
||||
if (fromPosition < toPosition) {
|
||||
for (int i = fromPosition; i < toPosition; i++) {
|
||||
sService.swap(sService.getPacksListReference().get(i), sService.getPacksListReference().get(i + 1));
|
||||
}
|
||||
} else {
|
||||
for (int i = fromPosition; i > toPosition; i--) {
|
||||
sService.swap(sService.getPacksListReference().get(i), sService.getPacksListReference().get(i - 1));
|
||||
}
|
||||
}
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class StickerPackViewHolder extends RecyclerView.ViewHolder {
|
||||
private final LinearLayout mContainer;
|
||||
private final ImageView mStickerPreview;
|
||||
|
||||
@@ -37,8 +37,10 @@ public class CrashReporter {
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, th) -> CrashReporter.start(defaultUncaughtExceptionHandler, thread, th, activity));
|
||||
}
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
static void start(Thread.UncaughtExceptionHandler uncaughtExceptionHandler, Thread thread, Throwable th, Activity activity) {
|
||||
logString = getStackTrace(th) + "\n\n" + new DeviceInfoCollector().collect(getGlobalContext()).toDeviceName() + ", commit: " + getBuildNumber();
|
||||
logString = getStackTrace(th) + "\n\n" + new DeviceInfoCollector().collect().toDeviceName();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
var notificationChannel = new NotificationChannel("crashes", "crash", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
notificationChannel.enableVibration(true);
|
||||
@@ -58,6 +60,7 @@ public class CrashReporter {
|
||||
var saveLogIntent = new Intent(getGlobalContext(), LogWriterService.class);
|
||||
saveLogIntent.putExtra("log", logString);
|
||||
saveLogIntent.putExtra("notificationId", 1);
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
var psaveLogIntent = PendingIntent.getService(getGlobalContext(), 0, saveLogIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
@@ -74,9 +77,8 @@ public class CrashReporter {
|
||||
builder.setChannelId("crashes");
|
||||
}
|
||||
|
||||
|
||||
((NotificationManager) getGlobalContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(-2147483548, builder.build());
|
||||
Log.e("VTosters Lite", "crashed: " + th.getLocalizedMessage());
|
||||
Log.e("VTLite", "crashed: " + th.getLocalizedMessage());
|
||||
uncaughtExceptionHandler.uncaughtException(thread, th);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,25 +2,22 @@ package ru.vtosters.lite.ui.components;
|
||||
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
|
||||
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 static ru.vtosters.lite.utils.Preferences.vkme_notifs;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.vk.apps.AppsFragment;
|
||||
import com.vk.discover.ThemedFeedFragment;
|
||||
import com.vk.discover.DiscoverFragment;
|
||||
import com.vk.fave.fragments.FaveTabFragment;
|
||||
import com.vk.feedlikes.d.FeedLikesFragment;
|
||||
import com.vk.friends.catalog.FriendsCatalogFragment;
|
||||
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;
|
||||
@@ -35,7 +32,6 @@ import com.vtosters.lite.fragments.p2.DocumentsViewFragment;
|
||||
import com.vtosters.lite.fragments.s2.GroupsFragment1;
|
||||
import com.vtosters.lite.fragments.t2.c.DialogsFragment;
|
||||
import com.vtosters.lite.fragments.y2.VideoCatalogFragment;
|
||||
import com.vtosters.lite.fragments.y2.VideosFragment;
|
||||
import com.vtosters.lite.general.fragments.GamesFragment;
|
||||
import com.vtosters.lite.general.fragments.PhotosFragment;
|
||||
|
||||
@@ -49,14 +45,15 @@ import ru.vtosters.lite.ui.items.DockBarTab;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class DockBarEditorManager {
|
||||
|
||||
public static final int MIN_SELECTED_TABS = 3;
|
||||
public static final int MAX_SELECTED_TABS = 9;
|
||||
|
||||
private static DockBarEditorManager sInstance;
|
||||
private final List<DockBarTab> mSelectedTabs = new ArrayList<>();
|
||||
private final List<DockBarTab> mDisabledTabs = new ArrayList<>();
|
||||
|
||||
private static DockBarEditorManager sInstance;
|
||||
public DockBarEditorManager() {
|
||||
init();
|
||||
}
|
||||
|
||||
public static DockBarEditorManager getInstance() {
|
||||
return sInstance == null
|
||||
@@ -64,10 +61,6 @@ public class DockBarEditorManager {
|
||||
: sInstance;
|
||||
}
|
||||
|
||||
public DockBarEditorManager() {
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (vkme()) {
|
||||
mSelectedTabs.add(getTabByTag("tab_settings"));
|
||||
@@ -98,17 +91,15 @@ public class DockBarEditorManager {
|
||||
private void parseSelectedTabs(String[] selectedTabsTags) {
|
||||
List<String> allTags = new ArrayList<>(Arrays.asList("tab_news", "tab_superapps", "tab_messages", "tab_feedback", "tab_profile",
|
||||
"tab_friends", "tab_groups", "tab_photos", "tab_audios", "tab_videos", "tab_lives", "tab_games", "tab_liked",
|
||||
"tab_fave", "tab_documents", "tab_payments", "tab_vk_apps", "tab_settings", "tab_menu"));
|
||||
"tab_fave", "tab_documents", "tab_payments", "tab_vk_apps", "tab_settings"));
|
||||
|
||||
for (String tag : selectedTabsTags) {
|
||||
mSelectedTabs.add(getTabByTag(tag));
|
||||
allTags.remove(tag);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
allTags.forEach(tag -> mDisabledTabs.add(getTabByTag(tag)));
|
||||
} else {
|
||||
for (String tag : allTags) {
|
||||
mDisabledTabs.add(getTabByTag(tag));
|
||||
}
|
||||
|
||||
for (String tag : allTags) {
|
||||
mDisabledTabs.add(getTabByTag(tag));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +148,7 @@ public class DockBarEditorManager {
|
||||
milkshake() ? R.drawable.ic_services_outline_28 : R.drawable.ic_menu_search_outline_28,
|
||||
milkshake() ? R.string.super_app_title : R.string.search,
|
||||
R.id.tab_discover,
|
||||
milkshake() ? (superapp() ? SuperAppFragment.class : SearchMenuFragment.class) : ThemedFeedFragment.class);
|
||||
milkshake() ? (superapp() ? SuperAppFragment.class : SearchMenuFragment.class) : DiscoverFragment.class);
|
||||
case "tab_messages":
|
||||
return DockBarTab.valuesOf(
|
||||
"tab_messages",
|
||||
@@ -206,14 +197,14 @@ public class DockBarEditorManager {
|
||||
R.drawable.ic_music_outline_28,
|
||||
R.string.music,
|
||||
R.id.menu_audios,
|
||||
getBoolValue("musicnewcatalog", true) ? MusicCatalogFragment1.class : MusicFragment.class);
|
||||
MusicCatalogFragment1.class);
|
||||
case "tab_videos":
|
||||
return DockBarTab.valuesOf(
|
||||
"tab_videos",
|
||||
R.drawable.ic_video_outline_28,
|
||||
R.string.videos,
|
||||
R.id.menu_videos,
|
||||
videonewcatalog() ? VideoCatalogFragment.class : VideosFragment.class);
|
||||
VideoCatalogFragment.class);
|
||||
case "tab_lives":
|
||||
return DockBarTab.valuesOf(
|
||||
"tab_lives",
|
||||
@@ -263,13 +254,6 @@ public class DockBarEditorManager {
|
||||
R.string.menu_apps,
|
||||
R.id.menu_vk_apps,
|
||||
AppsFragment.class);
|
||||
case "tab_menu":
|
||||
return DockBarTab.valuesOf(
|
||||
"tab_menu",
|
||||
milkshake() ? R.drawable.ic_user_circle_outline_28 : R.drawable.ic_menu_more_outline_28,
|
||||
milkshake() ? R.string.profile : R.string.menu,
|
||||
R.id.tab_menu,
|
||||
milkshake() ? ProfileFragment.class : MenuFragment.class);
|
||||
case "tab_settings":
|
||||
default:
|
||||
return DockBarTab.valuesOf(
|
||||
|
||||
@@ -22,7 +22,7 @@ public class NewsfeedListManager {
|
||||
var list = new ArrayList<VBListItem>();
|
||||
|
||||
if (filtersSet.size() == 0) {
|
||||
sendToast(AndroidUtils.getString("newsfeed_update_feed"));
|
||||
sendToast(AndroidUtils.getString("newsfeed_list_update_feed"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,7 @@ public class NewsfeedListManager {
|
||||
}
|
||||
|
||||
public static void resetHideItems() {
|
||||
getDefaultPrefs().edit()
|
||||
.putString("news_feed_selected_items", "")
|
||||
getDefaultPrefs().edit().remove("news_feed_selected_items").remove("news_feed_items_set")
|
||||
.apply();
|
||||
sendToast(AndroidUtils.getString("newsfeed_list_hidden_reseted"));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user