Compare commits
1043 Commits
1.1.4+fork
...
v1.2.3+for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99f0817bdb | ||
|
|
220cd35d82 | ||
|
|
07f4ef1697 | ||
|
|
f20732ddc2 | ||
|
|
b1e0dc5843 | ||
|
|
285eb25706 | ||
|
|
ec556511e6 | ||
|
|
85c3d9f65f | ||
|
|
a7ebadf269 | ||
|
|
94c09d46c2 | ||
|
|
f6f08d176c | ||
|
|
66cdd63496 | ||
|
|
8b502b605c | ||
|
|
2c0ec28803 | ||
|
|
a9ab9cb249 | ||
|
|
961c69b525 | ||
|
|
c70f393559 | ||
|
|
9abdc174f4 | ||
|
|
2e5bfa1d9c | ||
|
|
33cbd85e19 | ||
|
|
8cb1f3f387 | ||
|
|
3f0c6fcec5 | ||
|
|
797cf893da | ||
|
|
a3564b70e1 | ||
|
|
43004307b8 | ||
|
|
acd1e4ced3 | ||
|
|
6717070f93 | ||
|
|
387499ae49 | ||
|
|
8ab140c55d | ||
|
|
914abb95dd | ||
|
|
5360c0f0f7 | ||
|
|
243d803b51 | ||
|
|
b343fe3835 | ||
|
|
3c42c1120f | ||
|
|
ad840dcef6 | ||
|
|
f73072d95e | ||
|
|
95cb9b5079 | ||
|
|
c6684d3c9b | ||
|
|
5c5989d8c0 | ||
|
|
60e92d30b0 | ||
|
|
8bf8e3f86b | ||
|
|
891ee2d06b | ||
|
|
b450bc7ae8 | ||
|
|
4ca1e0d5db | ||
|
|
859213dd9e | ||
|
|
ad2857791d | ||
|
|
497827f2e2 | ||
|
|
967e333022 | ||
|
|
8df1406006 | ||
|
|
4af42fafdc | ||
|
|
a9e6a452c1 | ||
|
|
a4a4632397 | ||
|
|
421f39e414 | ||
|
|
f8121e2dc4 | ||
|
|
b1784fc51c | ||
|
|
96db0d7de7 | ||
|
|
3837ed9cb1 | ||
|
|
2be789a43c | ||
|
|
fd8d96169a | ||
|
|
1562dc32c1 | ||
|
|
38f377ca09 | ||
|
|
cc28bba884 | ||
|
|
beb3081918 | ||
|
|
1b3c9106b5 | ||
|
|
385b91761b | ||
|
|
d7b76ed70a | ||
|
|
43600756c0 | ||
|
|
3c3e0633ad | ||
|
|
f819ad6917 | ||
|
|
2e84faa505 | ||
|
|
e7e8d13d9e | ||
|
|
a683c2cb11 | ||
|
|
addf7de316 | ||
|
|
44d4eada51 | ||
|
|
40bfdea5b1 | ||
|
|
55138c1e86 | ||
|
|
e7ad396fc6 | ||
|
|
0aef680572 | ||
|
|
6dc37d6bde | ||
|
|
60ea7cedf6 | ||
|
|
c986b10e14 | ||
|
|
d52174bd9e | ||
|
|
c65d138911 | ||
|
|
ad9bb8ad58 | ||
|
|
63e536c66c | ||
|
|
b5a08b1b98 | ||
|
|
226e2a7cdc | ||
|
|
4d7c4aed4c | ||
|
|
c9bcd000c3 | ||
|
|
b1cb4d4257 | ||
|
|
de42145f30 | ||
|
|
7bcdd6070a | ||
|
|
8a215e90d0 | ||
|
|
b736fa18bb | ||
|
|
43c19e4942 | ||
|
|
ffc18029bb | ||
|
|
b88b3d15f8 | ||
|
|
c817886a2d | ||
|
|
aae239494e | ||
|
|
b0b2daa5d5 | ||
|
|
eea2e38f1b | ||
|
|
f894ecd25b | ||
|
|
e0b6ed7103 | ||
|
|
a78e75747a | ||
|
|
3b25e367bb | ||
|
|
08b29dff3d | ||
|
|
2f2e053d26 | ||
|
|
191d582c30 | ||
|
|
8d3380ff6e | ||
|
|
ba85d18574 | ||
|
|
0f53b17515 | ||
|
|
cb9c869712 | ||
|
|
aa3d9e7b8f | ||
|
|
b3a9b5824d | ||
|
|
b6186a349f | ||
|
|
100bd4b062 | ||
|
|
7da09d9b37 | ||
|
|
f46eb07228 | ||
|
|
7627b5eb25 | ||
|
|
c710448c6b | ||
|
|
1ad270b1d6 | ||
|
|
099e253b2b | ||
|
|
66de4a5b91 | ||
|
|
41437d91d5 | ||
|
|
d33d5a6efa | ||
|
|
4f9248d040 | ||
|
|
f40c0e41f3 | ||
|
|
15fcb0e25d | ||
|
|
2dae662333 | ||
|
|
3ad46926f1 | ||
|
|
2385d102ae | ||
|
|
deeb03ff2b | ||
|
|
5c2a09e243 | ||
|
|
2473c999db | ||
|
|
ea2cc265e3 | ||
|
|
a0cd2d42cf | ||
|
|
0a17ceb984 | ||
|
|
4ef18f1f4a | ||
|
|
0de227ab9c | ||
|
|
19cb8703a6 | ||
|
|
e18567dd82 | ||
|
|
bfb3bcdbfb | ||
|
|
565cd14d88 | ||
|
|
ebc37eac75 | ||
|
|
c3702db577 | ||
|
|
e15c4fa342 | ||
|
|
8330b9f1c5 | ||
|
|
f759150982 | ||
|
|
6af177b596 | ||
|
|
657bb94975 | ||
|
|
3c946212b1 | ||
|
|
b4e80f7fca | ||
|
|
c9efc2cb2b | ||
|
|
0d62e33dc7 | ||
|
|
ac88b9e19c | ||
|
|
871dfda79e | ||
|
|
e0c2c208ae | ||
|
|
22ac112bdb | ||
|
|
afd0cca176 | ||
|
|
c083c8bce5 | ||
|
|
63bde032b3 | ||
|
|
49492c0788 | ||
|
|
b439c64add | ||
|
|
1868bfe8e3 | ||
|
|
f240a3d996 | ||
|
|
788e5bd12e | ||
|
|
a55fed4502 | ||
|
|
a8fdaf1a47 | ||
|
|
4a758bd488 | ||
|
|
2c9731ec2a | ||
|
|
eef33266fc | ||
|
|
58d2c3e5a6 | ||
|
|
9e6a355db0 | ||
|
|
0d10e09fd6 | ||
|
|
f85bb995ba | ||
|
|
268e5639f6 | ||
|
|
51809df8ca | ||
|
|
94fb676b0c | ||
|
|
a47106594b | ||
|
|
d93d66f702 | ||
|
|
b2f9f7ae54 | ||
|
|
de7b908c78 | ||
|
|
75d3c2fdce | ||
|
|
ea1b6c5835 | ||
|
|
cb7887da41 | ||
|
|
a80313ee6b | ||
|
|
e1a821bc43 | ||
|
|
924ea2d03a | ||
|
|
55270fe654 | ||
|
|
a125fab57b | ||
|
|
395ee0aa99 | ||
|
|
0f50fa6ba1 | ||
|
|
adb7df3c71 | ||
|
|
5d7bcb629b | ||
|
|
a00f1417d2 | ||
|
|
8efd7e8ebf | ||
|
|
b016d277e0 | ||
|
|
fdb39617d1 | ||
|
|
89f83fbf62 | ||
|
|
ecee9e01a6 | ||
|
|
20dc9bb8b9 | ||
|
|
2c47d0e9ed | ||
|
|
8e13d52e51 | ||
|
|
cc40198c9e | ||
|
|
290897ea41 | ||
|
|
b9e1c84304 | ||
|
|
3c44c80e2e | ||
|
|
dffa4e4594 | ||
|
|
fa2d9fec58 | ||
|
|
09c1a2cfa0 | ||
|
|
d1f90eb231 | ||
|
|
1f7d97134b | ||
|
|
79be91784d | ||
|
|
de2654def3 | ||
|
|
56343dacff | ||
|
|
0e677f8ce7 | ||
|
|
aa911896d6 | ||
|
|
c62a8635b9 | ||
|
|
4e900247c5 | ||
|
|
b3bd62bc6c | ||
|
|
8e5fd48ecd | ||
|
|
b2bca9dd2c | ||
|
|
b8c0dc3181 | ||
|
|
cccdc5292e | ||
|
|
76d77a0e7a | ||
|
|
e737f4bf9a | ||
|
|
391db2f1c9 | ||
|
|
359d61183c | ||
|
|
46fd05d88e | ||
|
|
cde22a0945 | ||
|
|
111b7e25c5 | ||
|
|
4f8d8f0c8d | ||
|
|
915b0603d0 | ||
|
|
6ec43a6f86 | ||
|
|
df93a1a845 | ||
|
|
41a70a353c | ||
|
|
8d69bcfd4b | ||
|
|
0ef30f82a7 | ||
|
|
be60e78ea6 | ||
|
|
5434325fa8 | ||
|
|
0a04c9357c | ||
|
|
075aab8074 | ||
|
|
21c4cef397 | ||
|
|
4b2fcd760a | ||
|
|
6ebe4c86af | ||
|
|
0925c8c582 | ||
|
|
9824b5fb56 | ||
|
|
78fcf31e34 | ||
|
|
eadb62d3a8 | ||
|
|
f6279fcc0c | ||
|
|
a683fdce62 | ||
|
|
b958299446 | ||
|
|
3f80be8377 | ||
|
|
ced0accde5 | ||
|
|
b454ff5ec7 | ||
|
|
45af198f32 | ||
|
|
ff374f8899 | ||
|
|
faecb3bc4b | ||
|
|
6b893fadef | ||
|
|
c328467a41 | ||
|
|
182325470b | ||
|
|
f330ad71ac | ||
|
|
ba0c064f36 | ||
|
|
8d7aaee5b9 | ||
|
|
68cba2de63 | ||
|
|
5a914f9c0e | ||
|
|
b0e6805a20 | ||
|
|
21e7e44c01 | ||
|
|
f7df4abdae | ||
|
|
7674ceefe9 | ||
|
|
4be575c534 | ||
|
|
dd0f0a7d5a | ||
|
|
759b44c224 | ||
|
|
ebdd1a0b03 | ||
|
|
6c44575f7a | ||
|
|
8be832239a | ||
|
|
c7be202430 | ||
|
|
244c5dc6b4 | ||
|
|
6bbb9a638e | ||
|
|
44041b136a | ||
|
|
7e17c30ce2 | ||
|
|
c4d3d1b409 | ||
|
|
03de63754b | ||
|
|
0e506f0b1a | ||
|
|
a2ab752870 | ||
|
|
eaa032828a | ||
|
|
cceb0b4c6c | ||
|
|
a58640a718 | ||
|
|
920384b26c | ||
|
|
193a2c4f70 | ||
|
|
793dec98b2 | ||
|
|
11ddf8015d | ||
|
|
1753bdbd8b | ||
|
|
d6e563486b | ||
|
|
0112bfa9c4 | ||
|
|
5951611fb0 | ||
|
|
2d31b726ac | ||
|
|
d2f295ef88 | ||
|
|
d1b53554ce | ||
|
|
ec5db122d0 | ||
|
|
0216e22fcc | ||
|
|
5734b19d8c | ||
|
|
cc8c818e13 | ||
|
|
e58aeec097 | ||
|
|
58b000927a | ||
|
|
797642b972 | ||
|
|
2e1f273d78 | ||
|
|
35d6800877 | ||
|
|
781856b822 | ||
|
|
ff272179e7 | ||
|
|
bec47f40f7 | ||
|
|
f9607a434a | ||
|
|
b650ca85bc | ||
|
|
91e154bbee | ||
|
|
f4365ed163 | ||
|
|
0c02b0cb68 | ||
|
|
b0aaa58fa7 | ||
|
|
054ab774d5 | ||
|
|
8ca33b552d | ||
|
|
6734c2b9f7 | ||
|
|
c484477d6a | ||
|
|
d9b5223749 | ||
|
|
55856450b3 | ||
|
|
bb28a3bf83 | ||
|
|
5e4e56bd2c | ||
|
|
82fac1d4e7 | ||
|
|
bf9afba590 | ||
|
|
b667afc7cd | ||
|
|
21b0736842 | ||
|
|
8bd3c7cc28 | ||
|
|
4db90b87f4 | ||
|
|
46fd0be0eb | ||
|
|
05a966869b | ||
|
|
6fef51fcbb | ||
|
|
d5b6c02b22 | ||
|
|
6cd722dbef | ||
|
|
8dfc1ecae8 | ||
|
|
fdca799a5f | ||
|
|
def79e591b | ||
|
|
093a27970d | ||
|
|
9a1e33a776 | ||
|
|
a4a206eae6 | ||
|
|
21ee5fc2bf | ||
|
|
7df72e6bc6 | ||
|
|
9ceea8de99 | ||
|
|
79d48a12a9 | ||
|
|
473d3a5196 | ||
|
|
43717634f6 | ||
|
|
6ba8555adf | ||
|
|
722ad8c53e | ||
|
|
b0b497ed46 | ||
|
|
6c881eccd2 | ||
|
|
94b92bd7c1 | ||
|
|
676b195aee | ||
|
|
4591731cdc | ||
|
|
fc5eeae9e7 | ||
|
|
aaa88c45f7 | ||
|
|
5d91e8030c | ||
|
|
c9be188d19 | ||
|
|
ecc8daa8f1 | ||
|
|
5bbe11bf45 | ||
|
|
e52170abea | ||
|
|
0f87fc7924 | ||
|
|
cc7c4fc95f | ||
|
|
4a2dcdf701 | ||
|
|
ede47af962 | ||
|
|
70e4cb2286 | ||
|
|
c5e8460516 | ||
|
|
f852dac1e5 | ||
|
|
374626cb32 | ||
|
|
aefa0b89ae | ||
|
|
9cc8b2668c | ||
|
|
94b862a3ff | ||
|
|
f71bb6b78c | ||
|
|
499ac8f727 | ||
|
|
938ae97cac | ||
|
|
e2193e8e3c | ||
|
|
16f907c91f | ||
|
|
584700225c | ||
|
|
bad72985cb | ||
|
|
56b24420d1 | ||
|
|
92beac8dff | ||
|
|
ed1fdba9a5 | ||
|
|
5e194e3079 | ||
|
|
27c2791d6c | ||
|
|
d6bcc9c156 | ||
|
|
4c85fd4387 | ||
|
|
80d529d503 | ||
|
|
c5a19a2334 | ||
|
|
16857bebd9 | ||
|
|
1c340b7c66 | ||
|
|
7d9d8f0aae | ||
|
|
21fc35230c | ||
|
|
fc67c82040 | ||
|
|
a9b828001c | ||
|
|
be050abf7e | ||
|
|
2d071ca252 | ||
|
|
abf94e1e70 | ||
|
|
2991c7421c | ||
|
|
834f84f995 | ||
|
|
6bf713c96c | ||
|
|
72b16be297 | ||
|
|
0c7f27b3ac | ||
|
|
ca101748eb | ||
|
|
3584c123d6 | ||
|
|
a2fd4be339 | ||
|
|
656364db60 | ||
|
|
884347da12 | ||
|
|
675c49a922 | ||
|
|
4d04741fe0 | ||
|
|
5c7fe9dcb5 | ||
|
|
a3146d6cdd | ||
|
|
d769f757ed | ||
|
|
654e3eb36b | ||
|
|
f62ba685d1 | ||
|
|
c3aa3af650 | ||
|
|
8cc5678b38 | ||
|
|
592eb2d589 | ||
|
|
0bf31de1f1 | ||
|
|
7fd32cab3d | ||
|
|
4a695b2a83 | ||
|
|
a8ba50e762 | ||
|
|
f79fc66578 | ||
|
|
0c63b99e6c | ||
|
|
5881f5fa05 | ||
|
|
5e8ede6ab8 | ||
|
|
1124bab96b | ||
|
|
cbcdf09bfe | ||
|
|
575ca6251c | ||
|
|
9c83a5aaeb | ||
|
|
bc3e46eae1 | ||
|
|
6756b36e87 | ||
|
|
36808e4e8e | ||
|
|
1c38570609 | ||
|
|
2dc6deb93a | ||
|
|
0b58d19811 | ||
|
|
50362d630b | ||
|
|
bfcd67cbaf | ||
|
|
ea190c0597 | ||
|
|
e68160800d | ||
|
|
73481f4a1f | ||
|
|
0303e59fc1 | ||
|
|
0c0d36da62 | ||
|
|
e7e0b8841c | ||
|
|
79d9abe7f7 | ||
|
|
684fbc0050 | ||
|
|
03973d41be | ||
|
|
06c533bf5a | ||
|
|
cc64a20e53 | ||
|
|
4144639b75 | ||
|
|
d633b6f1d4 | ||
|
|
7b68baef0d | ||
|
|
fb5ef921f7 | ||
|
|
da44e50679 | ||
|
|
93d89f93b2 | ||
|
|
cce64f9a76 | ||
|
|
afb396acd8 | ||
|
|
0943908173 | ||
|
|
b018788cd1 | ||
|
|
4a315e73eb | ||
|
|
d0a9ba041d | ||
|
|
7ea42c8403 | ||
|
|
af9b527f35 | ||
|
|
df58cdd86e | ||
|
|
507fcea646 | ||
|
|
5cd1e88da9 | ||
|
|
e2293899f0 | ||
|
|
1c743ee3a6 | ||
|
|
daf4c69df4 | ||
|
|
14d3add7b3 | ||
|
|
7e3193a708 | ||
|
|
545aa16cd3 | ||
|
|
4dcf32d13a | ||
|
|
e0aba23e80 | ||
|
|
b19ae9bb10 | ||
|
|
d20f8669e8 | ||
|
|
1567e5aba4 | ||
|
|
b01ef6d5a7 | ||
|
|
604e581139 | ||
|
|
082f697b40 | ||
|
|
6b9b6710cf | ||
|
|
b07858a66d | ||
|
|
c05d0b600e | ||
|
|
0a8d73dc0b | ||
|
|
fd99f3caa1 | ||
|
|
794c4e5227 | ||
|
|
f5df8225d1 | ||
|
|
42c6446125 | ||
|
|
e3486ebf7c | ||
|
|
c0115f068c | ||
|
|
41682d1147 | ||
|
|
8c61660cfc | ||
|
|
d6933be3cd | ||
|
|
ca6cfd2b91 | ||
|
|
20960bdd57 | ||
|
|
dd582c4bee | ||
|
|
3a0d314af0 | ||
|
|
a00ca599c1 | ||
|
|
263b5b10b6 | ||
|
|
1068fa3120 | ||
|
|
569f288c00 | ||
|
|
dfd94511a5 | ||
|
|
2271f336b0 | ||
|
|
4486feee76 | ||
|
|
baaff2573c | ||
|
|
b9c3b23757 | ||
|
|
0c1fd22253 | ||
|
|
466c489b4d | ||
|
|
625f715e26 | ||
|
|
61caec4060 | ||
|
|
df233eb1e2 | ||
|
|
78225c482f | ||
|
|
c55703f0ba | ||
|
|
3b26dd44a0 | ||
|
|
2a1386a87a | ||
|
|
fb32430f96 | ||
|
|
2288c53adc | ||
|
|
84b7b67045 | ||
|
|
d233f039a3 | ||
|
|
8b01955a18 | ||
|
|
427aa1722d | ||
|
|
2a7eb09998 | ||
|
|
f2f48fce79 | ||
|
|
634408b8cb | ||
|
|
f050e3f22d | ||
|
|
8e9531b718 | ||
|
|
00457c1edb | ||
|
|
aa14986fcc | ||
|
|
1493cd9034 | ||
|
|
b2b295ee5b | ||
|
|
58847f80fd | ||
|
|
ed9db7b5fd | ||
|
|
433a7c6b7a | ||
|
|
7692f587ef | ||
|
|
67583150b2 | ||
|
|
ed49422f76 | ||
|
|
63078aaa3e | ||
|
|
9246c43ffe | ||
|
|
15f02863c0 | ||
|
|
be1921879d | ||
|
|
61b43e0112 | ||
|
|
b25b482630 | ||
|
|
1d44875a65 | ||
|
|
0dc5004898 | ||
|
|
0df86e315b | ||
|
|
31d3fa77de | ||
|
|
ec74b18c1a | ||
|
|
de36c31f45 | ||
|
|
43bcf0008e | ||
|
|
ac64087018 | ||
|
|
af77865a46 | ||
|
|
51a4a41147 | ||
|
|
987474462d | ||
|
|
c34ab79c6c | ||
|
|
8bac664a34 | ||
|
|
d77647c354 | ||
|
|
e8ef6ef2c7 | ||
|
|
a87cf640dd | ||
|
|
bcb69f1f47 | ||
|
|
0bdcc9057b | ||
|
|
f92977fddf | ||
|
|
230a59266d | ||
|
|
d97f3ed5c8 | ||
|
|
aae2cd2b65 | ||
|
|
8b8763bffc | ||
|
|
35758e720d | ||
|
|
fa48c80ab1 | ||
|
|
1621dbc67a | ||
|
|
4ece7b883f | ||
|
|
66a5b749fe | ||
|
|
49a80767a7 | ||
|
|
7683b464f3 | ||
|
|
64fbbb2f07 | ||
|
|
9003f690d1 | ||
|
|
8ffaca61bd | ||
|
|
f6c3b10c2b | ||
|
|
d68a3a6ef5 | ||
|
|
4475bd039a | ||
|
|
98dc7d0524 | ||
|
|
65948030a6 | ||
|
|
89edfaaa6d | ||
|
|
10372804e4 | ||
|
|
d1dd7d203b | ||
|
|
c6aed0b52e | ||
|
|
5f902d25a9 | ||
|
|
c43bed665d | ||
|
|
d70a2ae5b3 | ||
|
|
1fdf36b4d8 | ||
|
|
5d1cd0f4f6 | ||
|
|
3505460372 | ||
|
|
eeb91e867e | ||
|
|
c87062ee31 | ||
|
|
fb66fa1c6f | ||
|
|
f80af9f5bf | ||
|
|
85157ffe25 | ||
|
|
59f95159b7 | ||
|
|
703dbd4c8a | ||
|
|
1e75f9f1c2 | ||
|
|
d0860333a9 | ||
|
|
64b3951c25 | ||
|
|
e89e6cc3f5 | ||
|
|
3c2985fa6e | ||
|
|
bee01429f2 | ||
|
|
a96431cc00 | ||
|
|
bf9e6f54cf | ||
|
|
63084857a3 | ||
|
|
d8b7038972 | ||
|
|
976e71db25 | ||
|
|
2b59c2c080 | ||
|
|
5929b0c6b9 | ||
|
|
e160a05411 | ||
|
|
78d8f075a9 | ||
|
|
3784873cad | ||
|
|
528f8aaead | ||
|
|
4ba9f1ecaf | ||
|
|
697a666545 | ||
|
|
0ee6798424 | ||
|
|
9a95deb346 | ||
|
|
0155ef2675 | ||
|
|
858195f813 | ||
|
|
b681c7dfeb | ||
|
|
b89f931ffd | ||
|
|
1658e56729 | ||
|
|
2b7d8292ed | ||
|
|
494abdfeee | ||
|
|
426f3fe95b | ||
|
|
b7a96778b8 | ||
|
|
125cd525bf | ||
|
|
ed281a4619 | ||
|
|
f418a5a2c4 | ||
|
|
6d8971df64 | ||
|
|
8dd3343906 | ||
|
|
f65fc9299a | ||
|
|
ac00889001 | ||
|
|
7af0a3f351 | ||
|
|
2734f88206 | ||
|
|
c6cd8ca14b | ||
|
|
2fd61f738f | ||
|
|
d3575b60fe | ||
|
|
dce8808d62 | ||
|
|
db97dadb25 | ||
|
|
34116d9914 | ||
|
|
bc676e6eb3 | ||
|
|
ef2cb31b6c | ||
|
|
c45dc96316 | ||
|
|
f9b34b53c1 | ||
|
|
e128e144b1 | ||
|
|
c68ed6088f | ||
|
|
b66ad0e6f5 | ||
|
|
02a470bd7d | ||
|
|
9407bd9e86 | ||
|
|
825adda664 | ||
|
|
0a22c14eec | ||
|
|
5452da6a65 | ||
|
|
ffb321e36f | ||
|
|
eaecff52c9 | ||
|
|
b141a9ac74 | ||
|
|
a890f21ace | ||
|
|
4e72e5c234 | ||
|
|
1def56057a | ||
|
|
d99f6c7167 | ||
|
|
0d5d169e5f | ||
|
|
90e55c1043 | ||
|
|
780c5c345c | ||
|
|
c2bc0a4055 | ||
|
|
8032de4595 | ||
|
|
bd3f5018ed | ||
|
|
91c4e5e51f | ||
|
|
ca1cb668f3 | ||
|
|
a12ca697ed | ||
|
|
3ccb629a4e | ||
|
|
4db041c28f | ||
|
|
bdcf4a5438 | ||
|
|
c042050295 | ||
|
|
f87a87aba1 | ||
|
|
b3a88c4a7c | ||
|
|
9729663cb4 | ||
|
|
f66e6197d3 | ||
|
|
4ff940030d | ||
|
|
da8b88dfc6 | ||
|
|
ed13b1074d | ||
|
|
c5e985f6a4 | ||
|
|
160bb4e272 | ||
|
|
f89a3e644a | ||
|
|
4cfd0db899 | ||
|
|
c6cb992b92 | ||
|
|
1b3ea6cdbe | ||
|
|
79323e392b | ||
|
|
c6c985c1db | ||
|
|
8988b22a52 | ||
|
|
692ede503c | ||
|
|
3be04343b8 | ||
|
|
521157315b | ||
|
|
b4d7b34767 | ||
|
|
f912e90691 | ||
|
|
61ff2ce7e4 | ||
|
|
0316ec340a | ||
|
|
3975e8c280 | ||
|
|
37c40e4a8d | ||
|
|
7dc691deae | ||
|
|
ab5dfe6f62 | ||
|
|
b445e6f79f | ||
|
|
5f0cd72303 | ||
|
|
53dfa08300 | ||
|
|
321c23c52e | ||
|
|
dd6cb4af74 | ||
|
|
54b6aaec09 | ||
|
|
624d21d18b | ||
|
|
5c27155507 | ||
|
|
35552cfbef | ||
|
|
31bbeef24e | ||
|
|
5ee42c0294 | ||
|
|
3d08f768f8 | ||
|
|
603e3d7d65 | ||
|
|
eb8f71aa31 | ||
|
|
c7b5b41128 | ||
|
|
98dbff38ff | ||
|
|
d9317f6eb1 | ||
|
|
4b5dff8742 | ||
|
|
2256ef6232 | ||
|
|
182bc09023 | ||
|
|
a60e5040ea | ||
|
|
c2a6e17fa5 | ||
|
|
85931e2a65 | ||
|
|
da94cd801b | ||
|
|
594570f9a1 | ||
|
|
548a14ab60 | ||
|
|
40016332ff | ||
|
|
2b9746232b | ||
|
|
1fe31e9262 | ||
|
|
b2a152a728 | ||
|
|
f93d9a0c35 | ||
|
|
af9d9c3f48 | ||
|
|
5cae41a500 | ||
|
|
d6c560e015 | ||
|
|
a2679a3841 | ||
|
|
d826e0172b | ||
|
|
f593f5eb58 | ||
|
|
d29565af9c | ||
|
|
7c3cef32ed | ||
|
|
2c15796108 | ||
|
|
553a3ef7e1 | ||
|
|
a7dfb671ce | ||
|
|
929218d74c | ||
|
|
a5ec9695df | ||
|
|
2784828a93 | ||
|
|
84657e9529 | ||
|
|
110375462e | ||
|
|
09e385633e | ||
|
|
f7410a510f | ||
|
|
20511fd39d | ||
|
|
dbacbe0341 | ||
|
|
bd8da39a19 | ||
|
|
675a353494 | ||
|
|
7b23ca1c96 | ||
|
|
61e8c6f435 | ||
|
|
a41b8dbb01 | ||
|
|
fe525f9242 | ||
|
|
7cbae9c0a9 | ||
|
|
93ac0a103f | ||
|
|
3b11787984 | ||
|
|
7b6fcaf3db | ||
|
|
11b838f394 | ||
|
|
38bd5eb68e | ||
|
|
1dc8d66b3f | ||
|
|
9f64e56923 | ||
|
|
a9e84678b3 | ||
|
|
d7c5c0074d | ||
|
|
97e148f4c8 | ||
|
|
e83bd039b3 | ||
|
|
a30d288b13 | ||
|
|
ded14711ac | ||
|
|
cece9d4aa1 | ||
|
|
f7f56c7a9b | ||
|
|
613a9de40e | ||
|
|
9ed8ad1382 | ||
|
|
7498118800 | ||
|
|
b894827607 | ||
|
|
8788fb0b27 | ||
|
|
62d4c62888 | ||
|
|
572d092f88 | ||
|
|
2ff771391c | ||
|
|
087e55277c | ||
|
|
a2d45fbbc5 | ||
|
|
59262fe345 | ||
|
|
ffc36f7346 | ||
|
|
549ace65f5 | ||
|
|
c76bec2298 | ||
|
|
290e47386e | ||
|
|
3d96475c21 | ||
|
|
b62fe06187 | ||
|
|
c0dc2b8392 | ||
|
|
e777bbb215 | ||
|
|
014f9f4d99 | ||
|
|
86bfd3d09f | ||
|
|
21d6f6da4c | ||
|
|
f826e0ceef | ||
|
|
458ad0f51a | ||
|
|
9a0ff42ec2 | ||
|
|
18dae448ec | ||
|
|
fc36a8cc8f | ||
|
|
a390df2b9e | ||
|
|
6f61d3f0e3 | ||
|
|
3a4e8ebdf4 | ||
|
|
537242b277 | ||
|
|
97eece59ea | ||
|
|
fc88d42e50 | ||
|
|
ec74712e55 | ||
|
|
12e1ccf439 | ||
|
|
24b8d5ce7c | ||
|
|
46c9c83b63 | ||
|
|
526a9fec03 | ||
|
|
ca20f3b906 | ||
|
|
af8c8a6248 | ||
|
|
2940e5d3d8 | ||
|
|
c98b001c9f | ||
|
|
1fc1c95d6e | ||
|
|
e4d0c4eda5 | ||
|
|
801d11c8e6 | ||
|
|
8143374929 | ||
|
|
df2ff9f874 | ||
|
|
4bac852d37 | ||
|
|
862a173392 | ||
|
|
bd47b31c65 | ||
|
|
aefb7f2e23 | ||
|
|
e509b8afa4 | ||
|
|
7b94f7258f | ||
|
|
8d81efae4e | ||
|
|
5b0b80277c | ||
|
|
60293d5a65 | ||
|
|
09b4aff9f5 | ||
|
|
7326cbeb14 | ||
|
|
91bd3fa4ea | ||
|
|
3cc6a9905e | ||
|
|
f01bfcd372 | ||
|
|
81b4365a14 | ||
|
|
7ab28a6db6 | ||
|
|
3bb548cf22 | ||
|
|
ba788d1b34 | ||
|
|
f51b01bcd9 | ||
|
|
361c97a9df | ||
|
|
f34153e601 | ||
|
|
67240acb48 | ||
|
|
4395dbfa7c | ||
|
|
48b0207636 | ||
|
|
f7b8ed519c | ||
|
|
eba88f2c0a | ||
|
|
8d95355727 | ||
|
|
e05a67c4ab | ||
|
|
5db91627a1 | ||
|
|
7e473aa8a8 | ||
|
|
1e1edd698d | ||
|
|
aa42a0a4c4 | ||
|
|
ad61596f66 | ||
|
|
bd518b3038 | ||
|
|
db3129ab11 | ||
|
|
57a38a83e4 | ||
|
|
c0de43e2f3 | ||
|
|
c6e29c9ce4 | ||
|
|
35e8f5eddf | ||
|
|
40ed72aeff | ||
|
|
75033cf42e | ||
|
|
4a29a63d50 | ||
|
|
595a6847dc | ||
|
|
64f403b644 | ||
|
|
314517c378 | ||
|
|
b90fc55b3f | ||
|
|
cd57966810 | ||
|
|
8c0851e2b5 | ||
|
|
b9efa434d2 | ||
|
|
adc085a313 | ||
|
|
a2a2f67239 | ||
|
|
c30fba61ca | ||
|
|
f09b37d28f | ||
|
|
6cbc89b01d | ||
|
|
ffd538fbd0 | ||
|
|
1f27f66432 | ||
|
|
25f302f62f | ||
|
|
d54eb6ed73 | ||
|
|
24a6d77777 | ||
|
|
5cbebe7ec6 | ||
|
|
a31c310ffa | ||
|
|
752d0b5ca9 | ||
|
|
170131188a | ||
|
|
3269613139 | ||
|
|
52cc74fb85 | ||
|
|
d7d09b1d56 | ||
|
|
797f2b5929 | ||
|
|
fa5053fe38 | ||
|
|
c682c249bd | ||
|
|
01c229c7c1 | ||
|
|
83f39d6b22 | ||
|
|
715ec6e7c6 | ||
|
|
b6fa34e87f | ||
|
|
6ff14cc7a1 | ||
|
|
d606ce89e0 | ||
|
|
41e80f1d24 | ||
|
|
8decd66e26 | ||
|
|
c033849fb4 | ||
|
|
14054b2198 | ||
|
|
3c3a6712bd | ||
|
|
6a97ed41e0 | ||
|
|
bfedd6c953 | ||
|
|
23d72346b3 | ||
|
|
e9510875ea | ||
|
|
84d7b6c48f | ||
|
|
5f4af7024d | ||
|
|
3b16eb807e | ||
|
|
ef6b52049f | ||
|
|
a7b035bb8e | ||
|
|
645216b8eb | ||
|
|
2b1c18635e | ||
|
|
bb7a76617e | ||
|
|
ab50e7861a | ||
|
|
d52b88c816 | ||
|
|
e5d0a2a14c | ||
|
|
11e9db7ded | ||
|
|
23c1a78d01 | ||
|
|
e524423191 | ||
|
|
7286e71442 | ||
|
|
71681458a1 | ||
|
|
4d4b3c8867 | ||
|
|
681d808a74 | ||
|
|
876a0b27a6 | ||
|
|
9dce3b9a17 | ||
|
|
f77b487520 | ||
|
|
f3c73a5c8a | ||
|
|
37502b3747 | ||
|
|
309e84d14c | ||
|
|
ff464bef9f | ||
|
|
dfa5cd65f3 | ||
|
|
ccba5969a5 | ||
|
|
03baef713d | ||
|
|
a3617349bb | ||
|
|
e57b22d2fc | ||
|
|
6aabaa497d | ||
|
|
6caa142ead | ||
|
|
b01e6e30a4 | ||
|
|
5e3a612828 | ||
|
|
b498e7e83e | ||
|
|
7508643c89 | ||
|
|
24d2189399 | ||
|
|
4e470f34fd | ||
|
|
e3ca6448f2 | ||
|
|
9dadac7d93 | ||
|
|
0715bd0aba | ||
|
|
92772e7ee0 | ||
|
|
f9f6c879e0 | ||
|
|
ec7623f5c5 | ||
|
|
0163242258 | ||
|
|
3f9c8247c6 | ||
|
|
20865ad202 | ||
|
|
b5ac895b15 | ||
|
|
fbd550228b | ||
|
|
238758fc0b | ||
|
|
328a4339a4 | ||
|
|
747d958507 | ||
|
|
8622160e62 | ||
|
|
df1042e87d | ||
|
|
a5c6c11f09 | ||
|
|
7bd602cd45 | ||
|
|
2065468f1f | ||
|
|
7f9061d0c8 | ||
|
|
00a638393e | ||
|
|
f21194f877 | ||
|
|
1772351fc5 | ||
|
|
1030fc5e16 | ||
|
|
bef3ae96f6 | ||
|
|
c9fa5b2104 | ||
|
|
79cd8c0805 | ||
|
|
2b80420794 | ||
|
|
b8e18613b1 | ||
|
|
87289e4804 | ||
|
|
ba3a06a782 | ||
|
|
2764ef0417 | ||
|
|
ae0e89aa31 | ||
|
|
d0e99cc517 | ||
|
|
604fb01d6c | ||
|
|
256a1687d1 | ||
|
|
ea1ae58e54 | ||
|
|
fe9d119fe2 | ||
|
|
4a199533c1 | ||
|
|
7e785f1b6c | ||
|
|
85b4824ea2 | ||
|
|
d39af74bcc | ||
|
|
bfb52af454 | ||
|
|
5630e5d488 | ||
|
|
29780ecf22 | ||
|
|
a8b542feaa | ||
|
|
e85b182da7 | ||
|
|
84e9195869 | ||
|
|
7a739457c9 | ||
|
|
30905a7c36 | ||
|
|
0326a6834a | ||
|
|
7c75a67f9f | ||
|
|
1aa0fbf7d5 | ||
|
|
9adce93645 | ||
|
|
b7392ef62d | ||
|
|
6b55c90a93 | ||
|
|
9e9cd9ea4e | ||
|
|
ef91fb9e06 | ||
|
|
2f49c525b6 | ||
|
|
f78f179071 | ||
|
|
6ed310f8ce | ||
|
|
8da5a32b48 | ||
|
|
74fcdaa223 | ||
|
|
6b77b8fbbb | ||
|
|
9f9fdca53d | ||
|
|
540317017f | ||
|
|
567174fcde | ||
|
|
262bc1dcbe | ||
|
|
b931928434 | ||
|
|
7e2057a847 | ||
|
|
4a9b98f534 | ||
|
|
7bf45581e3 | ||
|
|
61a7fe6217 | ||
|
|
efa1a3f14f | ||
|
|
925866c3f0 | ||
|
|
7291ec6f88 | ||
|
|
16c9203956 | ||
|
|
b08f104663 | ||
|
|
82b7c6c290 | ||
|
|
63009a332f | ||
|
|
95e56db159 | ||
|
|
48f981036b | ||
|
|
79be77f986 | ||
|
|
4fafab19fc | ||
|
|
cd71f6e858 | ||
|
|
63d5068c2c | ||
|
|
a9bc7fdeb7 | ||
|
|
89dc2608bc | ||
|
|
c97a7e5158 | ||
|
|
32e2d24b15 | ||
|
|
c102aae819 | ||
|
|
bed72cb5ed | ||
|
|
f0c1046fe9 | ||
|
|
d88104d105 | ||
|
|
f3e21e5a82 | ||
|
|
a77bee8664 | ||
|
|
b5d0aed59e | ||
|
|
2440cc6af5 | ||
|
|
90114dfbe0 | ||
|
|
64b8cdf7dc |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,8 +1,9 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: LucasGGamerM
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # mastodon
|
||||
open_collective: # Replace with a single Open Collective username e.g., user1
|
||||
ko_fi: xsk22
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username e.g., user1
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -22,6 +22,8 @@ Steps to reproduce the behavior:
|
||||
**Does this happen in the official app?**
|
||||
|
||||
Does this issue also occur with the respective upstream release?
|
||||
(Please test using the respective `upstream-xxxxxx.apk` provided in [Releases](https://github.com/sk22/megalodon/releases) or at least using the current Mastodon version from the Play Store)
|
||||
|
||||
> No / Yes
|
||||
|
||||
> In case it does, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead.
|
||||
@@ -33,7 +35,7 @@ If applicable, add screenshots (and screen recordings, if possible) to help expl
|
||||
|
||||
**Version**
|
||||
|
||||
Moshidon version: [e.g. v1.1.4+fork.#]
|
||||
Megalodon version: [e.g. v1.1.4+fork.#]
|
||||
|
||||
**Crash log**
|
||||
|
||||
|
||||
11
.github/workflows/validate-gradle-wrapper.yml
vendored
Normal file
11
.github/workflows/validate-gradle-wrapper.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: Validate Gradle Wrapper
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: Validation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
165
README.md
165
README.md
@@ -1,40 +1,25 @@
|
||||

|
||||

|
||||
|
||||
# Moshidon, the material you mastodon client!
|
||||
# Megalodon
|
||||
|
||||
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly won’t ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
|
||||
<a href="#installation"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||
|
||||
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||
|
||||
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
|
||||
alt="Get it on IzzyOnDroid"
|
||||
height="80">](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda)
|
||||
---
|
||||
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly won’t ever be implemented, such as the federated timeline, unlisted posting and an image description viewer.
|
||||
|
||||
|
||||
## Key features
|
||||
|
||||
### **Material you theme support on Android 12+ devices!**
|
||||
|
||||
### **Show posts filtered with a warning!**
|
||||
|
||||
**Allows you to have filtered posts collapsed with a warning! As shown in the screenshots:**
|
||||
|
||||
Before | After
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
|
||||
### **Color themes**
|
||||
|
||||
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
||||
|
||||
### **Unlisted posting**
|
||||
|
||||
**Allows you to post publicly without having your post show up in trends, hashtags or public timelines (i.e., in the tabs “Local”, “Community” and “Posts”).**
|
||||
**Allows you to post publicly without having your post show up in trends, hashtags or public timelines (i.e., in the tabs “Community”, “Federated” and “Posts”).**
|
||||
|
||||
When posting with Unlisted visibility, your posts will still be publicly accessible in your profile. They will also be shown in people’s Home timelines, but only if they follow you or someone they follow reposted/replied to your post.
|
||||
When posting with Unlisted visibility, your posts will still be publicly accessible in your profile. They will also be shown in people’s Home timelines, but only if they follow you or someone they follow reblogged/replied to your post.
|
||||
|
||||
The Mastodon documentation has some more information about [Unlisted posting](https://docs.joinmastodon.org/user/posting/#unlisted) and [Public timelines](https://docs.joinmastodon.org/user/network/#timelines).
|
||||
|
||||
@@ -46,6 +31,12 @@ Despite being one of the main features of federated social media, the Federated
|
||||
|
||||
That’s one of the reasons why choosing a small, **well-moderated instance is important**. Instance admins and moderators should always make sure to ban abusive users and stop federating with instances who platform them. On well-moderated instances, the Federated timeline can be a welcoming place to meet new people!
|
||||
|
||||
### **Draft and schedule posts**
|
||||
|
||||
**Allows for preparing a post and scheduling it to send it automatically at a specific time.**
|
||||
|
||||
You can create drafts, edit them, send them manually later or set a scheduled date. Drafts are technically saved as scheduled posts, so you can view and edit them from other apps that support scheduled posts. Scheduled posts are handled by your home instance, so they'll work even if you uninstall Megalodon.
|
||||
|
||||
### **Image description viewer**
|
||||
|
||||
**Allows you to quickly check whether an image or video has an alternative text attached to it.**
|
||||
@@ -58,29 +49,71 @@ This is important to **ensure the content you’re sharing is as accessible as p
|
||||
|
||||
On the Fediverse, it’s quite common for people to pin posts they want others to read before following them. You can pin/unpin posts yourself by clicking the `⋯` button in the top right corner of your posts.
|
||||
|
||||
### **Bookmarks**
|
||||
|
||||
**They allow for quickly saving posts and viewing them through the Bookmarks button on the top right of your profile.**
|
||||
|
||||
To bookmark a post, press the button between the Favorite and Share buttons on the bottom of the post. Bookmarks are saved privately, so the post authors won’t know you saved their post – the list of bookmarked posts is only visible to you.
|
||||
|
||||
## Installation
|
||||
|
||||
**Press the download button above to download the APK. Open the downloaded file on your Android device to install it. Moshidon will automatically notify you about new updates inside the app.**
|
||||
### IzzyOnDroid
|
||||
|
||||
To install this app on your Android device, download the [latest release from GitHub](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk) and open it. You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
|
||||
|
||||
Moshidon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)’s automatic update checker. Megalodon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
|
||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||
|
||||
Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first:
|
||||
|
||||
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
|
||||
|
||||
### Google Play Store
|
||||
|
||||
[play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
|
||||
### F-Droid
|
||||
|
||||
**[F-Droid.org?](https://f-droid.org)** Not yet, sorry!
|
||||
|
||||
If you want, you can help me figure out if something's missing in the [Issue #47: F-Droid.org](https://github.com/sk22/megalodon/issues/47)
|
||||
|
||||
### Direct
|
||||
|
||||
Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
||||
|
||||
Megalodon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)’s automatic update checker. Megalodon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
|
||||
|
||||
---
|
||||
|
||||
Moshidon is also available in [IzzyOnDroid repo](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda), compatible with all F-Droid clients. The APK provided here is the same as the one included in the Releases.
|
||||
|
||||
## Release variants
|
||||
|
||||
All downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||
All downloads can be found on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
||||
|
||||
**`moshidon.apk`**
|
||||
**`megalodon.apk`**
|
||||
|
||||
Variant with an integrated updater. If you download Megalodon from here (and not from an app store), just download the regular `megalodon.apk`.
|
||||
|
||||
**`upstream-1234abc.apk`**
|
||||
|
||||
This is an **unmodified version** of the official [Mastodon for Android](https://github.com/mastodon/mastodon-android) app the respective Megalodon release is based on. Should you find any bugs in Megalodon (which you will), try to see if it occurs with this variant, too. The last 7 digits of the file name are important to know which version of the official app you're using.
|
||||
|
||||
<!-- **`megalodon-fdroid.apk`**
|
||||
|
||||
Variant without the integrated updater. This is the variant to be published to F-Droid.org where an integrated updater is not necessary. -->
|
||||
|
||||
---
|
||||
|
||||
## Contribution
|
||||
|
||||
### Translation
|
||||
|
||||
As with the source code, the translation is sourced from the official project, which you can contribute to on the official “**Mastodon for Android**” Crowdin project: https://crowdin.com/project/mastodon-for-android
|
||||
|
||||
There's also a handful of custom strings exclusive to this projects that would need to be translated. You can help translate **Megalodon** on Weblate: https://translate.codeberg.org/projects/megalodon/
|
||||
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
Variant with an integrated updater. If you download Moshidon from here (and not from an app store), just download the regular `moshidon.apk`.
|
||||
|
||||
---
|
||||
|
||||
@@ -89,23 +122,16 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||
|
||||
### Features
|
||||
|
||||
* [Adding the ability to have filtered posts show with a warning](https://github.com/LucasGGamerM/moshidon/tree/feature/filters_again)
|
||||
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
||||
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
||||
* Adding a useful private profile note box!*
|
||||
* Auto hiding the compose button on scroll!*
|
||||
* Adding the hability to remind yourself to add alt text to images!*
|
||||
* An indicator for if an image has alt text or not*
|
||||
* Adding the ability to have drafts!*
|
||||
* Also adding the ability to view announcements from your instance!*
|
||||
* Adding the ability to post for local timeline only (Only on instances that support it!)*
|
||||
* [Add “Federation” tab and change Discover tab order](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
|
||||
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
|
||||
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
||||
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
||||
* [Implement a bookmark button and list](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/bookmarks) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/22))
|
||||
* [Add “Check for update” button in addition to integrated update checker](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/check-for-update-button)
|
||||
* [Add “Mark media as sensitive” option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/mark-media-as-sensitive)
|
||||
* [Add settings to hide replies and reposts from the timeline](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
|
||||
* [Add settings to hide replies and reblogs from the timeline](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/filter-home-timeline) ([Pull request](https://github.com/mastodon/mastodon-android/pull/317))
|
||||
* [Follow and unfollow hashtags](https://github.com/sk22/megalodon/commit/7d38f031f197aa6cefaf53e39d929538689c1e4e) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/233))
|
||||
* [Notification bell for posts](https://github.com/sk22/megalodon/commit/b166ca705eb9169025ef32bbe6315b42491b57ea) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/81))
|
||||
* [Viewing lists and adding/removing users from lists](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:list-timeline-views) based on [@obstsalatschuessel](https://github.com/obstsalatschuessel)'s [Pull request](https://github.com/mastodon/mastodon-android/pull/286)
|
||||
@@ -115,12 +141,23 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||
* [Add notifications tab for posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/posts-notifications-tab)
|
||||
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-reply-visibility)
|
||||
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
|
||||
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
|
||||
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
|
||||
* [Add option to allow voting for multiple options on polls](https://github.com/sk22/megalodon/commit/5b28468efd49387b4f8b83f142f3adf3104ca60c)
|
||||
* [Add translate function](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/translate-button)
|
||||
* [Add language selector](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/language-selector)
|
||||
* [Implement deleting notifications](https://github.com/sk22/megalodon/commit/b0f9ce081f69f29ad59658fc00ca41372cd2677d) (disabled by default)
|
||||
* [Long-click boost button to "quote" a post](https://github.com/sk22/megalodon/commit/b25a237c20c6a924ed4d9b357999867c3a32b32b)
|
||||
* [Draft and schedule posts](https://github.com/sk22/megalodon/pull/217)
|
||||
* [Display original post when replying](https://github.com/sk22/megalodon/commit/375f8ceb2747705fedf43686681cc0e0b812f899)
|
||||
* [Display server announcements](https://github.com/sk22/megalodon/commit/84179bc207d6b69cc2a770a3c28fa0a39b0b54e8)
|
||||
* [Create](https://github.com/sk22/megalodon/commit/294595513a45037359b31377aafc25ae5b58d8e7), [edit](https://github.com/sk22/megalodon/commit/d47797bf7ac8cff3f9ba1cfee219a1bb2af21da6) and [delete](https://github.com/sk22/megalodon/commit/54c29fd787fc2cd0dfd2787ad796b8190f795973) lists
|
||||
* [Soft-blocking (by blocking and immediately unblocking)](https://github.com/sk22/megalodon/commit/e75d350b7a2709259e9fc5138e0e1f361bdb0972)
|
||||
* [Pinnable custom timelines](https://github.com/sk22/megalodon/pull/338/commits)
|
||||
* Support for local-only posts
|
||||
|
||||
|
||||
### Behavior
|
||||
|
||||
* Adding a bottom option for the publish button, allowing for easier use on larger screens!
|
||||
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
|
||||
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
|
||||
* [Display full image when adding image description](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
|
||||
@@ -128,6 +165,20 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/hide-interaction-numbers)
|
||||
* [Option to always reveal content warnings](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/cw-above-text)
|
||||
* [Option to disable scrolling title bars](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/disable-marquee)
|
||||
* [No ellipsis for long poll answers](https://github.com/mastodon/mastodon-android/commit/c9aae828e2518adccdc092e41f8d1f0489636271)
|
||||
* [Show poll vote button for multiple and single answer polls](https://github.com/mastodon/mastodon-android/commit/e14dfda2fdf32f0fa3043504ac5831683a87559a)
|
||||
* [Show own vote after voting](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28) ([Closes issue](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28))
|
||||
* [Make inline emoji search case-insensitive and don't only search from start of emoji names](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:better-inline-emoji-search) ([Pull request](https://github.com/mastodon/mastodon-android/pull/445))
|
||||
* [Include subject line when sharing e.g. a website to Megalodon](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:external-share-include-subject)
|
||||
* [Improve semantics for voting on polls (radio buttons and checkboxes)](https://github.com/sk22/megalodon/commit/6fd58c96827cb1d2da329cebdc170a1425dd18d7)
|
||||
* [Copy post URL when long-pressing share button](https://github.com/sk22/megalodon/commit/ba36347f03278763ecec617b1ce57ba89db7be72)
|
||||
* [Add option to disable swiping between tabs](https://github.com/sk22/megalodon/commit/1f20b21fc84bf006c1ec14bd2229cbfad5215ec8)
|
||||
* [Resolve Fediverse links in the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/open-urls-in-app)
|
||||
* [Preserve whitespaces in HTML](https://github.com/sk22/megalodon/commit/7d876bddc7a07d98f0fecbf62b13bdb9fcce3412)
|
||||
* [Long-click to copy links](https://github.com/sk22/megalodon/commit/b32e32274923a94742a9926ef38785f746d41405)
|
||||
* Improved filtering using Mastodon 4.0 API: [#202](https://github.com/sk22/megalodon/pull/202), [#212](https://github.com/sk22/megalodon/pull/212), [#255](https://github.com/sk22/megalodon/pull/255) by [@thiagojedi](https://github.com/thiagojedi)
|
||||
* [Support admin notifications](https://github.com/sk22/megalodon/commit/c12a6eaee6b609bc53eb0a45d9199f37d5241801) and [notifications for edited reblogged posts](https://github.com/sk22/megalodon/commit/900e8fb2e9353002c16d15e06b78d2731e121601)
|
||||
* [Android file opener added back in addition to image picker](https://github.com/sk22/megalodon/commit/3a6ace53d5ab01e28077c9c930cb6ed487b78031)
|
||||
|
||||
|
||||
### Visual
|
||||
@@ -135,6 +186,16 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
||||
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:compact-extended-footer)
|
||||
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:true-black-improvements)
|
||||
* [Profile header tweaks](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:ui/profile-header-tweaks)
|
||||
* [Custom color themes](https://github.com/sk22/megalodon/pull/124) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom "megalodon" text logo](https://github.com/sk22/megalodon/commit/563afd487ca5c608cfbb00fa3909d3c27384acc0) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom login screen](https://github.com/sk22/megalodon/commit/9bbf8c4618dbe13accaeb3b5482bf3fe88cac4c0)
|
||||
* [More distinct filled boost icon](https://github.com/sk22/megalodon/commits/more-distinct-filled-boost-icon)
|
||||
* Material You color theme by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Animations for interaction buttons](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/animate-buttons)
|
||||
* [Dedicated icons for different notification types](https://github.com/sk22/megalodon/pull/178) by [@florian-obernberger](https://github.com/florian-obernberger)
|
||||
* Scale text according to system settings
|
||||
* Header in timeline for followed hashtags
|
||||
* [Indicator for missing alt texts](https://github.com/sk22/megalodon/commit/c0c276f03e793b78c478c17dfdef24a66ef7cedb)
|
||||
|
||||
|
||||
## Building
|
||||
@@ -151,6 +212,4 @@ This project is released under the [GPL-3 License](./LICENSE).
|
||||
|
||||
## Links
|
||||
|
||||
[Official matrix chatroom:](https://matrix.to/#/#moshidon:matrix.org) https://matrix.to/#/#moshidon:matrix.org
|
||||
|
||||
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>
|
||||
<a rel="me" href="https://floss.social/@megalodon">@megalodon<wbr>@floss.social</a>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
title: Moshidon
|
||||
title: Megalodon
|
||||
layout: default
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Moshidon</title>
|
||||
<title>Megalodon</title>
|
||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||
<link rel="me" href="https://floss.social/@mastodon">
|
||||
<link rel="me" href="https://floss.social/@megalodon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||
</head>
|
||||
<body class="markdown-body">
|
||||
@@ -14,4 +14,4 @@
|
||||
{{ content }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -5,7 +5,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath 'com.android.tools.build:gradle:8.0.0'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
files:
|
||||
- source: /mastodon/src/main/res/values/strings.xml
|
||||
translation: /mastodon/src/main/res/values-%android_code%/strings.xml
|
||||
- source: /fastlane/metadata/android/en-US/*.txt
|
||||
translation: /fastlane/metadata/android/%locale%/%original_file_name%
|
||||
@@ -16,4 +16,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=false
|
||||
android.enableJetifier=false
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonFinalResIds=false
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
#Thu Jan 13 11:33:43 MSK 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
288
gradlew
vendored
288
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,67 +17,98 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@@ -106,80 +137,109 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -2,18 +2,24 @@ plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 33
|
||||
defaultConfig {
|
||||
archivesBaseName = "moshidon"
|
||||
applicationId "org.joinmastodon.android.moshinda"
|
||||
archivesBaseName = "megalodon"
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 93
|
||||
versionName "1.1.4+fork.93.moshinda"
|
||||
versionCode 83
|
||||
versionName "1.2.3+fork.83"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
||||
}
|
||||
resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW']
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
@@ -49,14 +55,19 @@ android {
|
||||
setRoot "src/github"
|
||||
}
|
||||
}
|
||||
lintOptions{
|
||||
checkReleaseBuilds false
|
||||
namespace 'org.joinmastodon.android'
|
||||
lint {
|
||||
abortOnError false
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'androidx.annotation:annotation:1.3.0'
|
||||
api 'androidx.annotation:annotation:1.6.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
||||
implementation 'me.grishka.litex:recyclerview:1.2.1.1'
|
||||
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0.1'
|
||||
@@ -65,7 +76,7 @@ dependencies {
|
||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||
implementation 'me.grishka.appkit:appkit:1.2.7'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
implementation 'com.google.code.gson:gson:2.9.0'
|
||||
implementation 'org.jsoup:jsoup:1.14.3'
|
||||
implementation 'com.squareup:otto:1.3.8'
|
||||
implementation 'de.psdev:async-otto:1.0.3'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.joinmastodon.android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -115,7 +115,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||
|
||||
private void actuallyCheckForUpdates(){
|
||||
Request req=new Request.Builder()
|
||||
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
|
||||
.url("https://api.github.com/repos/sk22/megalodon/releases")
|
||||
.build();
|
||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||
try(Response resp=call.execute()){
|
||||
@@ -153,7 +153,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||
JsonObject asset=el.getAsJsonObject();
|
||||
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||
if("megalodon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||
long size=asset.get("size").getAsLong();
|
||||
String url=asset.get("browser_download_url").getAsString();
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M3.897 4.054L3.97 3.97c0.266-0.267 0.683-0.29 0.976-0.073L5.03 3.97 10 8.939l4.97-4.97c0.266-0.266 0.683-0.29 0.976-0.072L16.03 3.97c0.267 0.266 0.29 0.683 0.073 0.976L16.03 5.03 11.061 10l4.97 4.97c0.266 0.266 0.29 0.683 0.072 0.976L16.03 16.03c-0.266 0.267-0.683 0.29-0.976 0.073L14.97 16.03 10 11.061l-4.97 4.97c-0.266 0.266-0.683 0.29-0.976 0.072L3.97 16.03c-0.267-0.266-0.29-0.683-0.073-0.976L3.97 14.97 8.939 10l-4.97-4.97C3.704 4.764 3.68 4.347 3.898 4.054L3.97 3.97 3.897 4.054z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M22 6.5c0 3.038-2.462 5.5-5.5 5.5S11 9.538 11 6.5 13.462 1 16.5 1 22 3.462 22 6.5zm-7.146-2.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L15.793 6.5l-1.647 1.646c-0.195 0.196-0.195 0.512 0 0.707 0.196 0.196 0.512 0.196 0.708 0L16.5 7.208l1.646 1.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.707L17.207 6.5l1.647-1.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L16.5 5.793l-1.646-1.647zM19.5 14v-1.732c0.551-0.287 1.056-0.651 1.5-1.078v7.56c0 1.733-1.357 3.15-3.066 3.245L17.75 22H6.25c-1.733 0-3.15-1.357-3.245-3.066L3 18.75V7.25C3 5.517 4.356 4.1 6.066 4.005L6.25 4h4.248c-0.198 0.474-0.34 0.977-0.422 1.5H6.25c-0.918 0-1.671 0.707-1.744 1.606L4.5 7.25V14H9c0.38 0 0.694 0.282 0.743 0.648L9.75 14.75C9.75 15.993 10.757 17 12 17c1.19 0 2.166-0.925 2.245-2.096l0.005-0.154c0-0.38 0.282-0.694 0.648-0.743L15 14h4.5zm-15 1.5v3.25c0 0.918 0.707 1.671 1.606 1.744L6.25 20.5h11.5c0.918 0 1.671-0.707 1.744-1.607L19.5 18.75V15.5h-3.825c-0.335 1.648-1.75 2.904-3.475 2.995L12 18.5c-1.747 0-3.215-1.195-3.632-2.812L8.325 15.5H4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M26 7.5c0 3.59-2.91 6.5-6.5 6.5S13 11.09 13 7.5 15.91 1 19.5 1 26 3.91 26 7.5zm-9.146-3.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L18.793 7.5l-2.647 2.646c-0.195 0.196-0.195 0.512 0 0.708 0.196 0.195 0.512 0.195 0.708 0L19.5 8.207l2.646 2.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.708L20.207 7.5l2.647-2.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L19.5 6.793l-2.646-2.647zM25 22.75V12.6c-0.443 0.476-0.947 0.896-1.5 1.245V16h-6l-0.102 0.007c-0.366 0.05-0.648 0.363-0.648 0.743 0 1.519-1.231 2.75-2.75 2.75s-2.75-1.231-2.75-2.75l-0.007-0.102C11.193 16.282 10.88 16 10.5 16h-6V7.25c0-0.966 0.784-1.75 1.75-1.75h6.02c0.145-0.525 0.345-1.028 0.595-1.5H6.25C4.455 4 3 5.455 3 7.25v15.5C3 24.545 4.455 26 6.25 26h15.5c1.795 0 3.25-1.455 3.25-3.25zm-20.5 0V17.5h5.316l0.041 0.204C10.291 19.592 11.982 21 14 21l0.215-0.005c1.994-0.1 3.627-1.574 3.969-3.495H23.5v5.25c0 0.966-0.784 1.75-1.75 1.75H6.25c-0.966 0-1.75-0.784-1.75-1.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M10 2c4.418 0 8 3.582 8 8 0 2.706-1.142 4.5-3 4.5-1.226 0-2.14-0.781-2.62-2.09C11.784 13.393 10.781 14 9.5 14 7.36 14 6 12.307 6 10c0-2.337 1.313-4 3.5-4 1.052 0 1.901 0.385 2.5 1.044V6.5C12 6.224 12.224 6 12.5 6c0.245 0 0.45 0.177 0.492 0.41L13 6.5V10c0 2.223 0.813 3.5 2 3.5s2-1.277 2-3.5c0-3.866-3.134-7-7-7s-7 3.134-7 7 3.134 7 7 7c0.823 0 1.626-0.142 2.383-0.416 0.26-0.094 0.547 0.04 0.64 0.3 0.095 0.26-0.04 0.546-0.3 0.64C11.859 17.838 10.94 18 10 18c-4.418 0-8-3.582-8-8s3.582-8 8-8zM9.5 7C7.924 7 7 8.17 7 10c0 1.797 0.966 3 2.5 3s2.5-1.203 2.5-3c0-1.83-0.924-3-2.5-3z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M6.25 4.5C5.283 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.783 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75v-4c0-0.414 0.335-0.75 0.75-0.75 0.414 0 0.75 0.336 0.75 0.75v4c0 1.795-1.456 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25C3 4.455 4.455 3 6.25 3h4C10.664 3 11 3.336 11 3.75S10.664 4.5 10.25 4.5h-4zM13 3.75C13 3.336 13.335 3 13.75 3h6.5C20.664 3 21 3.336 21 3.75v6.5c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75V5.56l-5.22 5.22c-0.293 0.293-0.768 0.293-1.06 0-0.293-0.293-0.293-0.768 0-1.06l5.22-5.22h-4.69C13.335 4.5 13 4.164 13 3.75z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M8.502 11.5c0.554 0 1.002 0.448 1.002 1.002 0 0.553-0.448 1.002-1.002 1.002-0.553 0-1.002-0.449-1.002-1.002 0-0.554 0.449-1.003 1.002-1.003zM12 4.353v6.651h7.442L17.72 9.28c-0.267-0.266-0.29-0.683-0.073-0.977L17.72 8.22c0.266-0.266 0.683-0.29 0.976-0.072L18.78 8.22l2.997 2.998c0.266 0.266 0.29 0.682 0.073 0.976l-0.073 0.084-2.996 3.003c-0.293 0.294-0.767 0.294-1.06 0.002-0.267-0.266-0.292-0.683-0.075-0.977l0.073-0.084 1.713-1.717h-7.431L12 19.25c0 0.466-0.421 0.82-0.88 0.738l-8.5-1.501C2.26 18.424 2 18.112 2 17.748V5.75c0-0.368 0.266-0.681 0.628-0.74l8.5-1.396C11.585 3.539 12 3.89 12 4.354zm-1.5 0.883l-7 1.15v10.732l7 1.236V5.237zM13 18.5h0.765l0.102-0.007c0.366-0.05 0.649-0.364 0.648-0.744l-0.007-4.25H13v5zm0.002-8.502L13 8.726V5h0.745c0.38 0 0.693 0.281 0.743 0.647l0.007 0.101L14.502 10h-1.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M14.704 3.44C14.895 3.667 15 3.953 15 4.248V19.75c0 0.69-0.56 1.25-1.25 1.25-0.296 0-0.582-0.105-0.808-0.296l-4.967-4.206H4.25c-1.243 0-2.25-1.008-2.25-2.25v-4.5c0-1.243 1.007-2.25 2.25-2.25h3.725l4.968-4.204c0.526-0.446 1.315-0.38 1.761 0.147zM13.5 4.787l-4.975 4.21H4.25c-0.414 0-0.75 0.337-0.75 0.75v4.5c0 0.415 0.336 0.75 0.75 0.75h4.275L13.5 19.21V4.787z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M16.5 4.814c0-1.094-1.307-1.66-2.105-0.912l-4.937 4.63C9.134 8.836 8.706 9.005 8.261 9.005H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912V4.814zm-6.016 4.812L15 5.39v17.216l-4.516-4.232c-0.602-0.564-1.397-0.878-2.222-0.878H5.25c-0.966 0-1.75-0.784-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.826 0 1.62-0.314 2.223-0.88z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06L6.438 7.5H4.25C3.007 7.499 2 8.506 2 9.749v4.497c0 1.243 1.007 2.25 2.25 2.25h3.68c0.183 0 0.36 0.068 0.498 0.19l4.491 3.994C13.725 21.396 15 20.824 15 19.746V16.06l5.72 5.72c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM13.5 14.56v4.629l-4.075-3.624c-0.412-0.366-0.944-0.569-1.495-0.569H4.25c-0.414 0-0.75-0.335-0.75-0.75V9.75C3.5 9.335 3.836 9 4.25 9h3.688l5.562 5.56zm0-9.753v5.511l1.5 1.5V4.25c0-1.079-1.274-1.65-2.08-0.934l-3.4 3.022 1.063 1.063L13.5 4.807zm3.641 9.152l1.138 1.138C18.741 14.163 19 13.111 19 12c0-1.203-0.304-2.338-0.84-3.328-0.198-0.364-0.653-0.5-1.017-0.303-0.364 0.197-0.5 0.653-0.303 1.017 0.42 0.777 0.66 1.666 0.66 2.614 0 0.691-0.127 1.351-0.359 1.96zm2.247 2.247l1.093 1.094C21.445 15.763 22 13.946 22 12c0-2.226-0.728-4.284-1.96-5.946-0.246-0.333-0.716-0.403-1.048-0.157-0.333 0.247-0.403 0.716-0.157 1.05C19.881 8.358 20.5 10.106 20.5 12c0 1.531-0.404 2.966-1.112 4.206z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,3 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||
<path android:pathData="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l5.724 5.725H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912v-5.623l8.22 8.22c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM15 16.06v6.547l-4.516-4.231c-0.602-0.565-1.397-0.879-2.222-0.879H5.25c-0.966 0-1.75-0.783-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.35 0 0.693-0.056 1.02-0.164L15 16.061zm-4.378-8.62l1.061 1.061L15 5.392v6.427l1.5 1.5V4.814c0-1.094-1.307-1.66-2.105-0.912L10.622 7.44zm9.55 9.55l1.137 1.137C21.912 16.88 22.25 15.478 22.25 14c0-2.136-0.706-4.11-1.897-5.697-0.249-0.332-0.719-0.399-1.05-0.15-0.332 0.249-0.399 0.719-0.15 1.05C20.156 10.54 20.75 12.199 20.75 14c0 1.058-0.205 2.067-0.578 2.99zm2.803 2.803l1.095 1.096c1.224-2.008 1.93-4.366 1.93-6.89 0-3.35-1.245-6.414-3.298-8.747-0.274-0.31-0.747-0.341-1.058-0.068-0.311 0.274-0.342 0.748-0.068 1.059C23.396 8.313 24.5 11.027 24.5 14c0 2.107-0.554 4.084-1.525 5.793z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.joinmastodon.android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
@@ -17,12 +16,15 @@
|
||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.TRANSLATE" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".MastodonApp"
|
||||
android:allowBackup="true"
|
||||
android:label="@string/mo_app_name"
|
||||
android:label="@string/sk_app_name"
|
||||
android:supportsRtl="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
@@ -36,12 +38,28 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".PanicResponderActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ExitActivity"
|
||||
android:exported="false"
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
<activity android:name=".OAuthActivity" android:exported="true" android:configChanges="orientation|screenSize" android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:scheme="moshidon-android-auth" android:host="callback"/>
|
||||
<data android:scheme="megalodon-android-auth" android:host="callback"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
# lists.d Mastodon Blocklist (c) 2022 Greyhat Academy LICENSED UNDER: CC-BY-NC-SA 4.0
|
||||
# https://raw.githubusercontent.com/greyhat-academy/lists.d/main/mastodon.domains.block.list.tsv
|
||||
# This list contains domains of toxic mastodon instances
|
||||
# Last-Modified: 1672044500
|
||||
|
||||
# gab - a neonazi social network
|
||||
gab.ai
|
||||
gab.com
|
||||
gab.protohype.net
|
||||
|
||||
# consequence-free speech
|
||||
social.unzensiert.to
|
||||
freeatlantis.com
|
||||
|
||||
# reactionary bigotry and hatespeech against magrinalized groups
|
||||
poa.st
|
||||
freespeechextremist.com
|
||||
rdrama.cc
|
||||
outpoa.st
|
||||
anime.website
|
||||
gameliberty.club
|
||||
social.byoblu.com
|
||||
yggdrasil.social
|
||||
smuglo.li
|
||||
dogeposting.social
|
||||
unsafe.space
|
||||
freezepeach.xyz
|
||||
|
||||
# + CSAM
|
||||
rojogato.com
|
||||
|
||||
# antivaxxer shitposting & fearmongering
|
||||
shadowsocial.org
|
||||
|
||||
# Kiwifarms
|
||||
kiwifarms.net
|
||||
kiwifarms.cc
|
||||
kiwifarms.is
|
||||
kiwifarms.pleroma.net
|
||||
|
||||
|
||||
# https://mastodon.art/@Curator/109649354849593592
|
||||
|
||||
poa.st antisemitic racist homophobic
|
||||
nicecrew.digital antisemitic
|
||||
beefyboys.win antisemitic racist homophobic harassment
|
||||
cawfee.club antisemitic racist homophobic
|
||||
comfyboy.club antisemitic racist homophobic
|
||||
freespeechextremist.com racist homophobic
|
||||
cum.salon racist misogynist
|
||||
bae.st racist
|
||||
natehiggers.online racist
|
||||
rapemeat.solutions misogynist
|
||||
rapist.town misogynist
|
||||
rapefeminists.network misogynist
|
||||
kiwifarms.cc harassment
|
||||
noagendasocial.com noagenda
|
||||
posting.lolicon.rocks underage
|
||||
urchan.org harassment homophobic racist
|
||||
ryona.agency harassment
|
||||
yggdrasil.social antisemitic homophobic racist
|
||||
genderheretics.xyz transphobic
|
||||
baraag.net underage
|
||||
lolison.top underage
|
||||
shota.house underage
|
||||
shota.social underage
|
||||
aethy.com underage
|
||||
taullo.social underage
|
||||
childpawn.shop underage
|
||||
posting.lolicon.rocks underage
|
||||
loli.best underage
|
||||
gothloli.club underage
|
||||
smuglo.li underage
|
||||
youjo.love underage
|
||||
pedo.school underage
|
||||
lolison.network underage
|
||||
freak.university underage
|
||||
mirr0r.city underage
|
||||
xhais.love underage
|
||||
refusal.biz underage
|
||||
refusal.llc underage
|
||||
mirr0r.city underage
|
||||
nnia.space underage
|
||||
ignorelist.com malicious
|
||||
repl.co malicious
|
||||
|
||||
# custom
|
||||
|
||||
pawoo.net csam
|
||||
|
171
mastodon/src/main/assets/blocks.txt
Normal file
171
mastodon/src/main/assets/blocks.txt
Normal file
@@ -0,0 +1,171 @@
|
||||
13bells.com
|
||||
4aem.com
|
||||
aethy.com
|
||||
anime.website
|
||||
annihilation.social
|
||||
anon-kenkai.com
|
||||
asbestos.cafe
|
||||
bae.st
|
||||
bajax.us
|
||||
banepo.st
|
||||
baraag.net
|
||||
beefyboys.win
|
||||
beepboop.ga
|
||||
berserker.town
|
||||
bikeshed.party
|
||||
boks.moe
|
||||
brainsoap.net
|
||||
breastmilk.club
|
||||
brighteon.social
|
||||
cawfee.club
|
||||
clew.lol
|
||||
clubcyberia.co
|
||||
collapsitarian.io
|
||||
comfyboy.club
|
||||
contrapointsfan.club
|
||||
cum.camp
|
||||
cum.salon
|
||||
cybercriminal.eu
|
||||
darknight-coffee.org
|
||||
dembased.xyz
|
||||
desupost.soy
|
||||
detroitriotcity.com
|
||||
eatthebugs.social
|
||||
eientei.org
|
||||
elementality.org
|
||||
eveningzoo.club
|
||||
firedragonstudios.com
|
||||
firefaithfellowship.com
|
||||
fluf.club
|
||||
foxfam.club
|
||||
freak.university
|
||||
freeatlantis.com
|
||||
freecumextremist.com
|
||||
freedomstrike.org
|
||||
freesoftwareextremist.com
|
||||
freespeech.group
|
||||
freespeechextremist.com
|
||||
freetalklive.com
|
||||
froth.zone
|
||||
fulltermprivacy.com
|
||||
gameliberty.club
|
||||
gearlandia.haus
|
||||
genderheretics.xyz
|
||||
geofront.rocks
|
||||
gleasonator.com
|
||||
glee.li
|
||||
glindr.org
|
||||
goyim.app
|
||||
goyslop.cafe
|
||||
haeder.net
|
||||
handholding.io
|
||||
hidamari.apartments
|
||||
hitchhiker.social
|
||||
hunk.city
|
||||
iddqd.social
|
||||
intkos.link
|
||||
justicewarrior.social
|
||||
kawa-kun.com
|
||||
kitsunemimi.club
|
||||
kiwifarms.cc
|
||||
kompost.cz
|
||||
kurosawa.moe
|
||||
leafposter.club
|
||||
leftychan.net
|
||||
lewdieheaven.com
|
||||
liberdon.com
|
||||
ligma.pro
|
||||
lizards.live
|
||||
lolicon.rocks
|
||||
lolison.top
|
||||
lovingexpressions.net
|
||||
lucasvl.nl
|
||||
mahodou.moe
|
||||
makemysarcophagus.com
|
||||
maladaptive.art
|
||||
masochi.st
|
||||
mastinator.com
|
||||
merovingian.club
|
||||
midwaytrades.com
|
||||
mirr0r.city
|
||||
moa.st
|
||||
mouse.services
|
||||
mugicha.club
|
||||
narrativerry.xyz
|
||||
natehiggers.online
|
||||
neckbeard.xyz
|
||||
needs.vodka
|
||||
neenster.org
|
||||
nicecrew.digital
|
||||
nnia.space
|
||||
noagendasocial.com
|
||||
noagendasocial.nl
|
||||
noagendatube.com
|
||||
nobodyhasthe.biz
|
||||
nukem.biz
|
||||
obo.sh
|
||||
onionfarms.org
|
||||
outpoa.st
|
||||
pawlicker.com
|
||||
pawoo.net
|
||||
pedo.school
|
||||
piazza.today
|
||||
pibvt.net
|
||||
pieville.net
|
||||
pisskey.io
|
||||
plagu.ee
|
||||
pmth.us
|
||||
poa.st
|
||||
poast.org
|
||||
poast.tv
|
||||
poster.place
|
||||
prospeech.space
|
||||
quodverum.com
|
||||
rakket.app
|
||||
rapemeat.solutions
|
||||
rdrama.cc
|
||||
rebelbase.site
|
||||
retardedniggers.forsale
|
||||
rojogato.com
|
||||
ryona.agency
|
||||
schwartzwelt.xyz
|
||||
seal.cafe
|
||||
shigusegubu.club
|
||||
shitpost.cloud
|
||||
shitposter.club
|
||||
shota.house
|
||||
silliness.observer
|
||||
skinheads.eu
|
||||
skinheads.io
|
||||
skinheads.social
|
||||
skinheads.uk
|
||||
skippers-bin.com
|
||||
skyshanty.xyz
|
||||
slash.cl
|
||||
sleepy.cafe
|
||||
smuglo.li
|
||||
sneed.social
|
||||
sonichu.com
|
||||
spinster.xyz
|
||||
springbo.cc
|
||||
starnix.network
|
||||
stereophonic.space
|
||||
strelizia.net
|
||||
syspxl.xyz
|
||||
tastingtraffic.net
|
||||
teci.world
|
||||
theapex.social
|
||||
thepostearthdestination.com
|
||||
tkammer.de
|
||||
trumpislovetrumpis.life
|
||||
truthsocial.co.in
|
||||
urchan.org
|
||||
varishangout.net
|
||||
whinge.house
|
||||
whinge.town
|
||||
wideboys.org
|
||||
wolfgirl.bar
|
||||
xn--p1abe3d.xn--80asehdb
|
||||
yggdrasil.social
|
||||
youjo.love
|
||||
zztails.gay
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 358 KiB |
@@ -0,0 +1,24 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class ExitActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
public static void exit(Context context) {
|
||||
Intent intent = new Intent(context, ExitActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.jsoup.internal.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -51,9 +52,15 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
||||
String subject = "";
|
||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
|
||||
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
||||
if (!subject.isBlank()) builder.append(subject).append("\n\n");
|
||||
if (!StringUtil.isBlank(subject)) builder.append(subject).append("\n\n");
|
||||
}
|
||||
if (intent.hasExtra(Intent.EXTRA_TEXT)) {
|
||||
String extra = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
if (!StringUtil.isBlank(extra)) {
|
||||
if (extra.startsWith(subject)) extra = extra.substring(subject.length()).trim();
|
||||
builder.append(extra).append("\n\n");
|
||||
}
|
||||
}
|
||||
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
|
||||
String text=builder.toString();
|
||||
List<Uri> mediaUris;
|
||||
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
||||
@@ -80,8 +87,7 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
||||
args.putString("account", accountID);
|
||||
if(!TextUtils.isEmpty(text))
|
||||
args.putString("prefilledText", text);
|
||||
if(!subject.isBlank())
|
||||
args.putInt("selectionEnd", subject.length());
|
||||
args.putInt("selectionStart", StringUtil.isBlank(subject) ? 0 : subject.length());
|
||||
if(mediaUris!=null && !mediaUris.isEmpty())
|
||||
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
||||
Fragment fragment=new ComposeFragment();
|
||||
|
||||
@@ -4,11 +4,11 @@ import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
import org.joinmastodon.android.model.TimelineDefinition;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@@ -30,14 +30,12 @@ public class GlobalUserPreferences{
|
||||
public static boolean alwaysExpandContentWarnings;
|
||||
public static boolean disableMarquee;
|
||||
public static boolean disableSwipe;
|
||||
public static boolean disableDividers;
|
||||
public static boolean voteButtonForSingleChoice;
|
||||
public static boolean uniformNotificationIcon;
|
||||
public static boolean enableDeleteNotifications;
|
||||
public static boolean relocatePublishButton;
|
||||
public static boolean translateButtonOpenedOnly;
|
||||
public static boolean uniformNotificationIcon;
|
||||
public static boolean reduceMotion;
|
||||
public static boolean keepOnlyLatestNotification;
|
||||
public static boolean enableFabAutoHide;
|
||||
public static boolean disableAltTextReminder;
|
||||
public static boolean showAltIndicator;
|
||||
public static boolean showNoAltIndicator;
|
||||
@@ -46,21 +44,30 @@ public class GlobalUserPreferences{
|
||||
public static boolean bottomEncoding;
|
||||
public static boolean collapseLongPosts;
|
||||
public static boolean spectatorMode;
|
||||
public static boolean autoHideFab;
|
||||
public static boolean replyLineAboveHeader;
|
||||
public static boolean compactReblogReplyLine;
|
||||
public static boolean confirmBeforeReblog;
|
||||
public static String publishButtonText;
|
||||
public static ThemePreference theme;
|
||||
public static ColorPreference color;
|
||||
|
||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
||||
private final static Type accountsDefaultContentTypesType = new TypeToken<Map<String, ContentType>>() {}.getType();
|
||||
public static Map<String, List<String>> recentLanguages;
|
||||
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
|
||||
public static Set<String> accountsWithLocalOnlySupport;
|
||||
public static Set<String> accountsInGlitchMode;
|
||||
public static Set<String> accountsWithContentTypesEnabled;
|
||||
public static Map<String, ContentType> accountsDefaultContentTypes;
|
||||
|
||||
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
||||
public static Map<String, Integer> recentEmojis;
|
||||
/**
|
||||
* Pleroma
|
||||
*/
|
||||
public static String replyVisibility;
|
||||
|
||||
private static SharedPreferences getPrefs(){
|
||||
private static SharedPreferences getPrefs(){
|
||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@@ -79,18 +86,16 @@ public class GlobalUserPreferences{
|
||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
|
||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
|
||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||
disableSwipe=prefs.getBoolean("disableSwipe", false);
|
||||
disableDividers=prefs.getBoolean("disableDividers", true);
|
||||
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
|
||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", false);
|
||||
translateButtonOpenedOnly=prefs.getBoolean("translateButtonOpenedOnly", false);
|
||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
|
||||
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
|
||||
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
||||
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
||||
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
||||
@@ -99,24 +104,25 @@ public class GlobalUserPreferences{
|
||||
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
||||
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
||||
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
||||
autoHideFab=prefs.getBoolean("autoHideFab", true);
|
||||
replyLineAboveHeader=prefs.getBoolean("replyLineAboveHeader", true);
|
||||
compactReblogReplyLine=prefs.getBoolean("compactReblogReplyLine", true);
|
||||
confirmBeforeReblog=prefs.getBoolean("confirmBeforeReblog", false);
|
||||
publishButtonText=prefs.getString("publishButtonText", "");
|
||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
||||
publishButtonText=prefs.getString("publishButtonText", "");
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>());
|
||||
pinnedTimelines=fromJson(prefs.getString("pinnedTimelines", null), pinnedTimelinesType, new HashMap<>());
|
||||
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
||||
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
|
||||
replyVisibility=prefs.getString("replyVisibility", null);
|
||||
accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>());
|
||||
accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>());
|
||||
|
||||
try {
|
||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.MATERIAL3.name()));
|
||||
}else{
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PURPLE.name()));
|
||||
}
|
||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
||||
} catch (IllegalArgumentException|ClassCastException ignored) {
|
||||
// invalid color name or color was previously saved as integer
|
||||
color=ColorPreference.PURPLE;
|
||||
color=ColorPreference.PINK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,13 +139,11 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||
.putBoolean("disableMarquee", disableMarquee)
|
||||
.putBoolean("disableSwipe", disableSwipe)
|
||||
.putBoolean("disableDividers", disableDividers)
|
||||
.putBoolean("relocatePublishButton", relocatePublishButton)
|
||||
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
||||
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
||||
.putBoolean("translateButtonOpenedOnly", translateButtonOpenedOnly)
|
||||
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
||||
.putBoolean("reduceMotion", reduceMotion)
|
||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||
.putBoolean("enableFabAutoHide", enableFabAutoHide)
|
||||
.putBoolean("disableAltTextReminder", disableAltTextReminder)
|
||||
.putBoolean("showAltIndicator", showAltIndicator)
|
||||
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
||||
@@ -147,15 +151,21 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
||||
.putBoolean("spectatorMode", spectatorMode)
|
||||
.putBoolean("autoHideFab", autoHideFab)
|
||||
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
||||
.putString("publishButtonText", publishButtonText)
|
||||
.putBoolean("bottomEncoding", bottomEncoding)
|
||||
.putBoolean("replyLineAboveHeader", replyLineAboveHeader)
|
||||
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
|
||||
.putInt("theme", theme.ordinal())
|
||||
.putString("color", color.name())
|
||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||
.putString("pinnedTimelines", gson.toJson(pinnedTimelines))
|
||||
.putString("recentEmojis", gson.toJson(recentEmojis))
|
||||
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
||||
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
||||
.putString("replyVisibility", replyVisibility)
|
||||
.putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled)
|
||||
.putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes))
|
||||
.apply();
|
||||
}
|
||||
|
||||
@@ -167,8 +177,7 @@ public class GlobalUserPreferences{
|
||||
BLUE,
|
||||
BROWN,
|
||||
RED,
|
||||
YELLOW,
|
||||
NORD
|
||||
YELLOW
|
||||
}
|
||||
|
||||
public enum ThemePreference{
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
|
||||
|
||||
public class PanicResponderActivity extends Activity {
|
||||
public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final Intent intent = getIntent();
|
||||
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
|
||||
AccountSessionManager.getInstance().getLoggedInAccounts().forEach(accountSession -> logOut(accountSession.getID()));
|
||||
ExitActivity.exit(this);
|
||||
}
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
private void logOut(String accountID){
|
||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
new RevokeOauthToken(session.app.clientId, session.app.clientSecret, session.token.accessToken)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Object result){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
onLoggedOut(accountID);
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
private void onLoggedOut(String accountID){
|
||||
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -16,15 +17,26 @@ import android.util.Log;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
||||
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
|
||||
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.NotificationAction;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.PushNotification;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
@@ -37,11 +49,14 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
private static final String TAG="PushNotificationReceive";
|
||||
|
||||
public static final int NOTIFICATION_ID=178;
|
||||
private static final String ACTION_KEY_TEXT_REPLY = "ACTION_KEY_TEXT_REPLY";
|
||||
|
||||
private static final int SUMMARY_ID = 791;
|
||||
private static int notificationId = 0;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent){
|
||||
UiUtils.setUserPreferredTheme(context);
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.e(TAG, "received: "+intent);
|
||||
Bundle extras=intent.getExtras();
|
||||
@@ -71,6 +86,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
}
|
||||
String accountID=account.getID();
|
||||
PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s);
|
||||
E.post(new NotificationReceivedEvent(accountID, pn.notificationId+""));
|
||||
new GetNotificationByID(pn.notificationId+"")
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
@@ -92,6 +108,35 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
Log.w(TAG, "onReceive: invalid push notification format");
|
||||
}
|
||||
}
|
||||
if(intent.getBooleanExtra("fromNotificationAction", false)){
|
||||
String accountID=intent.getStringExtra("accountID");
|
||||
int notificationId=intent.getIntExtra("notificationId", -1);
|
||||
|
||||
if (notificationId >= 0){
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(accountID, notificationId);
|
||||
}
|
||||
|
||||
if(intent.hasExtra("notification")){
|
||||
org.joinmastodon.android.model.Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||
String statusID=notification.status.id;
|
||||
if (statusID != null) {
|
||||
AccountSessionManager accountSessionManager = AccountSessionManager.getInstance();
|
||||
Preferences preferences = accountSessionManager.getAccount(accountID).preferences;
|
||||
|
||||
switch (NotificationAction.values()[intent.getIntExtra("notificationAction", 0)]) {
|
||||
case FAVORITE -> new SetStatusFavorited(statusID, true).exec(accountID);
|
||||
case BOOKMARK -> new SetStatusBookmarked(statusID, true).exec(accountID);
|
||||
case REBLOG -> new SetStatusReblogged(notification.status.id, true, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case UNDO_REBLOG -> new SetStatusReblogged(notification.status.id, false, preferences.postingDefaultVisibility).exec(accountID);
|
||||
case REPLY -> handleReplyAction(context, accountID, intent, notification, notificationId, preferences);
|
||||
default -> Log.w(TAG, "onReceive: Failed to get NotificationAction");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Log.e(TAG, "onReceive: Failed to load notification");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||
@@ -99,7 +144,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||
Notification.Builder builder;
|
||||
Notification.Builder summaryNotification;
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
||||
boolean hasGroup=false;
|
||||
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
|
||||
@@ -122,28 +166,21 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
nm.createNotificationChannels(channels);
|
||||
}
|
||||
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
|
||||
// summaryNotification=new Notification.Builder(context, accountID);
|
||||
}else{
|
||||
builder=new Notification.Builder(context)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||
summaryNotification=new Notification.Builder(context)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||
}
|
||||
Drawable avatar=ImageCache.getInstance(context).get(new UrlImageLoaderRequest(pn.icon, V.dp(50), V.dp(50)));
|
||||
Intent contentIntent=new Intent(context, MainActivity.class);
|
||||
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
contentIntent.putExtra("fromNotification", true);
|
||||
contentIntent.putExtra("accountID", accountID);
|
||||
contentIntent.putExtra("notificationID", notificationId);
|
||||
if(notification!=null){
|
||||
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
||||
}
|
||||
|
||||
builder.setContentTitle(pn.title)
|
||||
.setContentText(pn.body)
|
||||
.setContentTitle(pn.title)
|
||||
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
||||
.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
@@ -151,8 +188,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
.setShowWhen(true)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setAutoCancel(true)
|
||||
.setColor(context.getColor(R.color.shortcut_icon_background))
|
||||
.setGroup(accountID);
|
||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
||||
|
||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||
builder.setSmallIcon(switch (pn.notificationType) {
|
||||
@@ -175,8 +211,107 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
builder.setSubText(accountName);
|
||||
}
|
||||
|
||||
notificationId++;
|
||||
nm.notify(accountID, GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId, builder.build());
|
||||
int id = GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId++;
|
||||
|
||||
if (notification != null){
|
||||
switch (pn.notificationType){
|
||||
case MENTION, STATUS -> {
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
|
||||
builder.addAction(buildReplyAction(context, id, accountID, notification));
|
||||
}
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.button_favorite), NotificationAction.FAVORITE));
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.add_bookmark), NotificationAction.BOOKMARK));
|
||||
if(notification.status.visibility != StatusPrivacy.DIRECT) {
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.button_reblog), NotificationAction.REBLOG));
|
||||
}
|
||||
}
|
||||
case UPDATE -> {
|
||||
if(notification.status.reblogged)
|
||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNDO_REBLOG));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm.notify(accountID, id, builder.build());
|
||||
}
|
||||
|
||||
private Notification.Action buildNotificationAction(Context context, int notificationId, String accountID, org.joinmastodon.android.model.Notification notification, String title, NotificationAction action){
|
||||
Intent notificationIntent=new Intent(context, PushNotificationReceiver.class);
|
||||
notificationIntent.putExtra("notificationId", notificationId);
|
||||
notificationIntent.putExtra("fromNotificationAction", true);
|
||||
notificationIntent.putExtra("accountID", accountID);
|
||||
notificationIntent.putExtra("notificationAction", action.ordinal());
|
||||
notificationIntent.putExtra("notification", Parcels.wrap(notification));
|
||||
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, new Random().nextInt(), notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
|
||||
|
||||
return new Notification.Action.Builder(null, title, actionPendingIntent).build();
|
||||
}
|
||||
|
||||
private Notification.Action buildReplyAction(Context context, int notificationId, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||
String replyLabel = context.getResources().getString(R.string.button_reply);
|
||||
RemoteInput remoteInput = new RemoteInput.Builder(ACTION_KEY_TEXT_REPLY)
|
||||
.setLabel(replyLabel)
|
||||
.build();
|
||||
|
||||
Intent notificationIntent=new Intent(context, PushNotificationReceiver.class);
|
||||
notificationIntent.putExtra("notificationId", notificationId);
|
||||
notificationIntent.putExtra("fromNotificationAction", true);
|
||||
notificationIntent.putExtra("accountID", accountID);
|
||||
notificationIntent.putExtra("notificationAction", NotificationAction.REPLY.ordinal());
|
||||
notificationIntent.putExtra("notification", Parcels.wrap(notification));
|
||||
|
||||
int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT : PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context, new Random().nextInt(), notificationIntent,flags);
|
||||
return new Notification.Action.Builder(null, replyLabel, replyPendingIntent).addRemoteInput(remoteInput).build();
|
||||
}
|
||||
|
||||
private void handleReplyAction(Context context, String accountID, Intent intent, org.joinmastodon.android.model.Notification notification, int notificationId, Preferences preferences) {
|
||||
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
||||
if (remoteInput == null) {
|
||||
Log.e(TAG, "handleReplyAction: Could not get reply input");
|
||||
return;
|
||||
}
|
||||
CharSequence input = remoteInput.getCharSequence(ACTION_KEY_TEXT_REPLY);
|
||||
|
||||
CreateStatus.Request req=new CreateStatus.Request();
|
||||
req.status = input.toString();
|
||||
req.language = preferences.postingDefaultLanguage;
|
||||
req.visibility = preferences.postingDefaultVisibility;
|
||||
req.inReplyToId = notification.status.id;
|
||||
if(!notification.status.spoilerText.isEmpty() && GlobalUserPreferences.prefixRepliesWithRe && !notification.status.spoilerText.startsWith("re: ")){
|
||||
req.spoilerText = "re: " + notification.status.spoilerText;
|
||||
}
|
||||
|
||||
new CreateStatus(req, UUID.randomUUID().toString()).setCallback(new Callback<Status>() {
|
||||
@Override
|
||||
public void onSuccess(Status status) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
Notification.Builder builder = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
|
||||
new Notification.Builder(context, accountID+"_"+notification.type) :
|
||||
new Notification.Builder(context)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
||||
|
||||
notification.status = status;
|
||||
Intent contentIntent=new Intent(context, MainActivity.class);
|
||||
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
contentIntent.putExtra("fromNotification", true);
|
||||
contentIntent.putExtra("accountID", accountID);
|
||||
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
||||
|
||||
Notification repliedNotification = builder.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||
.setContentTitle(context.getString(R.string.sk_notification_action_replied, notification.status.account.displayName))
|
||||
.setContentText(status.getStrippedText())
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.build();
|
||||
notificationManager.notify(accountID, notificationId, repliedNotification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse errorResponse) {
|
||||
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class ApiUtils{
|
||||
//no instance
|
||||
}
|
||||
|
||||
public static <E extends Enum<E>> List<String> enumSetToStrings(EnumSet<E> e, Class<E> cls){
|
||||
public static <E extends Enum<E>> List<String> enumSetToStrings(EnumSet<E> e, Class<E> cls){
|
||||
return e.stream().map(ev->{
|
||||
try{
|
||||
SerializedName annotation=cls.getField(ev.name()).getAnnotation(SerializedName.class);
|
||||
|
||||
@@ -13,9 +13,11 @@ import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.model.SearchResult;
|
||||
@@ -72,10 +74,8 @@ public class CacheController{
|
||||
int flags=cursor.getInt(1);
|
||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||
newMaxID=status.id;
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(status))
|
||||
continue outer;
|
||||
}
|
||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.HOME).test(status))
|
||||
continue outer;
|
||||
result.add(status);
|
||||
}while(cursor.moveToNext());
|
||||
String _newMaxID=newMaxID;
|
||||
@@ -90,7 +90,7 @@ public class CacheController{
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters, Filter.FilterContext.HOME)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
putHomeTimeline(result, maxID==null);
|
||||
}
|
||||
|
||||
@@ -126,11 +126,12 @@ public class CacheController{
|
||||
});
|
||||
}
|
||||
|
||||
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<PaginatedResponse<List<Notification>>> callback){
|
||||
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<CacheablePaginatedResponse<List<Notification>>> callback){
|
||||
cancelDelayedClose();
|
||||
databaseThread.postRunnable(()->{
|
||||
try{
|
||||
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
||||
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
List<Filter> filters=accountSession.wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
||||
if(!forceReload){
|
||||
SQLiteDatabase db=getOrOpenDatabase();
|
||||
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
|
||||
@@ -145,35 +146,30 @@ public class CacheController{
|
||||
ntf.postprocess();
|
||||
newMaxID=ntf.id;
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status))
|
||||
continue outer;
|
||||
}
|
||||
result.add(ntf);
|
||||
}while(cursor.moveToNext());
|
||||
String _newMaxID=newMaxID;
|
||||
uiHandler.post(()->callback.onSuccess(new PaginatedResponse<>(result, _newMaxID)));
|
||||
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
||||
return;
|
||||
}
|
||||
}catch(IOException x){
|
||||
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
|
||||
}
|
||||
}
|
||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class))
|
||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(accountSession.domain);
|
||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.pleroma != null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<Notification> result){
|
||||
callback.onSuccess(new PaginatedResponse<>(result.stream().filter(ntf->{
|
||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(ntf->{
|
||||
if(ntf.status!=null){
|
||||
for(Filter filter:filters){
|
||||
if(filter.matches(ntf.status)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status);
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id));
|
||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
||||
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@@ -27,6 +28,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -40,14 +42,19 @@ import okhttp3.ResponseBody;
|
||||
|
||||
public class MastodonAPIController{
|
||||
private static final String TAG="MastodonAPIController";
|
||||
public static final Gson gson=new GsonBuilder()
|
||||
public static final Gson gsonWithoutDeserializer = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
|
||||
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
|
||||
.create();
|
||||
public static final Gson gson = gsonWithoutDeserializer.newBuilder()
|
||||
.registerTypeAdapter(Status.class, new Status.StatusDeserializer())
|
||||
.create();
|
||||
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
||||
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
|
||||
private static OkHttpClient httpClient=new OkHttpClient.Builder()
|
||||
.readTimeout(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private AccountSession session;
|
||||
private static List<String> badDomains = new ArrayList<>();
|
||||
@@ -56,7 +63,7 @@ public class MastodonAPIController{
|
||||
thread.start();
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
MastodonApp.context.getAssets().open("blocks.tsv")
|
||||
MastodonApp.context.getAssets().open("blocks.txt")
|
||||
));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
@@ -87,7 +94,7 @@ public class MastodonAPIController{
|
||||
Request.Builder builder=new Request.Builder()
|
||||
.url(req.getURL().toString())
|
||||
.method(req.getMethod(), req.getRequestBody())
|
||||
.header("User-Agent", "MastodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||
.header("User-Agent", "MegalodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||
|
||||
String token=null;
|
||||
if(session!=null)
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
|
||||
public class MastodonErrorResponse extends ErrorResponse{
|
||||
@@ -22,7 +20,7 @@ public class MastodonErrorResponse extends ErrorResponse{
|
||||
|
||||
@Override
|
||||
public void bindErrorView(View view){
|
||||
TextView text=view.findViewById(R.id.error_text);
|
||||
TextView text=view.findViewById(me.grishka.appkit.R.id.error_text);
|
||||
text.setText(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.joinmastodon.android.api.requests.accounts;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
|
||||
public class SetPrivateNote extends MastodonAPIRequest<Relationship>{
|
||||
public SetPrivateNote(String id, String comment){
|
||||
super(MastodonAPIRequest.HttpMethod.POST, "/accounts/"+id+"/note", Relationship.class);
|
||||
Request req = new Request(comment);
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public String comment;
|
||||
public Request(String comment){
|
||||
this.comment=comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class AddList extends MastodonAPIRequest<Object> {
|
||||
public AddList(String listName){
|
||||
super(HttpMethod.POST, "/lists", Object.class);
|
||||
Request req = new Request();
|
||||
req.title = listName;
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
public static class Request{
|
||||
public String title;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class EditListName extends MastodonAPIRequest<Object> {
|
||||
public EditListName(String newListName, String listId){
|
||||
super(HttpMethod.PUT, "/lists/"+listId, Object.class);
|
||||
Request req = new Request();
|
||||
req.title = newListName;
|
||||
setRequestBody(req);
|
||||
}
|
||||
|
||||
public static class Request{
|
||||
public String title;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.joinmastodon.android.api.requests.lists;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import java.util.List;
|
||||
|
||||
public class RemoveList extends MastodonAPIRequest<Object> {
|
||||
public RemoveList(String listId){
|
||||
super(HttpMethod.DELETE, "/lists/"+listId, Object.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.joinmastodon.android.api.requests.markers;
|
||||
|
||||
import org.joinmastodon.android.api.ApiUtils;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Marker;
|
||||
import org.joinmastodon.android.model.Markers;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class GetMarkers extends MastodonAPIRequest<Markers> {
|
||||
public GetMarkers(EnumSet<Marker.Type> timelines) {
|
||||
super(HttpMethod.GET, "/markers", Markers.class);
|
||||
for (String type : ApiUtils.enumSetToStrings(timelines, Marker.Type.class)){
|
||||
addQueryParameter("timeline[]", type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class DismissNotification extends MastodonAPIRequest<Object>{
|
||||
public DismissNotification(String id){
|
||||
super(HttpMethod.POST, "/notifications/" + (id != null ? id + "/dismiss" : "clear"), Object.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
public DismissNotification(String id){
|
||||
super(HttpMethod.POST, "/notifications/" + (id != null ? id + "/dismiss" : "clear"), Object.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.joinmastodon.android.api.requests.notifications;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.api.ApiUtils;
|
||||
@@ -11,18 +10,24 @@ import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
|
||||
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes){
|
||||
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes, boolean isPleromaInstance){
|
||||
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", ""+limit);
|
||||
if(includeTypes!=null){
|
||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
||||
addQueryParameter("types[]", type);
|
||||
}
|
||||
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
||||
addQueryParameter("exclude_types[]", type);
|
||||
if(!isPleromaInstance) {
|
||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
||||
addQueryParameter("types[]", type);
|
||||
}
|
||||
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
||||
addQueryParameter("exclude_types[]", type);
|
||||
}
|
||||
}else{
|
||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
||||
addQueryParameter("include_types[]", type);
|
||||
}
|
||||
}
|
||||
}
|
||||
removeUnsupportedItems=true;
|
||||
|
||||
@@ -11,9 +11,9 @@ public class CreateOAuthApp extends MastodonAPIRequest<Application>{
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public String clientName="Moshidon";
|
||||
public String clientName="Megalodon";
|
||||
public String redirectUris=AccountSessionManager.REDIRECT_URI;
|
||||
public String scopes=AccountSessionManager.SCOPE;
|
||||
public String website="https://github.com/LucasGGamerM/moshidon";
|
||||
public String website="https://sk22.github.io/megalodon";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.joinmastodon.android.api.requests.statuses;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
import org.joinmastodon.android.model.ScheduledStatus;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
@@ -45,6 +46,9 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
||||
public Instant scheduledAt;
|
||||
public String language;
|
||||
|
||||
public String quoteId;
|
||||
public ContentType contentType;
|
||||
|
||||
public static class Poll{
|
||||
public ArrayList<String> options=new ArrayList<>();
|
||||
public int expiresIn;
|
||||
|
||||
@@ -2,17 +2,22 @@ package org.joinmastodon.android.api.requests.statuses;
|
||||
|
||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.model.BaseModel;
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
|
||||
public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.Response>{
|
||||
public GetStatusSourceText(String id){
|
||||
super(HttpMethod.GET, "/statuses/"+id+"/source", Response.class);
|
||||
}
|
||||
|
||||
@AllFieldsAreRequired
|
||||
public static class Response extends BaseModel{
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String text;
|
||||
@RequiredField
|
||||
public String spoilerText;
|
||||
public ContentType contentType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.joinmastodon.android.api.requests.timelines;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
@@ -18,5 +19,7 @@ public class GetHomeTimeline extends MastodonAPIRequest<List<Status>>{
|
||||
addQueryParameter("since_id", sinceID);
|
||||
if(limit>0)
|
||||
addQueryParameter("limit", ""+limit);
|
||||
if(GlobalUserPreferences.replyVisibility != null)
|
||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Application;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Markers;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.PushSubscription;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
@@ -31,6 +32,7 @@ public class AccountSession{
|
||||
public String pushAccountID;
|
||||
public Preferences preferences;
|
||||
public AccountActivationInfo activationInfo;
|
||||
public Markers markers;
|
||||
private transient MastodonAPIController apiController;
|
||||
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
|
||||
private transient CacheController cacheController;
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.requests.markers.GetMarkers;
|
||||
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
|
||||
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -33,6 +34,8 @@ import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.EmojiCategory;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Marker;
|
||||
import org.joinmastodon.android.model.Markers;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
|
||||
@@ -46,6 +49,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -61,7 +65,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
||||
public class AccountSessionManager{
|
||||
private static final String TAG="AccountSessionManager";
|
||||
public static final String SCOPE="read write follow push";
|
||||
public static final String REDIRECT_URI="moshidon-android-auth://callback";
|
||||
public static final String REDIRECT_URI="megalodon-android-auth://callback";
|
||||
|
||||
private static final AccountSessionManager instance=new AccountSessionManager();
|
||||
|
||||
@@ -211,7 +215,7 @@ public class AccountSessionManager{
|
||||
.path("/oauth/authorize")
|
||||
.appendQueryParameter("response_type", "code")
|
||||
.appendQueryParameter("client_id", result.clientId)
|
||||
.appendQueryParameter("redirect_uri", "moshidon-android-auth://callback")
|
||||
.appendQueryParameter("redirect_uri", "megalodon-android-auth://callback")
|
||||
.appendQueryParameter("scope", SCOPE)
|
||||
.build();
|
||||
|
||||
@@ -255,6 +259,7 @@ public class AccountSessionManager{
|
||||
// if(now-session.filtersLastUpdated>3600_000L){
|
||||
updateSessionWordFilters(session);
|
||||
// }
|
||||
updateSessionMarkers(session);
|
||||
}
|
||||
if(loadedInstances){
|
||||
maybeUpdateCustomEmojis(domains);
|
||||
@@ -271,6 +276,15 @@ public class AccountSessionManager{
|
||||
}
|
||||
}
|
||||
|
||||
private void preferencesFromSource(AccountSession session, Account account) {
|
||||
if (account != null && account.source != null && session.preferences != null) {
|
||||
if (account.source.privacy != null)
|
||||
session.preferences.postingDefaultVisibility = account.source.privacy;
|
||||
if (account.source.language != null)
|
||||
session.preferences.postingDefaultLanguage = account.source.language;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSessionLocalInfo(AccountSession session){
|
||||
new GetOwnAccount()
|
||||
.setCallback(new Callback<>(){
|
||||
@@ -278,13 +292,12 @@ public class AccountSessionManager{
|
||||
public void onSuccess(Account result){
|
||||
session.self=result;
|
||||
session.infoLastUpdated=System.currentTimeMillis();
|
||||
preferencesFromSource(session, result);
|
||||
writeAccountsFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
|
||||
}
|
||||
public void onError(ErrorResponse error){}
|
||||
})
|
||||
.exec(session.getID());
|
||||
}
|
||||
@@ -294,10 +307,14 @@ public class AccountSessionManager{
|
||||
@Override
|
||||
public void onSuccess(Preferences preferences) {
|
||||
session.preferences=preferences;
|
||||
preferencesFromSource(session, session.self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {}
|
||||
public void onError(ErrorResponse error) {
|
||||
session.preferences = new Preferences();
|
||||
preferencesFromSource(session, session.self);
|
||||
}
|
||||
}).exec(session.getID());
|
||||
}
|
||||
|
||||
@@ -319,6 +336,21 @@ public class AccountSessionManager{
|
||||
.exec(session.getID());
|
||||
}
|
||||
|
||||
private void updateSessionMarkers(AccountSession session) {
|
||||
new GetMarkers(EnumSet.allOf(Marker.Type.class)).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Markers markers) {
|
||||
session.markers = markers;
|
||||
writeAccountsFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
|
||||
}
|
||||
}).exec(session.getID());
|
||||
}
|
||||
|
||||
public void updateInstanceInfo(String domain){
|
||||
new GetInstance()
|
||||
.setCallback(new Callback<>(){
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.joinmastodon.android.events;
|
||||
|
||||
public class AllNotificationsSeenEvent {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.joinmastodon.android.events;
|
||||
|
||||
public class NotificationReceivedEvent {
|
||||
public String account, id;
|
||||
public NotificationReceivedEvent(String account, String id) {
|
||||
this.account = account;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,6 @@ package org.joinmastodon.android.fragments;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||
@@ -64,7 +60,12 @@ public class AccountTimelineFragment extends StatusListFragment{
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
if(getActivity()==null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.ACCOUNT)).collect(Collectors.toList());
|
||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
||||
result=result.stream().filter(status -> {
|
||||
// don't hide own posts in own profile
|
||||
if (asm.isSelf(accountID, user) && asm.isSelf(accountID, status.account)) return true;
|
||||
else return new StatusFilterPredicate(accountID, getFilterContext()).test(status);
|
||||
}).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -74,7 +75,6 @@ public class AccountTimelineFragment extends StatusListFragment{
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab = ((ProfileFragment) getParentFragment()).getFab();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,4 +123,10 @@ public class AccountTimelineFragment extends StatusListFragment{
|
||||
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,11 @@ import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
@@ -34,13 +32,10 @@ import org.joinmastodon.android.model.Poll;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||
import org.joinmastodon.android.ui.TileGridLayoutManager;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
@@ -48,8 +43,9 @@ import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||
import org.joinmastodon.android.utils.TypedObjectPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -60,13 +56,11 @@ import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -74,7 +68,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop{
|
||||
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends RecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop, HasFab{
|
||||
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
||||
protected DisplayItemsAdapter adapter;
|
||||
protected String accountID;
|
||||
@@ -84,21 +78,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||
protected Rect tmpRect=new Rect();
|
||||
|
||||
private final int THRESHHOLD = 800;
|
||||
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
|
||||
|
||||
public BaseStatusListFragment(){
|
||||
super(20);
|
||||
if (withComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||
if (wantsComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||
}
|
||||
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
UiUtils.loadMaxWidth(getContext());
|
||||
if(GlobalUserPreferences.disableMarquee){
|
||||
setTitleMarqueeEnabled(false);
|
||||
setSubtitleMarqueeEnabled(false);
|
||||
@@ -107,8 +101,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
setRetainInstance(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
return adapter=new DisplayItemsAdapter();
|
||||
@@ -195,21 +187,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex){
|
||||
final Status status=_status.reblog!=null ? _status.reblog : _status;
|
||||
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){
|
||||
final Status status=_status.getContentStatus();
|
||||
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
|
||||
private ImageStatusDisplayItem.Holder<?> transitioningHolder;
|
||||
private MediaAttachmentViewController transitioningHolder;
|
||||
|
||||
@Override
|
||||
public void setPhotoViewVisibility(int index, boolean visible){
|
||||
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
||||
if(holder!=null)
|
||||
holder.photo.setAlpha(visible ? 1f : 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
||||
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
||||
if(holder!=null){
|
||||
transitioningHolder=holder;
|
||||
View view=transitioningHolder.photo;
|
||||
@@ -217,7 +209,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
view.getLocationOnScreen(pos);
|
||||
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
|
||||
list.setClipChildren(false);
|
||||
transitioningHolder.itemView.setElevation(1f);
|
||||
gridHolder.setClipChildren(false);
|
||||
transitioningHolder.view.setElevation(1f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -244,15 +237,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
view.setTranslationY(0f);
|
||||
view.setScaleX(1f);
|
||||
view.setScaleY(1f);
|
||||
transitioningHolder.itemView.setElevation(0f);
|
||||
transitioningHolder.view.setElevation(0f);
|
||||
if(list!=null)
|
||||
list.setClipChildren(true);
|
||||
gridHolder.setClipChildren(true);
|
||||
transitioningHolder=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getPhotoViewCurrentDrawable(int index){
|
||||
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
||||
if(holder!=null)
|
||||
return holder.photo.getDrawable();
|
||||
return null;
|
||||
@@ -268,27 +262,47 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
||||
}
|
||||
|
||||
private ImageStatusDisplayItem.Holder<?> findPhotoViewHolder(int index){
|
||||
if(list==null)
|
||||
return null;
|
||||
int offset=0;
|
||||
for(StatusDisplayItem item:displayItems){
|
||||
if(item.parentID.equals(parentID)){
|
||||
if(item instanceof ImageStatusDisplayItem){
|
||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(getMainAdapterOffset()+offset+index);
|
||||
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||
return imgHolder;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
return null;
|
||||
private MediaAttachmentViewController findPhotoViewHolder(int index){
|
||||
return gridHolder.getViewController(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable View getFab() {
|
||||
if (getParentFragment() instanceof HasFab l) return l.getFab();
|
||||
else return fab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFab() {
|
||||
View fab = getFab();
|
||||
if (fab == null || fab.getVisibility() == View.VISIBLE) return;
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
animate.setDuration(300);
|
||||
fab.startAnimation(animate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideFab() {
|
||||
View fab = getFab();
|
||||
if (fab == null || fab.getVisibility() != View.VISIBLE) return;
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
fab.startAnimation(animate);
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
scrollDiff = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -300,45 +314,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
if(currentPhotoViewer!=null)
|
||||
currentPhotoViewer.offsetView(-dx, -dy);
|
||||
|
||||
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
||||
// This piece of code should make it so that the fab is always visible if the status list scroll view is at the item at the top
|
||||
if(list.getChildLayoutPosition(list.getChildAt(0)) < 1){
|
||||
scrollDiff=THRESHHOLD+1;
|
||||
}else{
|
||||
if(dy > 0){
|
||||
scrollDiff=0;
|
||||
}
|
||||
}
|
||||
|
||||
View fab = getFab();
|
||||
if (fab!=null && GlobalUserPreferences.autoHideFab) {
|
||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
scrollDiff = 0;
|
||||
hideFab();
|
||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||
if (scrollDiff > THRESHHOLD) {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
if (list.getChildAt(0).getTop() == 0 || scrollDiff > 400) {
|
||||
showFab();
|
||||
scrollDiff = 0;
|
||||
} else {
|
||||
scrollDiff += Math.abs(dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}});
|
||||
}
|
||||
});
|
||||
list.addItemDecoration(new StatusListItemDecoration());
|
||||
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
||||
private Rect tmpRect=new Rect();
|
||||
@@ -373,39 +363,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
||||
updateToolbar();
|
||||
|
||||
if (withComposeButton()) {
|
||||
fab = view.findViewById(R.id.fab);
|
||||
if (wantsComposeButton() && !getArguments().getBoolean("__disable_fab", false)) {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
fab.setOnLongClickListener(this::onFabLongClick);
|
||||
} else if (fab != null) {
|
||||
fab.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.LayoutManager onCreateLayoutManager(){
|
||||
GridLayoutManager lm=new TileGridLayoutManager(getActivity(), 1000);
|
||||
lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
|
||||
@Override
|
||||
public int getSpanSize(int position){
|
||||
position-=getMainAdapterOffset();
|
||||
if(position>=0 && position<displayItems.size()){
|
||||
StatusDisplayItem item=displayItems.get(position);
|
||||
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||
PhotoLayoutHelper.TiledLayoutResult layout=imgItem.tiledLayout;
|
||||
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgItem.thisTile;
|
||||
int spans=0;
|
||||
for(int i=0;i<tile.colSpan;i++){
|
||||
spans+=layout.columnSizes[tile.startCol+i];
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
});
|
||||
return lm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig){
|
||||
super.onConfigurationChanged(newConfig);
|
||||
@@ -524,7 +490,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
revealSpoiler(status, holder.getItemID());
|
||||
}
|
||||
|
||||
public void onRevealSpoilerClick(ImageStatusDisplayItem.Holder<?> holder){
|
||||
public void onRevealSpoilerClick(MediaGridStatusDisplayItem.Holder holder){
|
||||
Status status=holder.getItem().status;
|
||||
revealSpoiler(status, holder.getItemID());
|
||||
}
|
||||
@@ -558,7 +524,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
holder.getItem().status.textExpandable = expandable;
|
||||
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
||||
if (header != null) header.rebind();
|
||||
holder.rebind();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,13 +537,14 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
|
||||
protected void updateImagesSpoilerState(Status status, String itemID){
|
||||
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
||||
for(ImageStatusDisplayItem.Holder photo:(List<ImageStatusDisplayItem.Holder>)findAllHoldersOfType(itemID, ImageStatusDisplayItem.Holder.class)){
|
||||
photo.setRevealed(status.spoilerRevealed);
|
||||
updatedPositions.add(photo.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||
MediaGridStatusDisplayItem.Holder mediaGrid=findHolderOfType(itemID, MediaGridStatusDisplayItem.Holder.class);
|
||||
if(mediaGrid!=null){
|
||||
mediaGrid.setRevealed(status.spoilerRevealed);
|
||||
updatedPositions.add(mediaGrid.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||
}
|
||||
int i=0;
|
||||
for(StatusDisplayItem item:displayItems){
|
||||
if(itemID.equals(item.parentID) && item instanceof ImageStatusDisplayItem && !updatedPositions.contains(i)){
|
||||
if(itemID.equals(item.parentID) && item instanceof MediaGridStatusDisplayItem && !updatedPositions.contains(i)){
|
||||
adapter.notifyItemChanged(i);
|
||||
}
|
||||
i++;
|
||||
@@ -711,16 +677,25 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
currentPhotoViewer.onPause();
|
||||
}
|
||||
|
||||
protected void onFabClick(View v){
|
||||
public void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}
|
||||
|
||||
protected boolean onFabLongClick(View v) {
|
||||
public boolean onFabLongClick(View v) {
|
||||
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
||||
}
|
||||
|
||||
private MediaAttachmentViewController makeNewMediaAttachmentView(MediaGridStatusDisplayItem.GridItemType type){
|
||||
return new MediaAttachmentViewController(getActivity(), type);
|
||||
}
|
||||
|
||||
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> getAttachmentViewsPool(){
|
||||
return attachmentViewsPool;
|
||||
}
|
||||
|
||||
|
||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||
|
||||
public DisplayItemsAdapter(){
|
||||
@@ -758,16 +733,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||
return displayItems.get(position).getImageRequest(image);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onViewDetachedFromWindow(@NonNull BindableViewHolder<StatusDisplayItem> holder){
|
||||
// if(holder instanceof ImageLoaderViewHolder){
|
||||
// int count=holder.getItem().getImageCount();
|
||||
// for(int i=0;i<count;i++){
|
||||
// ((ImageLoaderViewHolder) holder).clearImage(i);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private class StatusListItemDecoration extends RecyclerView.ItemDecoration{
|
||||
@@ -777,7 +742,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
private int currentMediaHiddenLayoutsWidth=0;
|
||||
|
||||
{
|
||||
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), GlobalUserPreferences.disableDividers ? R.attr.colorWindowBackground : R.attr.colorPollVoted));
|
||||
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorPollVoted));
|
||||
dividerPaint.setStyle(Paint.Style.STROKE);
|
||||
dividerPaint.setStrokeWidth(V.dp(1));
|
||||
}
|
||||
@@ -801,25 +766,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
||||
if(!imgHolder.getItem().status.spoilerRevealed && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||
hiddenMediaPaint.setColor(0x80000000);
|
||||
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgHolder.getItem().thisTile;
|
||||
float hGap=tile.startCol>0 ? V.dp(1) : 0;
|
||||
float vGap=tile.startRow>0 ? V.dp(1) : 0;
|
||||
c.drawRect(child.getX()-hGap, child.getY()-vGap, child.getX()+child.getWidth(), child.getY()+child.getHeight(), hiddenMediaPaint);
|
||||
c.drawRect(child.getX(), child.getY(), child.getX()+child.getWidth(), child.getY()+child.getHeight(), hiddenMediaPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i=0;i<parent.getChildCount();i++){
|
||||
View child=parent.getChildAt(i);
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
||||
if(!imgHolder.getItem().status.spoilerRevealed){
|
||||
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgHolder.getItem().thisTile;
|
||||
if(tile.startCol==0 && tile.startRow==0 && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||
if(TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||
int listWidth=getListWidthForMediaLayout();
|
||||
int width=Math.min(listWidth, V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||
int width=Math.min(listWidth, UiUtils.MAX_WIDTH);
|
||||
if(currentMediaHiddenLayoutsWidth!=width)
|
||||
rebuildMediaHiddenLayouts(width-V.dp(32));
|
||||
c.save();
|
||||
@@ -844,47 +805,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
|
||||
if(holder instanceof ImageStatusDisplayItem.Holder){
|
||||
int listWidth=getListWidthForMediaLayout();
|
||||
int width=Math.min(listWidth, V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||
PhotoLayoutHelper.TiledLayoutResult layout=((ImageStatusDisplayItem.Holder<?>) holder).getItem().tiledLayout;
|
||||
PhotoLayoutHelper.TiledLayoutResult.Tile tile=((ImageStatusDisplayItem.Holder<?>) holder).getItem().thisTile;
|
||||
if(tile.startCol+tile.colSpan<layout.columnSizes.length){
|
||||
outRect.right=V.dp(1);
|
||||
}
|
||||
if(tile.startRow+tile.rowSpan<layout.rowSizes.length){
|
||||
outRect.bottom=V.dp(1);
|
||||
}
|
||||
|
||||
// For a view that spans rows, compensate its additional height so the row it's in stays the right height
|
||||
if(tile.rowSpan>1){
|
||||
outRect.bottom=-(Math.round(tile.height/1000f*width)-Math.round(layout.rowSizes[tile.startRow]/1000f*width));
|
||||
}
|
||||
// ...and for its siblings, offset those on rows below first to the right where they belong
|
||||
if(tile.startCol>0 && layout.tiles[0].rowSpan>1 && tile.startRow>layout.tiles[0].startRow){
|
||||
int xOffset=Math.round(layout.tiles[0].width/1000f*listWidth);
|
||||
outRect.left=xOffset;
|
||||
outRect.right=-xOffset;
|
||||
}
|
||||
|
||||
// If the width of the media block is smaller than that of the RecyclerView, offset the views horizontally to center them
|
||||
if(listWidth>width){
|
||||
outRect.left+=(listWidth-V.dp(ImageAttachmentFrameLayout.MAX_WIDTH))/2;
|
||||
if(tile.startCol>0){
|
||||
int spanOffset=0;
|
||||
for(int i=0;i<tile.startCol;i++){
|
||||
spanOffset+=layout.columnSizes[i];
|
||||
}
|
||||
outRect.left-=Math.round(spanOffset/1000f*listWidth);
|
||||
outRect.left+=Math.round(spanOffset/1000f*width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuildMediaHiddenLayouts(int width){
|
||||
currentMediaHiddenLayoutsWidth=width;
|
||||
String title=getString(R.string.sensitive_content);
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetBookmarkedStatuses;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
@@ -35,4 +36,9 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import static android.os.ext.SdkExtensions.getExtensionVersion;
|
||||
|
||||
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
|
||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
||||
@@ -93,6 +91,7 @@ import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Attachment;
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.EmojiCategory;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
@@ -111,7 +110,7 @@ import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
||||
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||
import org.joinmastodon.android.ui.utils.TransferSpeedTracker;
|
||||
import org.joinmastodon.android.utils.TransferSpeedTracker;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.ComposeEditText;
|
||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||
@@ -181,8 +180,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private int charCount, charLimit, trimmedCharCount;
|
||||
|
||||
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
|
||||
private PopupMenu languagePopup, visibilityPopup, draftOptionsPopup;
|
||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss;
|
||||
private PopupMenu languagePopup, contentTypePopup, visibilityPopup, draftOptionsPopup;
|
||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss, contentTypeBtn;
|
||||
private ImageView sensitiveIcon;
|
||||
private ComposeMediaLayout attachmentsView;
|
||||
private TextView replyText;
|
||||
@@ -206,6 +205,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private List<EmojiCategory> customEmojis;
|
||||
private CustomEmojiPopupKeyboard emojiKeyboard;
|
||||
private Status replyTo;
|
||||
private Status quote;
|
||||
private String initialText;
|
||||
private String uuid;
|
||||
private int pollDuration=24*3600;
|
||||
@@ -235,18 +235,21 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private Runnable updateUploadEtaRunnable;
|
||||
|
||||
private String language, encoding;
|
||||
private ContentType contentType;
|
||||
private MastodonLanguage.LanguageResolver languageResolver;
|
||||
|
||||
private int navigationBarColorBefore;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
navigationBarColorBefore = getActivity().getWindow().getNavigationBarColor();
|
||||
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
|
||||
|
||||
accountID=getArguments().getString("account");
|
||||
contentType = GlobalUserPreferences.accountsDefaultContentTypes.get(accountID);
|
||||
if (contentType == null && GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID)) {
|
||||
// if formatting is enabled, use plain to avoid confusing unspecified default setting
|
||||
contentType = ContentType.PLAIN;
|
||||
}
|
||||
|
||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
self=session.self;
|
||||
instanceDomain=session.domain;
|
||||
@@ -258,6 +261,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
||||
if(getArguments().containsKey("replyTo"))
|
||||
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
||||
if(getArguments().containsKey("quote"))
|
||||
quote=Parcels.unwrap(getArguments().getParcelable("quote"));
|
||||
if(instance==null){
|
||||
Nav.finish(this);
|
||||
return;
|
||||
@@ -289,7 +294,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
UiUtils.removeCallbacks(updateUploadEtaRunnable);
|
||||
updateUploadEtaRunnable=null;
|
||||
}
|
||||
getActivity().getWindow().setNavigationBarColor(navigationBarColorBefore);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -307,25 +311,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
emojiKeyboard.setListener(this::onCustomEmojiClick);
|
||||
|
||||
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
||||
|
||||
if(GlobalUserPreferences.relocatePublishButton){
|
||||
publishButton=view.findViewById(R.id.publish);
|
||||
// publishButton.setText(editingStatus==null || redraftStatus ? R.string.publish : R.string.save);
|
||||
publishButton.setEllipsize(TextUtils.TruncateAt.END);
|
||||
publishButton.setOnClickListener(this::onPublishClick);
|
||||
publishButton.setSingleLine(true);
|
||||
publishButton.setVisibility(View.VISIBLE);
|
||||
|
||||
draftsBtn=view.findViewById(R.id.drafts_btn);
|
||||
draftsBtn.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
charCounter=view.findViewById(R.id.char_counter);
|
||||
charCounter.setVisibility(View.VISIBLE);
|
||||
charCounter.setText(String.valueOf(charLimit));
|
||||
}
|
||||
|
||||
mainEditText=view.findViewById(R.id.toot_text);
|
||||
mainEditTextWrap=view.findViewById(R.id.toot_text_wrap);
|
||||
charCounter=view.findViewById(R.id.char_counter);
|
||||
charCounter.setText(String.valueOf(charLimit));
|
||||
scrollView=view.findViewById(R.id.scroll_view);
|
||||
|
||||
selfName=view.findViewById(R.id.self_name);
|
||||
@@ -349,13 +338,16 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
emojiBtn=view.findViewById(R.id.btn_emoji);
|
||||
spoilerBtn=view.findViewById(R.id.btn_spoiler);
|
||||
visibilityBtn=view.findViewById(R.id.btn_visibility);
|
||||
contentTypeBtn=view.findViewById(R.id.btn_content_type);
|
||||
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
|
||||
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
|
||||
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
|
||||
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
|
||||
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
|
||||
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
||||
replyText=view.findViewById(R.id.reply_text);
|
||||
replyText=view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text : R.id.reply_text_below);
|
||||
view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text_below : R.id.reply_text)
|
||||
.setVisibility(View.GONE);
|
||||
|
||||
if (isPhotoPickerAvailable()) {
|
||||
PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn);
|
||||
@@ -381,6 +373,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||
|
||||
buildContentTypePopup(contentTypeBtn);
|
||||
contentTypeBtn.setOnClickListener(v->contentTypePopup.show());
|
||||
contentTypeBtn.setOnTouchListener(contentTypePopup.getDragToOpenListener());
|
||||
|
||||
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
|
||||
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
|
||||
|
||||
@@ -483,8 +479,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
}
|
||||
|
||||
if(editingStatus!=null && editingStatus.visibility!=null) {
|
||||
statusVisibility=editingStatus.visibility;
|
||||
if (savedInstanceState != null) {
|
||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||
} else if (editingStatus != null && editingStatus.visibility != null) {
|
||||
statusVisibility = editingStatus.visibility;
|
||||
} else {
|
||||
loadDefaultStatusVisibility(savedInstanceState);
|
||||
}
|
||||
@@ -499,6 +497,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}).setChecked(true);
|
||||
visibilityPopup.getMenu().findItem(R.id.local_only).setChecked(localOnly);
|
||||
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey("contentType")) {
|
||||
contentType = (ContentType) savedInstanceState.getSerializable("contentType");
|
||||
} else if (getArguments().containsKey("sourceContentType")) {
|
||||
try {
|
||||
String val = getArguments().getString("sourceContentType");
|
||||
contentType = val == null ? null : ContentType.valueOf(val);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
|
||||
int contentTypeId = ContentType.getContentTypeRes(contentType);
|
||||
contentTypePopup.getMenu().findItem(contentTypeId).setChecked(true);
|
||||
contentTypeBtn.setSelected(contentTypeId != R.id.content_type_null && contentTypeId != R.id.content_type_plain);
|
||||
|
||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||
View autocompleteView=autocompleteViewController.getView();
|
||||
@@ -535,6 +547,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
outState.putParcelableArrayList("attachments", serializedAttachments);
|
||||
}
|
||||
outState.putSerializable("visibility", statusVisibility);
|
||||
outState.putSerializable("contentType", contentType);
|
||||
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
|
||||
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
|
||||
}
|
||||
@@ -627,7 +640,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
});
|
||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||
if(replyTo!=null){
|
||||
if(replyTo!=null || quote!=null){
|
||||
Status status = quote!=null ? quote : replyTo;
|
||||
View replyWrap = view.findViewById(R.id.reply_wrap);
|
||||
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||
int scrollHeight = scrollView.getHeight();
|
||||
@@ -653,13 +667,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
originalPost.setOnClickListener(v->{
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("status", Parcels.wrap(replyTo));
|
||||
args.putParcelable("status", Parcels.wrap(status));
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||
});
|
||||
|
||||
ImageView avatar = view.findViewById(R.id.avatar);
|
||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(replyTo.account.avatar));
|
||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(status.account.avatar));
|
||||
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline){
|
||||
@@ -671,15 +685,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
avatar.setOnClickListener(v->{
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("profileAccount", Parcels.wrap(replyTo.account));
|
||||
args.putParcelable("profileAccount", Parcels.wrap(status.account));
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||
});
|
||||
|
||||
((TextView) view.findViewById(R.id.name)).setText(replyTo.account.displayName);
|
||||
((TextView) view.findViewById(R.id.username)).setText(replyTo.account.getDisplayUsername());
|
||||
((TextView) view.findViewById(R.id.name)).setText(status.account.displayName);
|
||||
((TextView) view.findViewById(R.id.username)).setText(status.account.getDisplayUsername());
|
||||
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
||||
Drawable visibilityIcon = getActivity().getDrawable(switch(status.visibility){
|
||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
||||
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
||||
@@ -690,40 +704,37 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
moreBtn.setImageDrawable(visibilityIcon);
|
||||
moreBtn.setBackground(null);
|
||||
TextView timestamp = view.findViewById(R.id.timestamp);
|
||||
if (replyTo.editedAt==null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), replyTo.createdAt));
|
||||
else timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), replyTo.editedAt)));
|
||||
if (replyTo.spoilerText != null && !replyTo.spoilerText.isBlank()) {
|
||||
if (status.editedAt!=null) timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt)));
|
||||
else if (status.createdAt!=null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), status.createdAt));
|
||||
else timestamp.setText("");
|
||||
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
|
||||
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
|
||||
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(replyTo.spoilerText);
|
||||
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(status.spoilerText);
|
||||
}
|
||||
|
||||
SpannableStringBuilder content = HtmlParser.parse(replyTo.content, replyTo.emojis, replyTo.mentions, replyTo.tags, accountID);
|
||||
SpannableStringBuilder content = HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, accountID);
|
||||
LinkedTextView text = view.findViewById(R.id.text);
|
||||
if (content.length() > 0) text.setText(content);
|
||||
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
||||
|
||||
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||
int visibilityNameRes = switch (replyTo.visibility) {
|
||||
replyText.setText(getString(quote!=null? R.string.sk_quoting_user : R.string.in_reply_to, status.account.displayName));
|
||||
int visibilityNameRes = switch (status.visibility) {
|
||||
case PUBLIC -> R.string.visibility_public;
|
||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
||||
case PRIVATE -> R.string.visibility_followers_only;
|
||||
case DIRECT -> R.string.visibility_private;
|
||||
case LOCAL -> R.string.sk_local_only;
|
||||
};
|
||||
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
||||
replyText.setContentDescription(getString(R.string.in_reply_to, status.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
||||
replyText.setOnClickListener(v->{
|
||||
scrollView.smoothScrollTo(0, 0);
|
||||
});
|
||||
replyText.setOnClickListener(v->{
|
||||
scrollView.smoothScrollTo(0, 0);
|
||||
});
|
||||
|
||||
|
||||
ArrayList<String> mentions=new ArrayList<>();
|
||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
||||
if(!replyTo.account.id.equals(ownID))
|
||||
mentions.add('@'+replyTo.account.acct);
|
||||
for(Mention mention:replyTo.mentions){
|
||||
if(!status.account.id.equals(ownID))
|
||||
mentions.add('@'+status.account.acct);
|
||||
for(Mention mention:status.mentions){
|
||||
if(mention.id.equals(ownID))
|
||||
continue;
|
||||
String m='@'+mention.acct;
|
||||
@@ -736,17 +747,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
ignoreSelectionChanges=true;
|
||||
mainEditText.setSelection(mainEditText.length());
|
||||
ignoreSelectionChanges=false;
|
||||
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
||||
hasSpoiler=true;
|
||||
spoilerEdit.setVisibility(View.VISIBLE);
|
||||
if(GlobalUserPreferences.prefixRepliesWithRe && !replyTo.spoilerText.startsWith("re: ")){
|
||||
spoilerEdit.setText("re: " + replyTo.spoilerText);
|
||||
if(GlobalUserPreferences.prefixRepliesWithRe && !status.spoilerText.startsWith("re: ")){
|
||||
spoilerEdit.setText("re: " + status.spoilerText);
|
||||
}else{
|
||||
spoilerEdit.setText(replyTo.spoilerText);
|
||||
spoilerEdit.setText(status.spoilerText);
|
||||
}
|
||||
spoilerBtn.setSelected(true);
|
||||
}
|
||||
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||
if (status.language != null && !status.language.isEmpty()) updateLanguage(status.language);
|
||||
}
|
||||
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
||||
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
||||
@@ -813,20 +824,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
item.setActionView(wrap);
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
|
||||
if(!GlobalUserPreferences.relocatePublishButton){
|
||||
publishButton = wrap.findViewById(R.id.publish_btn);
|
||||
publishButton.setOnClickListener(this::onPublishClick);
|
||||
publishButton.setVisibility(View.VISIBLE);
|
||||
|
||||
draftsBtn = wrap.findViewById(R.id.drafts_btn);
|
||||
draftsBtn.setVisibility(View.VISIBLE);
|
||||
}else{
|
||||
charCounter = wrap.findViewById(R.id.char_counter);
|
||||
charCounter.setVisibility(View.VISIBLE);
|
||||
charCounter.setText(String.valueOf(charLimit));
|
||||
}
|
||||
|
||||
// draftsBtn = wrap.findViewById(R.id.drafts_btn);
|
||||
draftsBtn = wrap.findViewById(R.id.drafts_btn);
|
||||
draftOptionsPopup = new PopupMenu(getContext(), draftsBtn);
|
||||
draftOptionsPopup.inflate(R.menu.compose_more);
|
||||
draftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.draft);
|
||||
@@ -843,11 +841,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
});
|
||||
UiUtils.enablePopupMenuIcons(getContext(), draftOptionsPopup);
|
||||
|
||||
|
||||
publishButton = wrap.findViewById(R.id.publish_btn);
|
||||
languageButton = wrap.findViewById(R.id.language_btn);
|
||||
sendProgress = wrap.findViewById(R.id.send_progress);
|
||||
sendError = wrap.findViewById(R.id.send_error);
|
||||
|
||||
publishButton.setOnClickListener(this::onPublishClick);
|
||||
draftsBtn.setOnClickListener(v-> draftOptionsPopup.show());
|
||||
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
||||
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
||||
@@ -938,6 +937,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
});
|
||||
}
|
||||
|
||||
private int getContentTypeName(String id) {
|
||||
return switch (id) {
|
||||
case "text/plain" -> R.string.sk_content_type_plain;
|
||||
case "text/html" -> R.string.sk_content_type_html;
|
||||
case "text/markdown" -> R.string.sk_content_type_markdown;
|
||||
case "text/bbcode" -> R.string.sk_content_type_bbcode;
|
||||
case "text/x.misskeymarkdown" -> R.string.sk_content_type_mfm;
|
||||
default -> throw new IllegalArgumentException("Invalid content type");
|
||||
};
|
||||
}
|
||||
|
||||
private void addBottomLanguage(Menu menu) {
|
||||
if (menu.findItem(allLanguages.size()) == null) {
|
||||
menu.add(0, allLanguages.size(), Menu.NONE, "bottom (\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48)");
|
||||
@@ -983,9 +993,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
|
||||
private void resetPublishButtonText() {
|
||||
int publishText = editingStatus==null || redraftStatus ? R.string.publish : R.string.save;
|
||||
if(GlobalUserPreferences.relocatePublishButton){
|
||||
return;
|
||||
}
|
||||
if (publishText == R.string.publish && !GlobalUserPreferences.publishButtonText.isEmpty()) {
|
||||
publishButton.setText(GlobalUserPreferences.publishButtonText);
|
||||
} else {
|
||||
@@ -1008,13 +1015,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
nonDoneAttachmentCount++;
|
||||
}
|
||||
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
|
||||
// sendError.setVisibility(View.GONE);
|
||||
sendError.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void onCustomEmojiClick(Emoji emoji){
|
||||
int start=mainEditText.getSelectionStart();
|
||||
String prefix=start>0 && !Character.isWhitespace(mainEditText.getText().charAt(start-1)) ? " :" : ":";
|
||||
mainEditText.getText().replace(start, mainEditText.getSelectionEnd(), prefix+emoji.shortcode+':');
|
||||
if(getActivity().getCurrentFocus() instanceof EditText edit){
|
||||
int start=edit.getSelectionStart();
|
||||
String prefix=start>0 && !Character.isWhitespace(edit.getText().charAt(start-1)) ? " :" : ":";
|
||||
edit.getText().replace(start, edit.getSelectionEnd(), prefix+emoji.shortcode+':');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1024,18 +1033,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
|
||||
private void onPublishClick(View v){
|
||||
if (!attachments.isEmpty()
|
||||
&& statusVisibility != StatusPrivacy.DIRECT
|
||||
&& !attachments.stream().allMatch(attachment -> attachment.description != null && !attachment.description.isBlank())) {
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.mo_no_image_desc_title)
|
||||
.setMessage(R.string.mo_no_image_desc)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.publish, (dialog, i)-> publish())
|
||||
.show();
|
||||
} else {
|
||||
publish();
|
||||
}
|
||||
publish();
|
||||
}
|
||||
|
||||
private void publishErrorCallback(ErrorResponse error) {
|
||||
@@ -1096,6 +1094,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
||||
req.sensitive=sensitive;
|
||||
req.language=language;
|
||||
req.contentType=contentType;
|
||||
req.scheduledAt = scheduledAt;
|
||||
if(!attachments.isEmpty()){
|
||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||
@@ -1137,6 +1136,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
if(hasSpoiler && spoilerEdit.length()>0){
|
||||
req.spoilerText=spoilerEdit.getText().toString();
|
||||
}
|
||||
if(quote != null){
|
||||
req.quoteId=quote.id;
|
||||
}
|
||||
if(uuid==null)
|
||||
uuid=UUID.randomUUID().toString();
|
||||
|
||||
@@ -1169,7 +1171,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}else{
|
||||
E.post(new StatusUpdatedEvent(result));
|
||||
}
|
||||
Nav.finish(ComposeFragment.this);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) {
|
||||
Nav.finish(ComposeFragment.this);
|
||||
}
|
||||
if (getArguments().getBoolean("navigateToStatus", false)) {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
@@ -1186,7 +1190,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if(editingStatus!=null && !redraftStatus){
|
||||
new EditStatus(req, editingStatus.id)
|
||||
.setCallback(resCallback)
|
||||
@@ -1493,7 +1496,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
|
||||
private void uploadMediaAttachment(DraftMediaAttachment attachment){
|
||||
if(areThereAnyUploadingAttachments()){
|
||||
throw new IllegalStateException("there is already an attachment being uploaded");
|
||||
throw new IllegalStateException("there is already an attachment being uploaded");
|
||||
}
|
||||
attachment.state=AttachmentUploadState.UPLOADING;
|
||||
attachment.progressBar.setVisibility(View.VISIBLE);
|
||||
@@ -1586,7 +1589,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
if(att.isUploadingOrProcessing())
|
||||
att.cancelUpload();
|
||||
attachments.remove(att);
|
||||
uploadNextQueuedAttachment();
|
||||
if(!areThereAnyUploadingAttachments())
|
||||
uploadNextQueuedAttachment();
|
||||
attachmentsView.removeView(att.view);
|
||||
if(getMediaAttachmentsCount()==0)
|
||||
attachmentsView.setVisibility(View.GONE);
|
||||
@@ -1691,6 +1695,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
DraftMediaAttachment att=(DraftMediaAttachment) v.getTag();
|
||||
if(att.serverAttachment==null)
|
||||
return;
|
||||
editMediaDescription(att);
|
||||
}
|
||||
|
||||
private void editMediaDescription(DraftMediaAttachment att) {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("attachment", att.serverAttachment.id);
|
||||
@@ -1848,15 +1856,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
scheduleDraftText.setText(R.string.sk_compose_draft);
|
||||
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_regular, 0, 0, 0);
|
||||
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_draft));
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(GlobalUserPreferences.relocatePublishButton ? R.drawable.ic_fluent_drafts_24_regular : R.drawable.ic_fluent_drafts_20_filled, 0, 0, 0);
|
||||
|
||||
if(GlobalUserPreferences.relocatePublishButton){
|
||||
publishButton.setCompoundDrawablesWithIntrinsicBounds(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
|
||||
? R.drawable.ic_fluent_save_24_selector : R.drawable.ic_fluent_drafts_24_selector, 0, 0, 0);
|
||||
}else{
|
||||
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
|
||||
? R.string.save : R.string.sk_draft);
|
||||
}
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_filled, 0, 0, 0);
|
||||
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
|
||||
? R.string.save : R.string.sk_draft);
|
||||
} else {
|
||||
scheduleMenuItem.setVisible(false);
|
||||
unscheduleMenuItem.setVisible(true);
|
||||
@@ -1866,21 +1868,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
scheduleDraftText.setText(R.string.sk_compose_scheduled);
|
||||
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_schedule));
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(GlobalUserPreferences.relocatePublishButton ? R.drawable.ic_fluent_clock_24_filled : R.drawable.ic_fluent_clock_20_filled, 0, 0, 0);
|
||||
if(GlobalUserPreferences.relocatePublishButton)
|
||||
{
|
||||
publishButton.setCompoundDrawablesWithIntrinsicBounds(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
|
||||
? R.drawable.ic_fluent_save_24_selector : R.drawable.ic_fluent_clock_24_selector, 0, 0, 0);
|
||||
}else{
|
||||
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.equals(scheduledAt)
|
||||
? R.string.save : R.string.sk_schedule);
|
||||
}
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_filled, 0, 0, 0);
|
||||
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.equals(scheduledAt)
|
||||
? R.string.save : R.string.sk_schedule);
|
||||
}
|
||||
} else {
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(GlobalUserPreferences.relocatePublishButton ? R.drawable.ic_fluent_clock_24_regular : R.drawable.ic_fluent_clock_20_regular, 0, 0, 0);
|
||||
if(GlobalUserPreferences.relocatePublishButton){
|
||||
publishButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_send_24_selector, 0, 0, 0);
|
||||
}
|
||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_regular, 0, 0, 0);
|
||||
resetPublishButtonText();
|
||||
}
|
||||
}
|
||||
@@ -1944,14 +1937,36 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void buildContentTypePopup(View btn) {
|
||||
contentTypePopup=new PopupMenu(getActivity(), btn);
|
||||
contentTypePopup.inflate(R.menu.compose_content_type);
|
||||
Menu m = contentTypePopup.getMenu();
|
||||
ContentType.adaptMenuToInstance(m, instance);
|
||||
if (contentType != null) m.findItem(R.id.content_type_null).setVisible(false);
|
||||
|
||||
contentTypePopup.setOnMenuItemClickListener(i->{
|
||||
int id=i.getItemId();
|
||||
if (id == R.id.content_type_null) contentType = null;
|
||||
else if (id == R.id.content_type_plain) contentType = ContentType.PLAIN;
|
||||
else if (id == R.id.content_type_html) contentType = ContentType.HTML;
|
||||
else if (id == R.id.content_type_markdown) contentType = ContentType.MARKDOWN;
|
||||
else if (id == R.id.content_type_bbcode) contentType = ContentType.BBCODE;
|
||||
else if (id == R.id.content_type_misskey_markdown) contentType = ContentType.MISSKEY_MARKDOWN;
|
||||
else return false;
|
||||
btn.setSelected(id != R.id.content_type_null && id != R.id.content_type_plain);
|
||||
i.setChecked(true);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID)) {
|
||||
btn.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||
if(replyTo != null) statusVisibility = replyTo.visibility;
|
||||
|
||||
// A saved privacy setting from a previous compose session wins over the reply visibility
|
||||
if(savedInstanceState !=null){
|
||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||
}
|
||||
|
||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
||||
Preferences prefs = asm.getAccount(accountID).preferences;
|
||||
if (prefs != null) {
|
||||
@@ -2100,14 +2115,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
});
|
||||
}
|
||||
private void editMediaDescription(DraftMediaAttachment att) {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("attachment", att.serverAttachment.id);
|
||||
args.putParcelable("uri", att.uri);
|
||||
args.putString("existingDescription", att.description);
|
||||
Nav.goForResult(getActivity(), ComposeImageDescriptionFragment.class, args, IMAGE_DESCRIPTION_RESULT, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle(){
|
||||
|
||||
@@ -47,11 +47,10 @@ import java.util.Map;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
||||
public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
||||
private String accountID;
|
||||
private TimelinesAdapter adapter;
|
||||
private final ItemTouchHelper itemTouchHelper;
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetFavoritedStatuses;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
|
||||
@@ -35,4 +36,9 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -47,7 +46,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
|
||||
public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
|
||||
private String accountID;
|
||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||
private GetAccountRelationships relationshipsRequest;
|
||||
|
||||
@@ -16,11 +16,10 @@ import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop {
|
||||
public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implements ScrollableToTop {
|
||||
private String nextMaxID;
|
||||
private String accountId;
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface HasFab {
|
||||
View getFab();
|
||||
void showFab();
|
||||
void hideFab();
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
private MenuItem followButton;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -138,12 +138,12 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onFabLongClick(View v) {
|
||||
public boolean onFabLongClick(View v) {
|
||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFabClick(View v){
|
||||
public void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("prefilledText", '#'+hashtag+' ');
|
||||
@@ -154,4 +154,9 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
protected void onSetFabBottomInset(int inset){
|
||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.PUBLIC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,34 @@ import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
||||
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.TabBar;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.squareup.otto.Subscribe;
|
||||
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.fragments.LoaderFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
@@ -41,17 +54,14 @@ import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
|
||||
public class HomeFragment extends AppKitFragment implements OnBackPressedListener{
|
||||
private FragmentRootLinearLayout content;
|
||||
|
||||
private HomeTabFragment homeTabFragment;
|
||||
|
||||
// private HomeTimelineFragment homeTimelineFragment;
|
||||
|
||||
private NotificationsFragment notificationsFragment;
|
||||
private DiscoverFragment searchFragment;
|
||||
private ProfileFragment profileFragment;
|
||||
private TabBar tabBar;
|
||||
private View tabBarWrap;
|
||||
private ImageView tabBarAvatar;
|
||||
private ImageView notificationTabIcon;
|
||||
@IdRes
|
||||
private int currentTab=R.id.tab_home;
|
||||
|
||||
@@ -60,8 +70,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
E.register(this);
|
||||
accountID=getArguments().getString("account");
|
||||
setTitle(R.string.mo_app_name);
|
||||
setTitle(R.string.sk_app_name);
|
||||
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||
setRetainInstance(true);
|
||||
@@ -69,13 +80,8 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
if(savedInstanceState==null){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
|
||||
homeTabFragment=new HomeTabFragment();
|
||||
homeTabFragment.setArguments(args);
|
||||
|
||||
// homeTimelineFragment=new HomeTimelineFragment();
|
||||
// homeTimelineFragment.setArguments(args);
|
||||
|
||||
args=new Bundle(args);
|
||||
args.putBoolean("noAutoLoad", true);
|
||||
searchFragment=new DiscoverFragment();
|
||||
@@ -98,7 +104,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
content.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
FrameLayout fragmentContainer=new FrameLayout(getActivity());
|
||||
fragmentContainer.setId(R.id.fragment_wrap);
|
||||
fragmentContainer.setId(me.grishka.appkit.R.id.fragment_wrap);
|
||||
content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||
|
||||
inflater.inflate(R.layout.tab_bar, content);
|
||||
@@ -117,21 +123,17 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||
ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28)));
|
||||
|
||||
notificationTabIcon=content.findViewById(R.id.tab_notifications);
|
||||
updateNotificationBadge();
|
||||
|
||||
if(savedInstanceState==null){
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.fragment_wrap, homeTabFragment)
|
||||
.add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||
.add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||
.add(me.grishka.appkit.R.id.fragment_wrap, homeTabFragment)
|
||||
.add(me.grishka.appkit.R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||
.add(me.grishka.appkit.R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||
.add(me.grishka.appkit.R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||
.commit();
|
||||
|
||||
// getChildFragmentManager().beginTransaction()
|
||||
// .add(R.id.fragment_wrap, homeTimelineFragment)
|
||||
// .add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||
// .add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||
// .add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||
// .commit();
|
||||
|
||||
String defaultTab=getArguments().getString("tab");
|
||||
if("notifications".equals(defaultTab)){
|
||||
tabBar.selectTab(R.id.tab_notifications);
|
||||
@@ -152,21 +154,14 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
@Override
|
||||
public void onViewStateRestored(Bundle savedInstanceState){
|
||||
super.onViewStateRestored(savedInstanceState);
|
||||
|
||||
if(savedInstanceState==null) return;
|
||||
|
||||
// if(savedInstanceState==null || homeTimelineFragment!=null)
|
||||
// return;
|
||||
|
||||
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
|
||||
|
||||
// homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
|
||||
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
||||
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
||||
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
||||
currentTab=savedInstanceState.getInt("selectedTab");
|
||||
tabBar.selectTab(currentTab);
|
||||
Fragment current=fragmentForTab(currentTab);
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.hide(homeTabFragment)
|
||||
.hide(searchFragment)
|
||||
@@ -174,14 +169,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
.hide(profileFragment)
|
||||
.show(current)
|
||||
.commit();
|
||||
|
||||
// getChildFragmentManager().beginTransaction()
|
||||
// .hide(homeTimelineFragment)
|
||||
// .hide(searchFragment)
|
||||
// .hide(notificationsFragment)
|
||||
// .hide(profileFragment)
|
||||
// .show(current)
|
||||
// .commit();
|
||||
maybeTriggerLoading(current);
|
||||
}
|
||||
|
||||
@@ -211,11 +198,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||
}
|
||||
WindowInsets topOnlyInsets=insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
|
||||
|
||||
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
|
||||
|
||||
// homeTimelineFragment.onApplyWindowInsets(topOnlyInsets);
|
||||
|
||||
searchFragment.onApplyWindowInsets(topOnlyInsets);
|
||||
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
|
||||
profileFragment.onApplyWindowInsets(topOnlyInsets);
|
||||
@@ -224,9 +207,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
private Fragment fragmentForTab(@IdRes int tab){
|
||||
if(tab==R.id.tab_home){
|
||||
return homeTabFragment;
|
||||
|
||||
// if(tab==R.id.tab_home){
|
||||
// return homeTimelineFragment;
|
||||
}else if(tab==R.id.tab_search){
|
||||
return searchFragment;
|
||||
}else if(tab==R.id.tab_notifications){
|
||||
@@ -240,23 +220,15 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
private void onTabSelected(@IdRes int tab){
|
||||
Fragment newFragment=fragmentForTab(tab);
|
||||
if(tab==currentTab){
|
||||
if(tab == R.id.tab_search){
|
||||
if(newFragment instanceof ScrollableToTop scrollable)
|
||||
scrollable.scrollToTop();
|
||||
searchFragment.selectSearch();
|
||||
return;
|
||||
}
|
||||
if(newFragment instanceof ScrollableToTop scrollable)
|
||||
scrollable.scrollToTop();
|
||||
return;
|
||||
}
|
||||
if(tab==currentTab && tab == R.id.tab_search){
|
||||
if(newFragment instanceof ScrollableToTop scrollable)
|
||||
if (tab == R.id.tab_search)
|
||||
searchFragment.onSelect();
|
||||
else if(newFragment instanceof ScrollableToTop scrollable)
|
||||
scrollable.scrollToTop();
|
||||
return;
|
||||
}
|
||||
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
|
||||
maybeTriggerLoading(newFragment);
|
||||
if (newFragment instanceof HasFab fabulous) fabulous.showFab();
|
||||
currentTab=tab;
|
||||
((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this);
|
||||
}
|
||||
@@ -288,12 +260,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
new AccountSwitcherSheet(getActivity()).show();
|
||||
return true;
|
||||
}
|
||||
if(tab==R.id.tab_search){
|
||||
onTabSelected(R.id.tab_search);
|
||||
tabBar.selectTab(R.id.tab_search);
|
||||
searchFragment.selectSearch();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -316,15 +282,52 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
public void onSaveInstanceState(Bundle outState){
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("selectedTab", currentTab);
|
||||
|
||||
if (homeTabFragment.isAdded()) getChildFragmentManager().putFragment(outState, "homeTabFragment", homeTabFragment);
|
||||
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||
}
|
||||
|
||||
// getChildFragmentManager().putFragment(outState, "homeTimelineFragment", homeTimelineFragment);
|
||||
// getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||
// getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||
// getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||
public void updateNotificationBadge() {
|
||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||
if (instance == null) return;
|
||||
|
||||
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance != null && instance.pleroma != null)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(List<Notification> notifications) {
|
||||
if (notifications.size() > 0) {
|
||||
try {
|
||||
long newestId = Long.parseLong(notifications.get(0).id);
|
||||
long lastSeenId = Long.parseLong(session.markers.notifications.lastReadId);
|
||||
setNotificationBadge(newestId > lastSeenId);
|
||||
} catch (Exception ignored) {
|
||||
setNotificationBadge(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
setNotificationBadge(false);
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
public void setNotificationBadge(boolean badge) {
|
||||
notificationTabIcon.setImageResource(badge
|
||||
? R.drawable.ic_fluent_alert_28_selector_badged
|
||||
: R.drawable.ic_fluent_alert_28_selector);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNotificationReceived(NotificationReceivedEvent notificationReceivedEvent) {
|
||||
if (notificationReceivedEvent.account.equals(accountID)) setNotificationBadge(true);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onAllNotificationsSeen(AllNotificationsSeenEvent allNotificationsSeenEvent) {
|
||||
setNotificationBadge(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.view.ViewParent;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
@@ -70,7 +71,7 @@ import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener {
|
||||
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener, HasFab {
|
||||
private static final int ANNOUNCEMENTS_RESULT = 654;
|
||||
|
||||
private String accountID;
|
||||
@@ -98,6 +99,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
private PopupMenu overflowPopup;
|
||||
private View overflowActionView = null;
|
||||
private boolean announcementsBadged, settingsBadged;
|
||||
private ImageButton fab;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
@@ -122,6 +124,10 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
@Override
|
||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
FrameLayout view = new FrameLayout(getContext());
|
||||
inflater.inflate(R.layout.compose_fab, view);
|
||||
fab = view.findViewById(R.id.fab);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
fab.setOnLongClickListener(this::onFabLongClick);
|
||||
pager = new ViewPager2(getContext());
|
||||
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
||||
|
||||
@@ -129,6 +135,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
Bundle args = new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putBoolean("__is_tab", true);
|
||||
args.putBoolean("__disable_fab", true);
|
||||
args.putBoolean("onlyPosts", true);
|
||||
|
||||
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
||||
@@ -280,6 +287,20 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
private void onFabClick(View v){
|
||||
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) {
|
||||
l.onFabClick(v);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onFabLongClick(View v) {
|
||||
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) {
|
||||
return l.onFabLongClick(v);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addListsToOverflowMenu() {
|
||||
Context ctx = getContext();
|
||||
listsMenu.clear();
|
||||
@@ -358,7 +379,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
addListsToOverflowMenu();
|
||||
addHashtagsToOverflowMenu();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !UiUtils.isEMUI()) {
|
||||
m.setGroupDividerEnabled(true);
|
||||
}
|
||||
}
|
||||
@@ -427,9 +448,20 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
updateSwitcherIcon(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFab() {
|
||||
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) l.showFab();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideFab() {
|
||||
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) l.hideFab();
|
||||
}
|
||||
|
||||
private void updateSwitcherIcon(int i) {
|
||||
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
||||
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
||||
showFab();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -657,6 +689,10 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
return hashtagsItems.values();
|
||||
}
|
||||
|
||||
public ImageButton getFab() {
|
||||
return fab;
|
||||
}
|
||||
|
||||
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
||||
@NonNull
|
||||
@Override
|
||||
|
||||
@@ -8,8 +8,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
@@ -38,7 +36,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
private String lastSavedMarkerID;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -50,7 +48,6 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
}
|
||||
|
||||
private List<Status> filterPosts(List<Status> items) {
|
||||
// This is the function I must use to solve the filters thing for real
|
||||
return items.stream().filter(i ->
|
||||
(GlobalUserPreferences.showReplies || i.inReplyToId == null) &&
|
||||
(GlobalUserPreferences.showBoosts || i.reblog == null)
|
||||
@@ -103,24 +100,24 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
@Override
|
||||
protected void onHidden(){
|
||||
super.onHidden();
|
||||
// if(!data.isEmpty()){
|
||||
// String topPostID=displayItems.get(list.getChildAdapterPosition(list.getChildAt(0))-getMainAdapterOffset()).parentID;
|
||||
// if(!topPostID.equals(lastSavedMarkerID)){
|
||||
// lastSavedMarkerID=topPostID;
|
||||
// new SaveMarkers(topPostID, null)
|
||||
// .setCallback(new Callback<>(){
|
||||
// @Override
|
||||
// public void onSuccess(SaveMarkers.Response result){
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(ErrorResponse error){
|
||||
// lastSavedMarkerID=null;
|
||||
// }
|
||||
// })
|
||||
// .exec(accountID);
|
||||
// }
|
||||
// }
|
||||
if(!data.isEmpty()){
|
||||
String topPostID=displayItems.get(Math.max(0, list.getChildAdapterPosition(list.getChildAt(0))-getMainAdapterOffset())).parentID;
|
||||
if(!topPostID.equals(lastSavedMarkerID)){
|
||||
lastSavedMarkerID=topPostID;
|
||||
new SaveMarkers(topPostID, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SaveMarkers.Response result){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
lastSavedMarkerID=null;
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onStatusCreated(StatusCreatedEvent ev){
|
||||
@@ -151,7 +148,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
result.get(result.size()-1).hasGapAfter=true;
|
||||
toAdd=result;
|
||||
}
|
||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
|
||||
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||
if(!toAdd.isEmpty()){
|
||||
prependItems(toAdd, true);
|
||||
@@ -167,6 +164,10 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
|
||||
if (parent.getParentFragment() instanceof HomeFragment homeFragment) {
|
||||
homeFragment.updateNotificationBadge();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,7 +227,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
||||
targetList.clear();
|
||||
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
|
||||
for(Status s:result){
|
||||
if(idsBelowGap.contains(s.id))
|
||||
break;
|
||||
@@ -279,4 +280,9 @@ public class HomeTimelineFragment extends StatusListFragment {
|
||||
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.HOME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||
@Override
|
||||
public void onSuccess(List<Status> result) {
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.HOME)).collect(Collectors.toList());
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -152,7 +152,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFabClick(View v){
|
||||
public void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
@@ -162,4 +162,10 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
||||
protected void onSetFabBottomInset(int inset) {
|
||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.HOME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,10 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> implements ScrollableToTop {
|
||||
public class ListTimelinesFragment extends RecyclerFragment<ListTimeline> implements ScrollableToTop {
|
||||
private String accountId;
|
||||
private String profileAccountId;
|
||||
private final HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
||||
|
||||
@@ -44,7 +44,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
private FrameLayout[] tabViews;
|
||||
private TabLayoutMediator tabLayoutMediator;
|
||||
|
||||
private NotificationsListFragment allNotificationsFragment, mentionsFragment, postsFragment;
|
||||
private NotificationsListFragment allNotificationsFragment, mentionsFragment;
|
||||
|
||||
private String accountID;
|
||||
|
||||
@@ -102,14 +102,14 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
|
||||
tabLayout=view.findViewById(R.id.tabbar);
|
||||
pager=view.findViewById(R.id.pager);
|
||||
UiUtils.reduceSwipeSensitivity(pager);
|
||||
|
||||
tabViews=new FrameLayout[3];
|
||||
tabViews=new FrameLayout[2];
|
||||
for(int i=0;i<tabViews.length;i++){
|
||||
FrameLayout tabView=new FrameLayout(getActivity());
|
||||
tabView.setId(switch(i){
|
||||
case 0 -> R.id.notifications_all;
|
||||
case 1 -> R.id.notifications_mentions;
|
||||
case 2 -> R.id.notifications_posts;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
tabView.setVisibility(View.GONE);
|
||||
@@ -119,6 +119,18 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
|
||||
tabLayout.setTabTextSize(V.dp(16));
|
||||
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
scrollToTop();
|
||||
}
|
||||
});
|
||||
|
||||
pager.setOffscreenPageLimit(4);
|
||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||
@@ -149,15 +161,9 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
mentionsFragment=new NotificationsListFragment();
|
||||
mentionsFragment.setArguments(args);
|
||||
|
||||
args=new Bundle(args);
|
||||
args.putBoolean("onlyPosts", true);
|
||||
postsFragment=new NotificationsListFragment();
|
||||
postsFragment.setArguments(args);
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.notifications_all, allNotificationsFragment)
|
||||
.add(R.id.notifications_mentions, mentionsFragment)
|
||||
.add(R.id.notifications_posts, postsFragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@@ -167,7 +173,6 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.all_notifications;
|
||||
case 1 -> R.string.mentions;
|
||||
case 2 -> R.string.posts;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||
});
|
||||
tab.view.textView.setAllCaps(true);
|
||||
@@ -182,6 +187,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
||||
if (getActivity() == null) return;
|
||||
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
||||
}
|
||||
|
||||
@@ -210,13 +216,13 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
protected void updateToolbar(){
|
||||
super.updateToolbar();
|
||||
getToolbar().setOutlineProvider(null);
|
||||
getToolbar().setOnClickListener(v->scrollToTop());
|
||||
}
|
||||
|
||||
private NotificationsListFragment getFragmentForPage(int page){
|
||||
return switch(page){
|
||||
case 0 -> allNotificationsFragment;
|
||||
case 1 -> mentionsFragment;
|
||||
case 2 -> postsFragment;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
};
|
||||
}
|
||||
@@ -226,7 +232,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
@@ -237,7 +243,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 3;
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,4 +251,4 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,19 +11,20 @@ import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
@@ -40,7 +41,6 @@ import java.util.stream.Stream;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||
private boolean onlyMentions;
|
||||
@@ -49,8 +49,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
return true;
|
||||
protected boolean wantsComposeButton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,6 +84,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
||||
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
|
||||
n.report.targetAccount;
|
||||
Emoji emoji = new Emoji();
|
||||
if(n.emojiUrl!=null){
|
||||
emoji.shortcode=n.emoji.substring(1,n.emoji.length()-1);
|
||||
emoji.url=n.emojiUrl;
|
||||
emoji.staticUrl=n.emojiUrl;
|
||||
emoji.visibleInPicker=false;
|
||||
}
|
||||
String extraText=switch(n.type){
|
||||
case FOLLOW -> getString(R.string.user_followed_you);
|
||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||
@@ -94,17 +101,12 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
case UPDATE -> getString(R.string.sk_post_edited);
|
||||
case SIGN_UP -> getString(R.string.sk_signed_up);
|
||||
case REPORT -> getString(R.string.sk_reported);
|
||||
case REACTION, PLEROMA_EMOJI_REACTION ->
|
||||
n.emoji != null ? getString(R.string.sk_reacted_with, n.emoji) : getString(R.string.sk_reacted);
|
||||
};
|
||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, n.emojiUrl!=null ? HtmlParser.parseCustomEmoji(extraText, Collections.singletonList(emoji)) : extraText, n, null) : null;
|
||||
if(n.status!=null){
|
||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, false, Filter.FilterContext.NOTIFICATIONS);
|
||||
if(titleItem!=null){
|
||||
for(StatusDisplayItem item:items){
|
||||
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||
imgItem.horizontalInset=V.dp(32);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(titleItem!=null)
|
||||
items.add(0, titleItem);
|
||||
return items;
|
||||
@@ -127,6 +129,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
knownAccounts.put(s.account.id, s.account);
|
||||
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
|
||||
knownAccounts.put(s.status.account.id, s.status.account);
|
||||
if(s.status!=null && s.status.reblog!=null && !knownAccounts.containsKey(s.status.reblog.account.id))
|
||||
knownAccounts.put(s.status.reblog.account.id, s.status.reblog.account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +139,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
.getAccount(accountID).getCacheController()
|
||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||
public void onSuccess(CacheablePaginatedResponse<List<Notification>> result){
|
||||
if (getActivity() == null) return;
|
||||
if(refreshing)
|
||||
relationships.clear();
|
||||
@@ -147,8 +151,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
loadRelationships(needRelationships);
|
||||
maxID=result.maxID;
|
||||
|
||||
if(offset==0 && !result.items.isEmpty()){
|
||||
if(offset==0 && !result.items.isEmpty() && !result.isFromCache()){
|
||||
E.post(new AllNotificationsSeenEvent());
|
||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||
if (AccountSessionManager.getInstance().getAccount(accountID).markers != null)
|
||||
AccountSessionManager.getInstance().getAccount(accountID).markers
|
||||
.notifications.lastReadId = result.items.get(0).id;
|
||||
AccountSessionManager.getInstance().writeAccountsFile();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -198,7 +207,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||
if (getParentFragment() instanceof NotificationsFragment) fab.setVisibility(View.GONE);
|
||||
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PinnableStatusListFragment extends StatusListFragment {
|
||||
protected boolean pinnedUpdated;
|
||||
protected List<TimelineDefinition> pinnedTimelines;
|
||||
|
||||
@Override
|
||||
@@ -54,7 +53,7 @@ public abstract class PinnableStatusListFragment extends StatusListFragment {
|
||||
}
|
||||
|
||||
protected void togglePin(MenuItem pin) {
|
||||
pinnedUpdated = true;
|
||||
onPinnedUpdated(true);
|
||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
TimelineDefinition def = makeTimelineDefinition();
|
||||
boolean pinned = isPinned();
|
||||
@@ -66,17 +65,5 @@ public abstract class PinnableStatusListFragment extends StatusListFragment {
|
||||
updatePinButton(pin);
|
||||
}
|
||||
|
||||
protected Bundle getResultArgs() {
|
||||
return new Bundle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Bundle resultArgs = getResultArgs();
|
||||
if (pinnedUpdated) {
|
||||
resultArgs.putBoolean("pinnedUpdated", true);
|
||||
setResult(true, resultArgs);
|
||||
}
|
||||
}
|
||||
public void onPinnedUpdated(boolean pinned) {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
@@ -13,23 +12,11 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
|
||||
import org.joinmastodon.android.model.AccountField;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||
@@ -39,8 +26,11 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.fragments.WindowInsetsAwareFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
@@ -56,11 +46,6 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
||||
private static final int MAX_FIELDS=4;
|
||||
|
||||
public UsableRecyclerView list;
|
||||
public FrameLayout noteWrap;
|
||||
public EditText noteEdit;
|
||||
private String accountID;
|
||||
private String profileAccountID;
|
||||
private String note;
|
||||
private List<AccountField> fields=Collections.emptyList();
|
||||
private AboutAdapter adapter;
|
||||
private Paint dividerPaint=new Paint();
|
||||
@@ -79,49 +64,11 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setNote(String note, String accountID, String profileAccountID){
|
||||
this.note=note;
|
||||
this.accountID=accountID;
|
||||
this.profileAccountID=profileAccountID;
|
||||
// noteWrap.setVisibility(View.VISIBLE);
|
||||
// noteEdit.setVisibility(View.VISIBLE);
|
||||
// noteEdit.setText(note);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
||||
View view = inflater.inflate(R.layout.fragment_profile_about, null);
|
||||
|
||||
noteEdit = view.findViewById(R.id.note_edit);
|
||||
noteWrap = view.findViewById(R.id.note_edit_wrap);
|
||||
ImageButton noteEditConfirm = view.findViewById(R.id.note_edit_confirm);
|
||||
|
||||
|
||||
noteEdit.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
noteEditConfirm.setVisibility(View.VISIBLE);
|
||||
noteEditConfirm.animate()
|
||||
.alpha(1.0f)
|
||||
.setDuration(700);
|
||||
} else {
|
||||
noteEditConfirm.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(700);
|
||||
noteEditConfirm.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
noteEditConfirm.setOnClickListener((v -> {
|
||||
if (!noteEdit.getText().toString().trim().equals(note)) {
|
||||
savePrivateNote();
|
||||
}
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(this.getView().getRootView().getWindowToken(), 0);
|
||||
noteEdit.clearFocus();
|
||||
}));
|
||||
|
||||
list = view.findViewById(R.id.list);
|
||||
list=new UsableRecyclerView(getActivity());
|
||||
list.setId(R.id.list);
|
||||
list.setItemAnimator(new BetterItemAnimator());
|
||||
list.setDrawSelectorOnTop(true);
|
||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
@@ -148,20 +95,8 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
||||
}
|
||||
}
|
||||
});
|
||||
return view;
|
||||
return list;
|
||||
}
|
||||
private void savePrivateNote(){
|
||||
new SetPrivateNote(profileAccountID, noteEdit.getText().toString()).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship result) {}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse result) {
|
||||
Toast.makeText(getActivity(), getString(R.string.mo_personal_note_update_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
|
||||
public void enterEditMode(List<AccountField> editableFields){
|
||||
isInEditMode=true;
|
||||
@@ -402,7 +337,7 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||
viewHolder.itemView.setTag(me.grishka.appkit.R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||
draggedViewHolder=viewHolder;
|
||||
}
|
||||
|
||||
@@ -19,13 +19,12 @@ import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
@@ -44,11 +43,6 @@ import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
||||
@@ -56,7 +50,6 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
|
||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||
@@ -117,7 +110,7 @@ import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop, HasFab{
|
||||
private static final int AVATAR_RESULT=722;
|
||||
private static final int COVER_RESULT=343;
|
||||
|
||||
@@ -142,9 +135,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private TextView followsYouView;
|
||||
private ViewGroup rolesView;
|
||||
|
||||
public FrameLayout noteWrap;
|
||||
public EditText noteEdit;
|
||||
private String note;
|
||||
private Account account;
|
||||
private String accountID;
|
||||
private Relationship relationship;
|
||||
@@ -160,7 +150,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private WindowInsets childInsets;
|
||||
private PhotoViewer currentPhotoViewer;
|
||||
private boolean editModeLoading;
|
||||
protected int scrollDiff = 0;
|
||||
|
||||
private static final int MAX_FIELDS=4;
|
||||
|
||||
@@ -226,7 +215,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
followingCount=content.findViewById(R.id.following_count);
|
||||
followingLabel=content.findViewById(R.id.following_label);
|
||||
followingBtn=content.findViewById(R.id.following_btn);
|
||||
|
||||
postsCount=content.findViewById(R.id.posts_count);
|
||||
postsLabel=content.findViewById(R.id.posts_label);
|
||||
postsBtn=content.findViewById(R.id.posts_btn);
|
||||
@@ -245,10 +233,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
list=content.findViewById(R.id.metadata);
|
||||
rolesView=content.findViewById(R.id.roles);
|
||||
|
||||
noteEdit = content.findViewById(R.id.note_edit);
|
||||
noteWrap = content.findViewById(R.id.note_edit_wrap);
|
||||
Button noteEditConfirm = content.findViewById(R.id.note_edit_confirm);
|
||||
|
||||
avatar.setOutlineProvider(new ViewOutlineProvider(){
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline){
|
||||
@@ -257,49 +241,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
});
|
||||
avatar.setClipToOutline(true);
|
||||
|
||||
noteEdit.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (hasFocus) {
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
|
||||
noteEditConfirm.setVisibility(View.VISIBLE);
|
||||
noteEditConfirm.animate()
|
||||
.alpha(1.0f)
|
||||
.setDuration(700);
|
||||
} else {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
|
||||
noteEditConfirm.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(700);
|
||||
noteEditConfirm.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
noteEditConfirm.setOnClickListener((v -> {
|
||||
if (!noteEdit.getText().toString().trim().equals(note)) {
|
||||
savePrivateNote();
|
||||
}
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(this.getView().getRootView().getWindowToken(), 0);
|
||||
noteEdit.clearFocus();
|
||||
}));
|
||||
|
||||
FrameLayout sizeWrapper=new FrameLayout(getActivity()){
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||
@@ -361,6 +302,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
});
|
||||
|
||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||
avatar.setOnClickListener(this::onAvatarClick);
|
||||
cover.setOnClickListener(this::onCoverClick);
|
||||
@@ -399,25 +341,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
return sizeWrapper;
|
||||
}
|
||||
|
||||
public void setNote(String note){
|
||||
this.note=note;
|
||||
noteWrap.setVisibility(View.VISIBLE);
|
||||
noteEdit.setVisibility(View.VISIBLE);
|
||||
noteEdit.setText(note);
|
||||
}
|
||||
|
||||
private void savePrivateNote(){
|
||||
new SetPrivateNote(profileAccountID, noteEdit.getText().toString()).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship result) {}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse result) {
|
||||
Toast.makeText(getActivity(), getString(R.string.mo_personal_note_update_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(){
|
||||
currentRequest=new GetAccountByID(profileAccountID)
|
||||
@@ -455,11 +378,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
@Override
|
||||
public void onRefresh(){
|
||||
if(isInEditMode){
|
||||
refreshing=false;
|
||||
refreshLayout.setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
if(refreshing)
|
||||
return;
|
||||
refreshing=true;
|
||||
@@ -518,6 +436,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
toolbarTitleView.setTranslationY(titleTransY);
|
||||
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||
}
|
||||
RecyclerFragment.setRefreshLayoutColors(refreshLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -576,15 +495,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
for (Account.Role role : account.roles) {
|
||||
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
||||
roleText.setText(role.name);
|
||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||
if (!TextUtils.isEmpty(role.color) && role.color.startsWith("#")) try {
|
||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||
} catch (Exception ignored) {}
|
||||
rolesView.addView(roleText);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||
|
||||
|
||||
if(account.locked){
|
||||
ssb=new SpannableStringBuilder("@");
|
||||
ssb.append(account.acct);
|
||||
@@ -598,19 +518,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
lock.setTint(username.getCurrentTextColor());
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
|
||||
username.setText(ssb);
|
||||
}else if(account.bot){
|
||||
ssb=new SpannableStringBuilder("@");
|
||||
ssb.append(account.acct);
|
||||
if(isSelf){
|
||||
ssb.append('@');
|
||||
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||
}
|
||||
ssb.append(" ");
|
||||
Drawable botIcon=username.getResources().getDrawable(R.drawable.ic_bot, getActivity().getTheme()).mutate();
|
||||
botIcon.setBounds(0, 0, botIcon.getIntrinsicWidth(), botIcon.getIntrinsicHeight());
|
||||
botIcon.setTint(username.getCurrentTextColor());
|
||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(botIcon, ImageSpan.ALIGN_BASELINE), 0);
|
||||
username.setText(ssb);
|
||||
}else{
|
||||
// noinspection SetTextI18n
|
||||
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
|
||||
@@ -622,8 +529,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
bio.setVisibility(View.VISIBLE);
|
||||
bio.setText(parsedBio);
|
||||
}
|
||||
|
||||
|
||||
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
|
||||
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
|
||||
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
|
||||
@@ -643,10 +548,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
fields.clear();
|
||||
|
||||
AccountField joined=new AccountField();
|
||||
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
||||
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
||||
fields.add(joined);
|
||||
if (account.createdAt != null) {
|
||||
AccountField joined=new AccountField();
|
||||
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
||||
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
||||
fields.add(joined);
|
||||
}
|
||||
|
||||
for(AccountField field:account.fields){
|
||||
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||
@@ -701,10 +608,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
if(relationship==null && !isOwnProfile)
|
||||
return;
|
||||
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
|
||||
if(isOwnProfile){
|
||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.scheduled, R.id.bookmarks, R.id.favorites);
|
||||
}else{
|
||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags);
|
||||
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
||||
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
|
||||
openWithAccounts.setVisible(hasMultipleAccounts);
|
||||
SubMenu accountsMenu = openWithAccounts.getSubMenu();
|
||||
if (hasMultipleAccounts) {
|
||||
accountsMenu.clear();
|
||||
UiUtils.populateAccountsMenu(accountID, accountsMenu, s-> UiUtils.openURL(
|
||||
getActivity(), s.getID(), account.url, false
|
||||
));
|
||||
}
|
||||
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||
if(isOwnProfile)
|
||||
@@ -838,10 +751,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||
notifyButton.setSelected(relationship.notifying);
|
||||
if (!isOwnProfile) {
|
||||
setNote(relationship.note);
|
||||
// aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
||||
}
|
||||
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||
}
|
||||
|
||||
@@ -849,6 +758,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
return fab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFab() {
|
||||
if (getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous) fabulous.showFab();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideFab() {
|
||||
if (getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous) fabulous.hideFab();
|
||||
}
|
||||
|
||||
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||
int topBarsH=getToolbar().getHeight()+statusBarHeight;
|
||||
if(scrollY>avatarBorder.getTop()-topBarsH){
|
||||
@@ -879,36 +798,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
if(currentPhotoViewer!=null){
|
||||
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||
}
|
||||
|
||||
int dy = scrollY - oldScrollY;
|
||||
|
||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
fab.setVisibility(View.INVISIBLE);
|
||||
scrollDiff = 0;
|
||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||
if (scrollDiff > 400) {
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
TranslateAnimation animate = new TranslateAnimation(
|
||||
0,
|
||||
0,
|
||||
fab.getHeight() * 2,
|
||||
0);
|
||||
animate.setDuration(300);
|
||||
animate.setFillAfter(true);
|
||||
fab.startAnimation(animate);
|
||||
scrollDiff = 0;
|
||||
} else {
|
||||
scrollDiff += Math.abs(dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Fragment getFragmentForPage(int page){
|
||||
@@ -937,6 +826,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onActionButtonLongClick(View v) {
|
||||
if (isOwnProfile || AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
|
||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
||||
UiUtils.lookupAccount(getActivity(), account, session.getID(), accountID, acc -> {
|
||||
if (acc == null) return;
|
||||
new SetAccountFollowed(acc.id, true, true).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Relationship relationship) {
|
||||
Toast.makeText(
|
||||
getActivity(),
|
||||
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
||||
Toast.LENGTH_SHORT
|
||||
).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
}).exec(session.getID());
|
||||
});
|
||||
}, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setActionProgressVisible(boolean visible){
|
||||
actionButton.setTextVisible(!visible);
|
||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
@@ -983,7 +897,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
pager.setUserInputEnabled(false);
|
||||
actionButton.setText(R.string.done);
|
||||
ArrayList<Animator> animators=new ArrayList<>();
|
||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay, getActivity().getTheme()).mutate();
|
||||
avatar.setForeground(overlay);
|
||||
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
||||
|
||||
@@ -1102,9 +1016,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(noteEdit.hasFocus()) {
|
||||
savePrivateNote();
|
||||
}
|
||||
if(isInEditMode){
|
||||
exitEditMode();
|
||||
return true;
|
||||
@@ -1203,7 +1114,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
@@ -1355,7 +1266,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
private class EditableAboutViewHolder extends BaseViewHolder {
|
||||
private EditText title;
|
||||
private boolean titleHasFocus, valueHasFocus;
|
||||
private EditText value;
|
||||
|
||||
public EditableAboutViewHolder(){
|
||||
@@ -1367,19 +1277,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
return true;
|
||||
});
|
||||
title.addTextChangedListener(new SimpleTextWatcher(e->item.name=e.toString()));
|
||||
title.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
titleHasFocus = hasFocus;
|
||||
}
|
||||
});
|
||||
value.addTextChangedListener(new SimpleTextWatcher(e->item.value=e.toString()));
|
||||
value.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
valueHasFocus = hasFocus;
|
||||
}
|
||||
});
|
||||
findViewById(R.id.remove_row_btn).setOnClickListener(this::onRemoveRowClick);
|
||||
}
|
||||
|
||||
@@ -1390,10 +1288,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}
|
||||
|
||||
private void onRemoveRowClick(View v){
|
||||
if(titleHasFocus || valueHasFocus){
|
||||
return;
|
||||
}
|
||||
|
||||
int pos=getAbsoluteAdapterPosition();
|
||||
metadataListData.remove(pos);
|
||||
adapter.notifyItemRemoved(pos);
|
||||
@@ -1459,7 +1353,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||
viewHolder.itemView.setTag(me.grishka.appkit.R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||
draggedViewHolder=viewHolder;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
|
||||
|
||||
public abstract class RecyclerFragment<T> extends BaseRecyclerFragment<T> {
|
||||
public RecyclerFragment(int perPage) {
|
||||
super(perPage);
|
||||
}
|
||||
|
||||
public RecyclerFragment(int layout, int perPage) {
|
||||
super(layout, perPage);
|
||||
}
|
||||
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (refreshLayout != null) setRefreshLayoutColors(refreshLayout);
|
||||
}
|
||||
|
||||
public static void setRefreshLayoutColors(SwipeRefreshLayout l) {
|
||||
List<Integer> colors = new ArrayList<>(Arrays.asList(
|
||||
R.color.primary_600,
|
||||
R.color.red_primary_600,
|
||||
R.color.green_primary_600,
|
||||
R.color.blue_primary_600,
|
||||
R.color.purple_600
|
||||
));
|
||||
int primary = UiUtils.getThemeColorRes(l.getContext(), R.attr.colorPrimary600);
|
||||
if (!colors.contains(primary)) colors.add(0, primary);
|
||||
int offset = colors.indexOf(primary);
|
||||
int[] sorted = new int[colors.size()];
|
||||
for (int i = 0; i < colors.size(); i++) {
|
||||
sorted[i] = colors.get((i + offset) % colors.size());
|
||||
}
|
||||
l.setColorSchemeResources(sorted);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetScheduledStatuses;
|
||||
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
|
||||
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||
import org.joinmastodon.android.model.ScheduledStatus;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
@@ -32,7 +31,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFabClick(View v) {
|
||||
public void onFabClick(View v) {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||
@@ -65,7 +64,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onFabLongClick(View v) {
|
||||
public boolean onFabLongClick(View v) {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||
@@ -80,7 +79,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
||||
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, Filter.FilterContext.HOME);
|
||||
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,6 +96,8 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
||||
args.putString("sourceText", status.text);
|
||||
args.putString("sourceSpoiler", status.spoilerText);
|
||||
args.putBoolean("redraftStatus", true);
|
||||
args.putString("sourceContentType", scheduledStatus.params.contentType != null ?
|
||||
scheduledStatus.params.contentType.name() : null);
|
||||
setResult(true, null);
|
||||
|
||||
// closing this scheduled status list if another status list is opened from compose fragment
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.util.LruCache;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -21,8 +22,6 @@ import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -50,6 +49,7 @@ import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||
import org.joinmastodon.android.model.ContentType;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||
import org.joinmastodon.android.model.PushNotification;
|
||||
@@ -57,6 +57,7 @@ import org.joinmastodon.android.model.PushSubscription;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
@@ -65,6 +66,7 @@ import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -81,7 +83,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
private ArrayList<Item> items=new ArrayList<>();
|
||||
private ThemeItem themeItem;
|
||||
private NotificationPolicyItem notificationPolicyItem;
|
||||
private SwitchItem showNewPostsButtonItem, glitchModeItem;
|
||||
private SwitchItem showNewPostsItem, glitchModeItem, compactReblogReplyLineItem;
|
||||
private ButtonItem defaultContentTypeButtonItem;
|
||||
private String accountID;
|
||||
private boolean needUpdateNotificationSettings;
|
||||
private boolean needAppRestart;
|
||||
@@ -90,7 +93,9 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
private ImageView themeTransitionWindowView;
|
||||
private TextItem checkForUpdateItem, clearImageCacheItem;
|
||||
private ImageCache imageCache;
|
||||
private Menu contentTypeMenu;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -114,21 +119,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
items.add(new HeaderItem(R.string.settings_theme));
|
||||
items.add(themeItem=new ThemeItem());
|
||||
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
|
||||
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||
GlobalUserPreferences.disableMarquee=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
||||
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
|
||||
GlobalUserPreferences.reduceMotion=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
|
||||
|
||||
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
|
||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||
popupMenu.inflate(R.menu.color_palettes);
|
||||
@@ -145,41 +135,43 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
case BROWN -> R.string.sk_color_palette_brown;
|
||||
case RED -> R.string.sk_color_palette_red;
|
||||
case YELLOW -> R.string.sk_color_palette_yellow;
|
||||
case NORD -> R.string.mo_color_palette_nord;
|
||||
});
|
||||
}));
|
||||
items.add(new ButtonItem(R.string.sk_settings_publish_button_text, R.drawable.ic_fluent_send_24_regular, b-> {
|
||||
items.add(new ButtonItem(R.string.sk_settings_publish_button_text, R.drawable.ic_fluent_send_24_regular, b->{
|
||||
updatePublishText(b);
|
||||
if (GlobalUserPreferences.relocatePublishButton) {
|
||||
b.setOnClickListener(l -> {
|
||||
Toast.makeText(getActivity(), R.string.mo_disable_relocate_publish_button_to_enable_customization,
|
||||
Toast.LENGTH_LONG).show();
|
||||
});
|
||||
} else {
|
||||
b.setOnClickListener(l -> {
|
||||
FrameLayout inputWrap = new FrameLayout(getContext());
|
||||
EditText input = new EditText(getContext());
|
||||
input.setHint(R.string.publish);
|
||||
input.setText(GlobalUserPreferences.publishButtonText.trim());
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.setMargins(V.dp(16), V.dp(4), V.dp(16), V.dp(16));
|
||||
input.setLayoutParams(params);
|
||||
inputWrap.addView(input);
|
||||
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_publish_button_text_title).setView(inputWrap)
|
||||
.setPositiveButton(R.string.save, (d, which) -> {
|
||||
GlobalUserPreferences.publishButtonText = input.getText().toString().trim();
|
||||
GlobalUserPreferences.save();
|
||||
updatePublishText(b);
|
||||
})
|
||||
.setNeutralButton(R.string.clear, (d, which) -> {
|
||||
GlobalUserPreferences.publishButtonText = "";
|
||||
GlobalUserPreferences.save();
|
||||
updatePublishText(b);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, (d, which) -> {
|
||||
})
|
||||
.show();
|
||||
});}
|
||||
|
||||
b.setOnClickListener(l->{
|
||||
TextInputFrameLayout input = new TextInputFrameLayout(
|
||||
getContext(),
|
||||
getString(R.string.publish),
|
||||
GlobalUserPreferences.publishButtonText.trim()
|
||||
);
|
||||
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_publish_button_text_title).setView(input)
|
||||
.setPositiveButton(R.string.save, (d, which) -> {
|
||||
GlobalUserPreferences.publishButtonText = input.getEditText().getText().toString().trim();
|
||||
GlobalUserPreferences.save();
|
||||
updatePublishText(b);
|
||||
})
|
||||
.setNeutralButton(R.string.clear, (d, which) -> {
|
||||
GlobalUserPreferences.publishButtonText = "";
|
||||
GlobalUserPreferences.save();
|
||||
updatePublishText(b);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
||||
.show();
|
||||
});
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
||||
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||
GlobalUserPreferences.disableMarquee=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
|
||||
GlobalUserPreferences.reduceMotion=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
|
||||
items.add(new HeaderItem(R.string.settings_behavior));
|
||||
@@ -209,19 +201,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
||||
items.add(new SwitchItem(R.string.mo_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
|
||||
GlobalUserPreferences.disableDividers=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
||||
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
||||
GlobalUserPreferences.relocatePublishButton=i.checked;
|
||||
items.add(new SwitchItem(R.string.sk_settings_disable_alt_text_reminder, R.drawable.ic_fluent_image_alt_text_24_regular, GlobalUserPreferences.disableAltTextReminder, i->{
|
||||
GlobalUserPreferences.disableAltTextReminder=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
||||
@@ -232,55 +213,96 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
GlobalUserPreferences.prefixRepliesWithRe=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_confirm_before_reblog, R.drawable.ic_fluent_checkmark_circle_24_regular, GlobalUserPreferences.confirmBeforeReblog, i->{
|
||||
GlobalUserPreferences.confirmBeforeReblog=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
|
||||
items.add(new HeaderItem(R.string.sk_timelines));
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||
GlobalUserPreferences.showReplies=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
if (instance.pleroma != null) {
|
||||
items.add(new ButtonItem(R.string.sk_settings_reply_visibility, R.drawable.ic_fluent_chat_24_regular, b->{
|
||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||
popupMenu.inflate(R.menu.reply_visibility);
|
||||
popupMenu.setOnMenuItemClickListener(item -> this.onReplyVisibilityChanged(item, b));
|
||||
b.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||
b.setOnClickListener(v->popupMenu.show());
|
||||
b.setText(GlobalUserPreferences.replyVisibility == null ?
|
||||
R.string.sk_settings_reply_visibility_all :
|
||||
switch(GlobalUserPreferences.replyVisibility){
|
||||
case "following" -> R.string.sk_settings_reply_visibility_following;
|
||||
case "self" -> R.string.sk_settings_reply_visibility_self;
|
||||
default -> R.string.sk_settings_reply_visibility_all;
|
||||
});
|
||||
}));
|
||||
}
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
|
||||
GlobalUserPreferences.showBoosts=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||
showNewPostsButtonItem.enabled = i.checked;
|
||||
showNewPostsItem.enabled = i.checked;
|
||||
if (!i.checked) {
|
||||
GlobalUserPreferences.showNewPostsButton = false;
|
||||
showNewPostsButtonItem.checked = false;
|
||||
showNewPostsItem.checked = false;
|
||||
}
|
||||
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsButtonItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(showNewPostsButtonItem = new SwitchItem(R.string.sk_settings_show_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
||||
items.add(showNewPostsItem = new SwitchItem(R.string.sk_settings_see_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
||||
GlobalUserPreferences.showNewPostsButton=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_alt_indicator, R.drawable.ic_fluent_scan_text_24_regular, GlobalUserPreferences.showAltIndicator, i->{
|
||||
GlobalUserPreferences.showAltIndicator=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
|
||||
GlobalUserPreferences.showNoAltIndicator=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_collapse_long_posts, R.drawable.ic_fluent_chevron_down_24_filled, GlobalUserPreferences.collapseLongPosts, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_collapse_long_posts, R.drawable.ic_fluent_chevron_down_24_regular, GlobalUserPreferences.collapseLongPosts, i->{
|
||||
GlobalUserPreferences.collapseLongPosts=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_spectator_mode, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
||||
GlobalUserPreferences.spectatorMode=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||
// GlobalUserPreferences.save();
|
||||
// needAppRestart=true;
|
||||
// }));
|
||||
|
||||
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
|
||||
GlobalUserPreferences.autoHideFab=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_reply_line_above_avatar, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.replyLineAboveHeader, i->{
|
||||
GlobalUserPreferences.replyLineAboveHeader=i.checked;
|
||||
GlobalUserPreferences.compactReblogReplyLine=i.checked;
|
||||
compactReblogReplyLineItem.enabled=i.checked;
|
||||
compactReblogReplyLineItem.checked= GlobalUserPreferences.replyLineAboveHeader;
|
||||
if (list.findViewHolderForAdapterPosition(items.indexOf(compactReblogReplyLineItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
items.add(compactReblogReplyLineItem=new SwitchItem(R.string.sk_compact_reblog_reply_line, R.drawable.ic_fluent_re_order_24_regular, GlobalUserPreferences.compactReblogReplyLine, i->{
|
||||
GlobalUserPreferences.compactReblogReplyLine=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
compactReblogReplyLineItem.enabled=GlobalUserPreferences.replyLineAboveHeader;
|
||||
items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||
GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
||||
items.add(new SmallTextItem(getString(translationAvailable ?
|
||||
R.string.sk_settings_translation_availability_note_available :
|
||||
R.string.sk_settings_translation_availability_note_unavailable, instanceName)));
|
||||
|
||||
items.add(new HeaderItem(R.string.settings_notifications));
|
||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||
@@ -314,6 +336,36 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
|
||||
|
||||
items.add(new HeaderItem(R.string.sk_instance_features));
|
||||
items.add(new SwitchItem(R.string.sk_settings_content_types, 0, GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID), (i)->{
|
||||
if (i.checked) {
|
||||
GlobalUserPreferences.accountsWithContentTypesEnabled.add(accountID);
|
||||
if (GlobalUserPreferences.accountsDefaultContentTypes.get(accountID) == null) {
|
||||
GlobalUserPreferences.accountsDefaultContentTypes.put(accountID, ContentType.PLAIN);
|
||||
}
|
||||
} else {
|
||||
GlobalUserPreferences.accountsWithContentTypesEnabled.remove(accountID);
|
||||
GlobalUserPreferences.accountsDefaultContentTypes.remove(accountID);
|
||||
}
|
||||
if (list.findViewHolderForAdapterPosition(items.indexOf(defaultContentTypeButtonItem))
|
||||
instanceof ButtonViewHolder bvh) bvh.rebind();
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SmallTextItem(getString(R.string.sk_settings_content_types_explanation)));
|
||||
items.add(defaultContentTypeButtonItem = new ButtonItem(R.string.sk_settings_default_content_type, 0, b->{
|
||||
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||
popupMenu.inflate(R.menu.compose_content_type);
|
||||
popupMenu.setOnMenuItemClickListener(item -> this.onContentTypeChanged(item, b));
|
||||
b.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||
b.setOnClickListener(v->popupMenu.show());
|
||||
ContentType contentType = GlobalUserPreferences.accountsDefaultContentTypes.get(accountID);
|
||||
b.setText(getContentTypeString(contentType));
|
||||
contentTypeMenu = popupMenu.getMenu();
|
||||
contentTypeMenu.findItem(ContentType.getContentTypeRes(contentType)).setChecked(true);
|
||||
ContentType.adaptMenuToInstance(contentTypeMenu, instance);
|
||||
contentTypeMenu.findItem(R.id.content_type_null).setVisible(
|
||||
!GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID));
|
||||
}));
|
||||
items.add(new SmallTextItem(getString(R.string.sk_settings_default_content_type_explanation)));
|
||||
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
|
||||
glitchModeItem.enabled = i.checked;
|
||||
if (i.checked) {
|
||||
@@ -339,27 +391,9 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
glitchModeItem.enabled = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
||||
items.add(new SmallTextItem(getString(R.string.sk_settings_glitch_mode_explanation)));
|
||||
|
||||
|
||||
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
||||
items.add(new SmallTextItem(getString(translationAvailable ?
|
||||
R.string.sk_settings_translation_availability_note_available :
|
||||
R.string.sk_settings_translation_availability_note_unavailable, instance.title)));
|
||||
|
||||
|
||||
items.add(new HeaderItem(R.string.sk_settings_about));
|
||||
|
||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||
items.add(checkForUpdateItem);
|
||||
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
|
||||
GlobalUserPreferences.enablePreReleases=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
}
|
||||
|
||||
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
|
||||
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
|
||||
|
||||
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon"), R.drawable.ic_fluent_open_24_regular));
|
||||
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://ko-fi.com/xsk22"), R.drawable.ic_fluent_heart_24_regular));
|
||||
LruCache<?, ?> cache = imageCache == null ? null : imageCache.getLruCache();
|
||||
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), cache != null ? cache.size() : 0, true), this::clearImageCache, 0);
|
||||
items.add(clearImageCacheItem);
|
||||
@@ -367,12 +401,14 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
GlobalUserPreferences.recentLanguages.remove(accountID);
|
||||
GlobalUserPreferences.save();
|
||||
})));
|
||||
|
||||
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
|
||||
GlobalUserPreferences.recentEmojis.clear();
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
|
||||
GlobalUserPreferences.enablePreReleases=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||
items.add(checkForUpdateItem);
|
||||
}
|
||||
|
||||
if(BuildConfig.DEBUG){
|
||||
items.add(new RedHeaderItem("Debug options"));
|
||||
@@ -387,7 +423,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}));
|
||||
}
|
||||
|
||||
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||
}
|
||||
|
||||
private void updatePublishText(Button btn) {
|
||||
@@ -475,7 +511,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
else if (id == R.id.brown_color) pref = ColorPreference.BROWN;
|
||||
else if (id == R.id.red_color) pref = ColorPreference.RED;
|
||||
else if (id == R.id.yellow_color) pref = ColorPreference.YELLOW;
|
||||
else if (id == R.id.nord_color) pref = ColorPreference.NORD;
|
||||
|
||||
if (pref == null) return false;
|
||||
|
||||
@@ -485,7 +520,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void onTrueBlackThemeChanged(SwitchItem item){
|
||||
GlobalUserPreferences.trueBlackTheme=item.checked;
|
||||
GlobalUserPreferences.save();
|
||||
@@ -502,6 +536,53 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
}
|
||||
|
||||
private @StringRes int getContentTypeString(@Nullable ContentType contentType) {
|
||||
if (contentType == null) return R.string.sk_content_type_unspecified;
|
||||
return switch (contentType) {
|
||||
case PLAIN -> R.string.sk_content_type_plain;
|
||||
case HTML -> R.string.sk_content_type_html;
|
||||
case MARKDOWN -> R.string.sk_content_type_markdown;
|
||||
case BBCODE -> R.string.sk_content_type_bbcode;
|
||||
case MISSKEY_MARKDOWN -> R.string.sk_content_type_mfm;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean onContentTypeChanged(MenuItem item, Button btn){
|
||||
int id = item.getItemId();
|
||||
ContentType contentType = switch (id) {
|
||||
case R.id.content_type_plain -> ContentType.PLAIN;
|
||||
case R.id.content_type_html -> ContentType.HTML;
|
||||
case R.id.content_type_markdown -> ContentType.MARKDOWN;
|
||||
case R.id.content_type_bbcode -> ContentType.BBCODE;
|
||||
case R.id.content_type_misskey_markdown -> ContentType.MISSKEY_MARKDOWN;
|
||||
default -> null;
|
||||
};
|
||||
GlobalUserPreferences.accountsDefaultContentTypes.put(accountID, contentType);
|
||||
GlobalUserPreferences.save();
|
||||
btn.setText(getContentTypeString(contentType));
|
||||
item.setChecked(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onReplyVisibilityChanged(MenuItem item, Button btn){
|
||||
String pref = null;
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == R.id.reply_visibility_following) pref = "following";
|
||||
else if (id == R.id.reply_visibility_self) pref = "self";
|
||||
|
||||
GlobalUserPreferences.replyVisibility=pref;
|
||||
GlobalUserPreferences.save();
|
||||
btn.setText(GlobalUserPreferences.replyVisibility == null ?
|
||||
R.string.sk_settings_reply_visibility_all :
|
||||
switch(GlobalUserPreferences.replyVisibility){
|
||||
case "following" -> R.string.sk_settings_reply_visibility_following;
|
||||
case "self" -> R.string.sk_settings_reply_visibility_self;
|
||||
default -> R.string.sk_settings_reply_visibility_all;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private void restartActivityToApplyNewTheme(){
|
||||
// Calling activity.recreate() causes a black screen for like half a second.
|
||||
// So, let's take a screenshot and overlay it on top to create the illusion of a smoother transition.
|
||||
@@ -552,12 +633,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
needUpdateNotificationSettings=true;
|
||||
}
|
||||
|
||||
private void onNotificationStyleChanged(SwitchItem item){
|
||||
GlobalUserPreferences.uniformNotificationIcon=item.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}
|
||||
|
||||
|
||||
private void onNotificationsPolicyChanged(PushSubscription.Policy policy){
|
||||
PushSubscription subscription=getPushSubscription();
|
||||
PushSubscription.Policy prevPolicy=subscription.policy;
|
||||
@@ -727,13 +802,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
}
|
||||
|
||||
public class ColorPicker extends Item{
|
||||
@Override
|
||||
public int getViewType(){
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThemeItem extends Item{
|
||||
|
||||
@Override
|
||||
@@ -987,7 +1055,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ButtonViewHolder extends BindableViewHolder<ButtonItem>{
|
||||
private final Button button;
|
||||
private final ImageView icon;
|
||||
@@ -1004,7 +1071,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
@Override
|
||||
public void onBind(ButtonItem item){
|
||||
text.setText(item.text);
|
||||
icon.setImageResource(item.icon);
|
||||
if (item.icon == 0) {
|
||||
icon.setVisibility(View.GONE);
|
||||
} else {
|
||||
icon.setImageResource(item.icon);
|
||||
}
|
||||
item.buttonConsumer.accept(button);
|
||||
}
|
||||
}
|
||||
@@ -1151,10 +1222,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
|
||||
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
|
||||
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
|
||||
text.setText(getString(R.string.mo_update_available, info.version));
|
||||
text.setText(getString(R.string.sk_update_available, info.version));
|
||||
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
|
||||
}else{
|
||||
text.setText(getString(R.string.mo_update_ready, info.version));
|
||||
text.setText(getString(R.string.sk_update_ready, info.version));
|
||||
button.setText(R.string.install_update);
|
||||
}
|
||||
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){
|
||||
@@ -1171,7 +1242,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
progress.removeCallbacks(progressUpdater);
|
||||
}
|
||||
changelog.setText(info.changelog);
|
||||
// changelog.setText(getString(R.string.sk_changelog, info.changelog));
|
||||
}
|
||||
|
||||
private void updateProgress(){
|
||||
|
||||
@@ -1,43 +1,47 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.BottomSheet;
|
||||
|
||||
public class SplashFragment extends AppKitFragment{
|
||||
|
||||
private static final String DEFAULT_SERVER="mastodon.social";
|
||||
|
||||
private SizeListenerFrameLayout contentView;
|
||||
private View artContainer, blueFill, greenFill;
|
||||
private ViewPager2 pager;
|
||||
private ViewGroup pagerDots;
|
||||
private InterpolatingMotionEffect motionEffect;
|
||||
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -46,44 +50,26 @@ public class SplashFragment extends AppKitFragment{
|
||||
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
||||
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
||||
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
||||
Button joinDefault=contentView.findViewById(R.id.btn_join_default_server);
|
||||
joinDefault.setText(getString(R.string.join_default_server, DEFAULT_SERVER));
|
||||
joinDefault.setOnClickListener(this::onJoinDefaultServerClick);
|
||||
contentView.findViewById(R.id.btn_learn_more).setOnClickListener(this::onLearnMoreClick);
|
||||
|
||||
artClouds=contentView.findViewById(R.id.art_clouds);
|
||||
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
|
||||
artRightHill=contentView.findViewById(R.id.art_right_hill);
|
||||
artLeftHill=contentView.findViewById(R.id.art_left_hill);
|
||||
artCenterHill=contentView.findViewById(R.id.art_center_hill);
|
||||
pager=contentView.findViewById(R.id.pager);
|
||||
pagerDots=contentView.findViewById(R.id.pager_dots);
|
||||
pager.setAdapter(new PagerAdapter());
|
||||
pager.setOffscreenPageLimit(3);
|
||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
|
||||
for(int i=0;i<pagerDots.getChildCount();i++){
|
||||
float alpha;
|
||||
if(i==position){
|
||||
alpha=0.3f+0.7f*(1f-positionOffset);
|
||||
}else if(i==position+1){
|
||||
alpha=0.3f+0.7f*positionOffset;
|
||||
}else{
|
||||
alpha=0.3f;
|
||||
}
|
||||
pagerDots.getChildAt(i).setAlpha(alpha);
|
||||
}
|
||||
|
||||
float parallaxProgress=(position+positionOffset)/2f;
|
||||
artClouds.setTranslationX(V.dp(-27)*(position>=1 ? 1f : positionOffset));
|
||||
artPlaneElephant.setTranslationX(V.dp(101.55f)*parallaxProgress);
|
||||
artLeftHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||
artLeftHill.setTranslationY(V.dp(24)*parallaxProgress);
|
||||
artRightHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||
artRightHill.setTranslationY(V.dp(-24)*parallaxProgress);
|
||||
artCenterHill.setTranslationX(V.dp(-40)*parallaxProgress);
|
||||
}
|
||||
});
|
||||
|
||||
artContainer=contentView.findViewById(R.id.art_container);
|
||||
blueFill=contentView.findViewById(R.id.blue_fill);
|
||||
greenFill=contentView.findViewById(R.id.green_fill);
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artClouds, V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artRightHill, V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artLeftHill, V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artCenterHill, V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artPlaneElephant, V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
||||
artContainer.setOnTouchListener(motionEffect);
|
||||
|
||||
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
||||
@Override
|
||||
@@ -109,6 +95,38 @@ public class SplashFragment extends AppKitFragment{
|
||||
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
|
||||
}
|
||||
|
||||
private void onJoinDefaultServerClick(View v){
|
||||
new GetInstance()
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Instance result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
Bundle args=new Bundle();
|
||||
args.putParcelable("instance", Parcels.wrap(result));
|
||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
})
|
||||
.wrapProgress(getActivity(), R.string.loading_instance, true)
|
||||
.execNoAuth(DEFAULT_SERVER);
|
||||
}
|
||||
|
||||
private void onLearnMoreClick(View v){
|
||||
View sheetView=getActivity().getLayoutInflater().inflate(R.layout.intro_bottom_sheet, null);
|
||||
BottomSheet sheet=new BottomSheet(getActivity());
|
||||
sheet.setContentView(sheetView);
|
||||
sheet.setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Surface),
|
||||
UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme());
|
||||
sheet.show();
|
||||
}
|
||||
|
||||
private void updateArtSize(int w, int h){
|
||||
float scale=w/(float)V.dp(360);
|
||||
artContainer.setScaleX(scale);
|
||||
@@ -139,60 +157,15 @@ public class SplashFragment extends AppKitFragment{
|
||||
return true;
|
||||
}
|
||||
|
||||
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new PagerViewHolder(viewType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return position;
|
||||
}
|
||||
@Override
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
motionEffect.activate();
|
||||
}
|
||||
|
||||
private class PagerViewHolder extends RecyclerView.ViewHolder{
|
||||
public PagerViewHolder(int page){
|
||||
super(new LinearLayout(getActivity()));
|
||||
LinearLayout ll=(LinearLayout) itemView;
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
int pad=V.dp(16);
|
||||
ll.setPadding(pad, pad, pad, pad);
|
||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
TextView title=new TextView(getActivity());
|
||||
title.setTextAppearance(R.style.m3_headline_medium);
|
||||
title.setText(switch(page){
|
||||
case 0 -> getString(R.string.welcome_page1_title);
|
||||
case 1 -> getString(R.string.welcome_page2_title);
|
||||
case 2 -> getString(R.string.welcome_page3_title);
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
});
|
||||
title.setTextColor(0xFF17063B);
|
||||
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(page==0 ? 46 : 36));
|
||||
lp.bottomMargin=V.dp(page==0 ? 4 : 14);
|
||||
ll.addView(title, lp);
|
||||
|
||||
TextView text=new TextView(getActivity());
|
||||
text.setTextAppearance(R.style.m3_body_medium);
|
||||
text.setText(switch(page){
|
||||
case 0 -> R.string.welcome_page1_text;
|
||||
case 1 -> R.string.welcome_page2_text;
|
||||
case 2 -> R.string.welcome_page3_text;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
});
|
||||
text.setTextColor(0xFF17063B);
|
||||
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
@Override
|
||||
protected void onHidden(){
|
||||
super.onHidden();
|
||||
motionEffect.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, Filter.FilterContext.HOME);
|
||||
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, null);
|
||||
int idx=data.indexOf(s);
|
||||
if(idx>=0){
|
||||
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
||||
@@ -157,4 +157,9 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
||||
public boolean isItemEnabled(String id){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,17 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||
boolean addFooter = !GlobalUserPreferences.spectatorMode ||
|
||||
(this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id));
|
||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, Filter.FilterContext.HOME);
|
||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, getFilterContext());
|
||||
}
|
||||
|
||||
protected abstract Filter.FilterContext getFilterContext();
|
||||
|
||||
@Override
|
||||
protected void addAccountToKnown(Status s){
|
||||
if(!knownAccounts.containsKey(s.account.id))
|
||||
knownAccounts.put(s.account.id, s.account);
|
||||
if(s.reblog!=null && !knownAccounts.containsKey(s.reblog.account.id))
|
||||
knownAccounts.put(s.reblog.account.id, s.reblog.account);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,7 +153,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
||||
|
||||
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
||||
List<Status> toRemove=Stream.concat(data.stream(), preloadedData.stream())
|
||||
.filter(s->s.account.id.equals(ev.postsByAccountID) || (s.reblog!=null && s.reblog.account.id.equals(ev.postsByAccountID)))
|
||||
.filter(s->s.account.id.equals(ev.postsByAccountID) || (!ev.isUnfollow && s.reblog!=null && s.reblog.account.id.equals(ev.postsByAccountID)))
|
||||
.collect(Collectors.toList());
|
||||
for(Status s:toRemove){
|
||||
removeStatus(s);
|
||||
|
||||
@@ -5,9 +5,12 @@ import android.view.View;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.StatusContext;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
@@ -19,6 +22,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -68,6 +72,30 @@ public class ThreadFragment extends StatusListFragment{
|
||||
data.add(mainStatus);
|
||||
onAppendItems(Collections.singletonList(mainStatus));
|
||||
}
|
||||
AccountSession account=AccountSessionManager.getInstance().getAccount(accountID);
|
||||
Instance instance=AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
||||
if(instance.pleroma != null){
|
||||
List<String> threadIds=new ArrayList<>();
|
||||
threadIds.add(mainStatus.id);
|
||||
for(Status s:result.descendants){
|
||||
if(threadIds.contains(s.inReplyToId)){
|
||||
threadIds.add(s.id);
|
||||
}
|
||||
}
|
||||
threadIds.add(mainStatus.inReplyToId);
|
||||
for(int i=result.ancestors.size()-1; i >= 0; i--){
|
||||
Status s=result.ancestors.get(i);
|
||||
if(s.inReplyToId != null && threadIds.contains(s.id)){
|
||||
threadIds.add(s.inReplyToId);
|
||||
}
|
||||
}
|
||||
|
||||
result.ancestors=result.ancestors.stream().filter(s -> threadIds.contains(s.id)).collect(Collectors.toList());
|
||||
result.descendants=getDescendantsOrdered(mainStatus.id,
|
||||
result.descendants.stream()
|
||||
.filter(s -> threadIds.contains(s.id))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
result.descendants=filterStatuses(result.descendants);
|
||||
result.ancestors=filterStatuses(result.ancestors);
|
||||
if(footerProgress!=null)
|
||||
@@ -90,8 +118,26 @@ public class ThreadFragment extends StatusListFragment{
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
private List<Status> getDescendantsOrdered(String id, List<Status> statuses){
|
||||
List<Status> out=new ArrayList<>();
|
||||
for(Status s:getDirectDescendants(id, statuses)){
|
||||
out.add(s);
|
||||
getDirectDescendants(s.id, statuses).forEach(d ->{
|
||||
out.add(d);
|
||||
out.addAll(getDescendantsOrdered(d.id, statuses));
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private List<Status> getDirectDescendants(String id, List<Status> statuses){
|
||||
return statuses.stream()
|
||||
.filter(s -> s.inReplyToId.equals(id))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Status> filterStatuses(List<Status> statuses){
|
||||
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD);
|
||||
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,getFilterContext());
|
||||
return statuses.stream()
|
||||
.filter(statusFilterPredicate)
|
||||
.collect(Collectors.toList());
|
||||
@@ -135,4 +181,10 @@ public class ThreadFragment extends StatusListFragment{
|
||||
public boolean wantsLightNavigationBar(){
|
||||
return !UiUtils.isDarkTheme();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.THREAD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -48,7 +49,6 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.APIRequest;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -57,7 +57,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseAccountListFragment.AccountItem>{
|
||||
public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccountListFragment.AccountItem> {
|
||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||
protected String accountID;
|
||||
protected ArrayList<APIRequest<?>> relationshipsRequests=new ArrayList<>();
|
||||
@@ -295,7 +295,6 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||
|
||||
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
|
||||
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
|
||||
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername())).setVisible(relationship.following);
|
||||
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
|
||||
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
||||
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||
@@ -309,7 +308,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||
manageUserLists.setVisible(true);
|
||||
}else{
|
||||
hideBoosts.setVisible(false);
|
||||
manageUserLists.setVisible(true);
|
||||
manageUserLists.setVisible(false);
|
||||
}
|
||||
menu.findItem(R.id.block_domain).setVisible(false);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||
import org.joinmastodon.android.fragments.IsOnTop;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.FollowSuggestion;
|
||||
@@ -40,7 +41,6 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -49,7 +49,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccountsFragment.AccountWrapper> implements ScrollableToTop, IsOnTop {
|
||||
public class DiscoverAccountsFragment extends RecyclerFragment<DiscoverAccountsFragment.AccountWrapper> implements ScrollableToTop, IsOnTop {
|
||||
private String accountID;
|
||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||
private GetAccountRelationships relationshipsRequest;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.joinmastodon.android.fragments.discover;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
@@ -18,11 +17,10 @@ import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.IsOnTop;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||
@@ -38,7 +36,7 @@ import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener{
|
||||
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener, IsOnTop {
|
||||
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager2 pager;
|
||||
@@ -55,15 +53,10 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
private DiscoverNewsFragment newsFragment;
|
||||
private DiscoverAccountsFragment accountsFragment;
|
||||
private SearchFragment searchFragment;
|
||||
private LocalTimelineFragment localTimelineFragment;
|
||||
private FederatedTimelineFragment federatedTimelineFragment;
|
||||
private ListTimelinesFragment listTimelinesFragment;
|
||||
|
||||
private String accountID;
|
||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||
|
||||
// private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -81,22 +74,9 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
tabLayout=view.findViewById(R.id.tabbar);
|
||||
pager=view.findViewById(R.id.pager);
|
||||
|
||||
// tabViews=new FrameLayout[noFederated ? 5 : 6];
|
||||
tabViews=new FrameLayout[4];
|
||||
for(int i=0;i<tabViews.length;i++){
|
||||
FrameLayout tabView=new FrameLayout(getActivity());
|
||||
|
||||
/// int switchIndex = noFederated && i > 0 ? i + 1 : i;
|
||||
// tabView.setId(switch(switchIndex){
|
||||
// case 0 -> R.id.discover_local_timeline;
|
||||
// case 1 -> R.id.discover_federated_timeline;
|
||||
// case 2 -> R.id.discover_hashtags;
|
||||
// case 3 -> R.id.discover_posts;
|
||||
// case 4 -> R.id.discover_news;
|
||||
// case 5 -> R.id.discover_users;
|
||||
// default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
|
||||
// });
|
||||
|
||||
tabView.setId(switch(i){
|
||||
case 0 -> R.id.discover_hashtags;
|
||||
case 1 -> R.id.discover_posts;
|
||||
@@ -104,7 +84,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
case 3 -> R.id.discover_users;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
|
||||
tabView.setVisibility(View.GONE);
|
||||
view.addView(tabView); // needed so the fragment manager will have somewhere to restore the tab fragment
|
||||
tabViews[i]=tabView;
|
||||
@@ -130,7 +109,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
}
|
||||
});
|
||||
|
||||
if(localTimelineFragment==null || hashtagsFragment==null){
|
||||
if(hashtagsFragment==null){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putBoolean("__is_tab", true);
|
||||
@@ -147,27 +126,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
accountsFragment=new DiscoverAccountsFragment();
|
||||
accountsFragment.setArguments(args);
|
||||
|
||||
localTimelineFragment=new LocalTimelineFragment();
|
||||
localTimelineFragment.setArguments(args);
|
||||
|
||||
// listTimelinesFragment=new ListTimelinesFragment();
|
||||
// listTimelinesFragment.setArguments(args);
|
||||
//
|
||||
// FragmentTransaction transaction = getChildFragmentManager().beginTransaction()
|
||||
// .add(R.id.discover_posts, postsFragment)
|
||||
// .add(R.id.discover_local_timeline, localTimelineFragment)
|
||||
// .add(R.id.discover_hashtags, hashtagsFragment)
|
||||
// .add(R.id.discover_news, newsFragment)
|
||||
// .add(R.id.discover_users, accountsFragment)
|
||||
// .add(R.id.discover_lists, listTimelinesFragment);
|
||||
//
|
||||
// if (!noFederated) {
|
||||
// federatedTimelineFragment=new FederatedTimelineFragment();
|
||||
// federatedTimelineFragment.setArguments(args);
|
||||
// transaction.add(R.id.discover_federated_timeline, federatedTimelineFragment);
|
||||
// }
|
||||
//
|
||||
// transaction.commit();
|
||||
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.add(R.id.discover_posts, postsFragment)
|
||||
@@ -180,21 +138,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
tabLayoutMediator=new TabLayoutMediator(tabLayout, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||
@Override
|
||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||
|
||||
// if (noFederated && position > 0) position++;
|
||||
|
||||
// tab.setText(switch(position){
|
||||
// case 0 -> R.string.local_timeline;
|
||||
// case 1 -> R.string.sk_federated_timeline;
|
||||
// case 2 -> R.string.sk_list_timelines;
|
||||
// case 3 -> R.string.hashtags;
|
||||
// case 4 -> R.string.posts;
|
||||
// case 5 -> R.string.news;
|
||||
// case 6 -> R.string.for_you;
|
||||
//
|
||||
// default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||
// });
|
||||
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.hashtags;
|
||||
case 1 -> R.string.posts;
|
||||
@@ -284,12 +227,26 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnTop() {
|
||||
return searchActive ? searchFragment.isOnTop()
|
||||
: ((IsOnTop)getFragmentForPage(pager.getCurrentItem())).isOnTop();
|
||||
}
|
||||
|
||||
public void onSelect() {
|
||||
if (isOnTop()) selectSearch();
|
||||
else scrollToTop();
|
||||
}
|
||||
|
||||
private void selectSearch() {
|
||||
searchEdit.requestFocus();
|
||||
onSearchEditFocusChanged(searchEdit, true);
|
||||
getActivity().getSystemService(InputMethodManager.class).showSoftInput(searchEdit, 0);
|
||||
}
|
||||
|
||||
public void loadData(){
|
||||
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
|
||||
hashtagsFragment.loadData();
|
||||
|
||||
// if(localTimelineFragment!=null && !localTimelineFragment.loaded && !localTimelineFragment.dataLoading)
|
||||
// localTimelineFragment.loadData();
|
||||
}
|
||||
|
||||
private void onSearchEditFocusChanged(View v, boolean hasFocus){
|
||||
@@ -324,19 +281,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
}
|
||||
|
||||
private Fragment getFragmentForPage(int page){
|
||||
// if (noFederated && page > 0) page++;
|
||||
|
||||
// return switch(page){
|
||||
// case 0 -> localTimelineFragment;
|
||||
// case 1 -> federatedTimelineFragment;
|
||||
// case 2 -> hashtagsFragment;
|
||||
// case 3 -> postsFragment;
|
||||
// case 4 -> newsFragment;
|
||||
// case 5 -> accountsFragment;
|
||||
// case 6 -> listTimelinesFragment;
|
||||
// default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
// };
|
||||
|
||||
return switch(page){
|
||||
case 0 -> hashtagsFragment;
|
||||
case 1 -> postsFragment;
|
||||
@@ -398,10 +342,4 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
public void selectSearch(){
|
||||
searchEdit.requestFocus();
|
||||
onSearchEditFocusChanged(searchEdit, true);
|
||||
getActivity().getSystemService(InputMethodManager.class).showSoftInput(searchEdit, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.widget.TextView;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.trends.GetTrendingLinks;
|
||||
import org.joinmastodon.android.fragments.IsOnTop;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.model.Card;
|
||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
@@ -26,7 +27,6 @@ import java.util.stream.Collectors;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -35,7 +35,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements ScrollableToTop, IsOnTop {
|
||||
public class DiscoverNewsFragment extends RecyclerFragment<Card> implements ScrollableToTop, IsOnTop {
|
||||
private String accountID;
|
||||
private List<ImageLoaderRequest> imageRequests=Collections.emptyList();
|
||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_LINKS);
|
||||
|
||||
@@ -26,7 +26,7 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
}).exec(accountID);
|
||||
@@ -42,4 +42,10 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop
|
||||
public boolean isOnTop() {
|
||||
return isRecyclerViewOnTop(list);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.PUBLIC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class FederatedTimelineFragment extends StatusListFragment {
|
||||
private String maxID;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class FederatedTimelineFragment extends StatusListFragment {
|
||||
if(!result.isEmpty())
|
||||
maxID=result.get(result.size()-1).id;
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -45,6 +45,11 @@ public class FederatedTimelineFragment extends StatusListFragment {
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
// bannerHelper.maybeAddBanner(contentWrap);
|
||||
bannerHelper.maybeAddBanner(contentWrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.PUBLIC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class LocalTimelineFragment extends StatusListFragment {
|
||||
private String maxID;
|
||||
|
||||
@Override
|
||||
protected boolean withComposeButton() {
|
||||
protected boolean wantsComposeButton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class LocalTimelineFragment extends StatusListFragment {
|
||||
if(!result.isEmpty())
|
||||
maxID=result.get(result.size()-1).id;
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -46,4 +46,9 @@ public class LocalTimelineFragment extends StatusListFragment {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
bannerHelper.maybeAddBanner(contentWrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter.FilterContext getFilterContext() {
|
||||
return Filter.FilterContext.PUBLIC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.fragments.IsOnTop;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
@@ -38,11 +39,10 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
public class SearchFragment extends BaseStatusListFragment<SearchResult> implements IsOnTop {
|
||||
private String currentQuery;
|
||||
private List<StatusDisplayItem> prevDisplayItems;
|
||||
private EnumSet<SearchResult.Type> currentFilter=EnumSet.allOf(SearchResult.Type.class);
|
||||
@@ -185,7 +185,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
return;
|
||||
}
|
||||
UiUtils.updateList(prevDisplayItems, displayItems, list, adapter, (i1, i2)->i1.parentID.equals(i2.parentID) && i1.index==i2.index && i1.getType()==i2.getType());
|
||||
boolean recent=isInRecentMode();
|
||||
boolean recent=isInRecentMode() && !displayItems.isEmpty();
|
||||
if(recent!=headerAdapter.isVisible())
|
||||
headerAdapter.setVisible(recent);
|
||||
imgLoader.forceUpdateImages();
|
||||
@@ -248,7 +248,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
}
|
||||
|
||||
public void setQuery(String q){
|
||||
if(Objects.equals(q, currentQuery))
|
||||
if(Objects.equals(q, currentQuery) || q.isBlank())
|
||||
return;
|
||||
if(currentRequest!=null){
|
||||
currentRequest.cancel();
|
||||
@@ -311,6 +311,11 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnTop() {
|
||||
return isRecyclerViewOnTop(list);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ProgressVisibilityListener{
|
||||
void onProgressVisibilityChanged(boolean visible);
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.widget.TextView;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.trends.GetTrendingHashtags;
|
||||
import org.joinmastodon.android.fragments.IsOnTop;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
@@ -20,11 +21,10 @@ import java.util.List;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop, IsOnTop {
|
||||
public class TrendingHashtagsFragment extends RecyclerFragment<Hashtag> implements ScrollableToTop, IsOnTop {
|
||||
private String accountID;
|
||||
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_HASHTAGS);
|
||||
|
||||
|
||||
@@ -96,9 +96,9 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
}
|
||||
|
||||
// @Override
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
// super.onUpdateToolbar();
|
||||
super.onUpdateToolbar();
|
||||
getToolbar().setBackground(null);
|
||||
getToolbar().setElevation(0);
|
||||
}
|
||||
@@ -248,6 +248,6 @@ public class AccountActivationFragment extends ToolbarFragment{
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
// Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||
Nav.goClearingStack(getActivity(), OnboardingProfileSetupFragment.class, args);
|
||||
Nav.goClearingStack(getActivity(), OnboardingFollowSuggestionsFragment.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@ public class CustomWelcomeFragment extends InstanceCatalogFragment {
|
||||
headerView.findViewById(R.id.separator).setVisibility(View.GONE);
|
||||
headerView.findViewById(R.id.timestamp).setVisibility(View.GONE);
|
||||
headerView.findViewById(R.id.unread_indicator).setVisibility(View.GONE);
|
||||
((TextView) headerView.findViewById(R.id.username)).setText(R.string.mo_app_username);
|
||||
((TextView) headerView.findViewById(R.id.name)).setText(R.string.mo_app_name);
|
||||
((TextView) headerView.findViewById(R.id.username)).setText(R.string.sk_app_username);
|
||||
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
||||
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
||||
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
|
||||
|
||||
|
||||
@@ -5,25 +5,22 @@ import android.app.ProgressDialog;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.text.TextUtils;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
@@ -31,6 +28,8 @@ import org.xml.sax.InputSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.IDN;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -46,16 +45,14 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
||||
abstract class InstanceCatalogFragment extends RecyclerFragment<CatalogInstance> {
|
||||
protected RecyclerView.Adapter adapter;
|
||||
protected MergeRecyclerAdapter mergeAdapter;
|
||||
protected CatalogInstance chosenInstance;
|
||||
@@ -78,7 +75,7 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||
private static final double DUNBAR=Math.log(800);
|
||||
|
||||
public InstanceCatalogFragment(int layout, int perPage){
|
||||
super(layout, perPage);
|
||||
super(layout, perPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,46 +109,10 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||
}
|
||||
|
||||
protected List<CatalogInstance> sortInstances(List<CatalogInstance> result){
|
||||
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
|
||||
for(List<CatalogInstance> group:byLang.values()){
|
||||
Collections.sort(group, (a, b)->{
|
||||
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
|
||||
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
|
||||
return Double.compare(aa, bb);
|
||||
});
|
||||
}
|
||||
// get the list of user-configured system languages
|
||||
List<String> userLangs;
|
||||
if(Build.VERSION.SDK_INT<24){
|
||||
userLangs=Collections.singletonList(getResources().getConfiguration().locale.getLanguage());
|
||||
}else{
|
||||
LocaleList ll=getResources().getConfiguration().getLocales();
|
||||
userLangs=new ArrayList<>(ll.size());
|
||||
for(int i=0;i<ll.size();i++){
|
||||
userLangs.add(ll.get(i).getLanguage());
|
||||
}
|
||||
}
|
||||
// add instances in preferred languages to the top of the list, in the order of preference
|
||||
Map<Boolean, List<CatalogInstance>> byLang=result.stream().sorted(Comparator.comparingInt((CatalogInstance ci)->ci.lastWeekUsers).reversed()).collect(Collectors.groupingBy(ci->ci.approvalRequired));
|
||||
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
|
||||
for(String lang:userLangs){
|
||||
List<CatalogInstance> langInstances=byLang.remove(lang);
|
||||
if(langInstances!=null){
|
||||
sortedList.addAll(langInstances);
|
||||
}
|
||||
}
|
||||
// sort the remaining language groups by aggregate lastWeekUsers
|
||||
class InstanceGroup{
|
||||
public int activeUsers;
|
||||
public List<CatalogInstance> instances;
|
||||
}
|
||||
byLang.values().stream().map(il->{
|
||||
InstanceGroup group=new InstanceGroup();
|
||||
group.instances=il;
|
||||
for(CatalogInstance instance:il){
|
||||
group.activeUsers+=instance.lastWeekUsers;
|
||||
}
|
||||
return group;
|
||||
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
|
||||
sortedList.addAll(byLang.getOrDefault(false, Collections.emptyList()));
|
||||
sortedList.addAll(byLang.getOrDefault(true, Collections.emptyList()));
|
||||
return sortedList;
|
||||
}
|
||||
|
||||
@@ -208,6 +169,20 @@ abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInsta
|
||||
cancelLoadingInstanceInfo();
|
||||
}
|
||||
}
|
||||
try{
|
||||
new URI("https://"+domain+"/api/v1/instance"); // Validate the host by trying to parse the URI
|
||||
}catch(URISyntaxException x){
|
||||
showInstanceInfoLoadError(domain, x);
|
||||
if(fakeInstance!=null){
|
||||
fakeInstance.description=getString(R.string.error);
|
||||
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
|
||||
if(list.findViewHolderForAdapterPosition(1) instanceof BindableViewHolder<?> ivh){
|
||||
ivh.rebind();
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
loadingInstanceDomain=domain;
|
||||
loadingInstanceRequest=new GetInstance();
|
||||
loadingInstanceRequest.setCallback(new Callback<>(){
|
||||
|
||||
@@ -64,7 +64,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
|
||||
private List<String> languages=Collections.emptyList();
|
||||
private PopupMenu langFilterMenu, speedFilterMenu;
|
||||
private SignupSpeedFilter currentSignupSpeedFilter=SignupSpeedFilter.INSTANT;
|
||||
private SignupSpeedFilter currentSignupSpeedFilter=SignupSpeedFilter.ANY;
|
||||
private String currentLanguage=null;
|
||||
private boolean searchQueryMode;
|
||||
private LinearLayout filtersWrap;
|
||||
@@ -75,7 +75,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
private FilterChipView categoryGeneral, categorySpecialInterests;
|
||||
private List<FilterChipView> regionalFilters;
|
||||
private CatalogInstance.Region chosenRegion;
|
||||
private CategoryChoice categoryChoice;
|
||||
private CategoryChoice categoryChoice=CategoryChoice.GENERAL;
|
||||
|
||||
public InstanceCatalogSignupFragment(){
|
||||
super(R.layout.fragment_onboarding_common, 10);
|
||||
@@ -371,6 +371,9 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
if(instances.isEmpty()){
|
||||
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
|
||||
}
|
||||
if(instances.isEmpty()){
|
||||
instances=data.stream().filter(ci->("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
|
||||
}
|
||||
if(instances.isEmpty()){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ public class InstanceChooserLoginFragment extends InstanceCatalogFragment{
|
||||
.execNoAuth("");
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void onUpdateToolbar(){
|
||||
// super.onUpdateToolbar();
|
||||
// Toolbar toolbar=getToolbar();
|
||||
// toolbar.setElevation(0);
|
||||
// toolbar.setBackground(null);
|
||||
// }
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
Toolbar toolbar=getToolbar();
|
||||
toolbar.setElevation(0);
|
||||
toolbar.setBackground(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
|
||||
@@ -2,8 +2,14 @@ package org.joinmastodon.android.fragments.onboarding;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -64,13 +70,13 @@ public class InstanceRulesFragment extends ToolbarFragment{
|
||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
|
||||
TextView text=headerView.findViewById(R.id.text);
|
||||
text.setText(getString(R.string.instance_rules_subtitle, instance.uri));
|
||||
text.setText(Html.fromHtml(getString(R.string.instance_rules_subtitle, "<b>"+Html.escapeHtml(instance.uri)+"</b>")));
|
||||
|
||||
adapter=new MergeRecyclerAdapter();
|
||||
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
adapter.addAdapter(new ItemsAdapter());
|
||||
list.setAdapter(adapter);
|
||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
|
||||
|
||||
btn=view.findViewById(R.id.btn_next);
|
||||
btn.setOnClickListener(v->onButtonClick());
|
||||
@@ -84,8 +90,8 @@ public class InstanceRulesFragment extends ToolbarFragment{
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
// setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
// view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||
import org.joinmastodon.android.fragments.HomeFragment;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.RecyclerFragment;
|
||||
import org.joinmastodon.android.model.FollowSuggestion;
|
||||
import org.joinmastodon.android.model.ParsedAccount;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
@@ -43,7 +44,6 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -52,7 +52,7 @@ import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<ParsedAccount>{
|
||||
public class OnboardingFollowSuggestionsFragment extends RecyclerFragment<ParsedAccount> {
|
||||
private String accountID;
|
||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||
private GetAccountRelationships relationshipsRequest;
|
||||
@@ -191,6 +191,13 @@ public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<Pa
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Relationship result){
|
||||
relationships.put(id, result);
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
if(list.getChildViewHolder(list.getChildAt(i)) instanceof SuggestionViewHolder svh && svh.getItem().account.id.equals(id)){
|
||||
svh.rebind();
|
||||
break;
|
||||
}
|
||||
}
|
||||
numRunningFollowRequests--;
|
||||
progress.setProgress(progress.getMax()-accountIdsToFollow.size()-numRunningFollowRequests);
|
||||
followNextAccount(accountIdsToFollow, progress);
|
||||
@@ -209,20 +216,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseRecyclerFragment<Pa
|
||||
private void proceed(){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), HomeFragment.class, args);
|
||||
getActivity().getWindow().getDecorView().postDelayed(()->Nav.finish(this), 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canGoBack(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolbarNavigationClick(){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||
Nav.go(getActivity(), OnboardingProfileSetupFragment.class, args);
|
||||
}
|
||||
|
||||
private class SuggestionsAdapter extends UsableRecyclerView.Adapter<SuggestionViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||
|
||||
@@ -151,8 +151,7 @@ public class OnboardingProfileSetupFragment extends ToolbarFragment implements R
|
||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, result);
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.go(getActivity(), OnboardingFollowSuggestionsFragment.class, args);
|
||||
getActivity().getWindow().getDecorView().postDelayed(()->Nav.finish(OnboardingProfileSetupFragment.this), 500);
|
||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,6 +181,11 @@ public class OnboardingProfileSetupFragment extends ToolbarFragment implements R
|
||||
profileFieldsLayout.startDragging(view);
|
||||
return true;
|
||||
});
|
||||
view.findViewById(R.id.delete).setOnClickListener(v->{
|
||||
profileFieldsLayout.removeView(view);
|
||||
if(addRow.getVisibility()==View.GONE)
|
||||
addRow.setVisibility(View.VISIBLE);
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -222,16 +226,4 @@ public class OnboardingProfileSetupFragment extends ToolbarFragment implements R
|
||||
img.setForeground(null);
|
||||
ViewImageLoader.load(img, null, new UrlImageLoaderRequest(uri, size, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canGoBack(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolbarNavigationClick(){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
Nav.goClearingStack(getActivity(), HomeFragment.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user