Compare commits
854 Commits
v1.2.3+for
...
feature/di
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
104423b5e8 | ||
|
|
521c742d1a | ||
|
|
135a98224f | ||
|
|
bed71fd9e0 | ||
|
|
c695bb6aa3 | ||
|
|
4aa750c05e | ||
|
|
c3e5f4d254 | ||
|
|
2e5ff452fd | ||
|
|
c397c08e40 | ||
|
|
a4d2101f54 | ||
|
|
f956a17797 | ||
|
|
1c1d1772a3 | ||
|
|
4db87feec4 | ||
|
|
bef3c72513 | ||
|
|
4fa641b482 | ||
|
|
885b5d781a | ||
|
|
2f3bfb3e74 | ||
|
|
2be625fd76 | ||
|
|
134a371263 | ||
|
|
8b0eddb8e1 | ||
|
|
bd2d56b953 | ||
|
|
38e429f738 | ||
|
|
de8b15d447 | ||
|
|
0df1bcce31 | ||
|
|
4e17256cfa | ||
|
|
e12c3e2d68 | ||
|
|
aec2704f15 | ||
|
|
31f9173126 | ||
|
|
90196df65d | ||
|
|
6b9fa71806 | ||
|
|
130085f804 | ||
|
|
f4356e74a4 | ||
|
|
9c8a4b7a8e | ||
|
|
b7ccf1144c | ||
|
|
87d5b92a99 | ||
|
|
29f8260852 | ||
|
|
060745869b | ||
|
|
1aff3eacd8 | ||
|
|
0207ddb774 | ||
|
|
78d0add808 | ||
|
|
2fa042490a | ||
|
|
885f559092 | ||
|
|
77af7ceae3 | ||
|
|
09d4188d54 | ||
|
|
1ad03828e3 | ||
|
|
870ac2b946 | ||
|
|
394a3eebb1 | ||
|
|
95c10a9fea | ||
|
|
f0e14c5a13 | ||
|
|
616049bff2 | ||
|
|
1a79bc0b61 | ||
|
|
d43cbe642f | ||
|
|
77cee4c46a | ||
|
|
0949ad1ce6 | ||
|
|
1e411c0c23 | ||
|
|
76d306aef7 | ||
|
|
7ff19ef481 | ||
|
|
6650bb946f | ||
|
|
bbbf1683aa | ||
|
|
5cdea99eb0 | ||
|
|
356426b5fc | ||
|
|
7577d60f42 | ||
|
|
8c7364d57d | ||
|
|
c0a2945378 | ||
|
|
1af9a71210 | ||
|
|
dc859fe91c | ||
|
|
d50c37af23 | ||
|
|
00c8a03b80 | ||
|
|
d15d222b72 | ||
|
|
34d134cb57 | ||
|
|
40eb3e2400 | ||
|
|
0af45e5f56 | ||
|
|
82e3250623 | ||
|
|
533a51fc77 | ||
|
|
6fc82cf26b | ||
|
|
6dcfdb9735 | ||
|
|
217d8348d4 | ||
|
|
9bce934944 | ||
|
|
a1ef3e1cae | ||
|
|
978c1cfdc4 | ||
|
|
9fb9ffc269 | ||
|
|
4f357637de | ||
|
|
1acd177e81 | ||
|
|
e4b1bf452f | ||
|
|
ce22cb4678 | ||
|
|
3c45215fca | ||
|
|
b17e63acae | ||
|
|
859ba5ebb9 | ||
|
|
164579cbb5 | ||
|
|
990f8189e4 | ||
|
|
4d0a642fd9 | ||
|
|
84b2994b99 | ||
|
|
efbca327c1 | ||
|
|
aa4b007d25 | ||
|
|
23dccef4b4 | ||
|
|
1ef96ed5e6 | ||
|
|
31e2a32233 | ||
|
|
168ae80743 | ||
|
|
8acf23ddac | ||
|
|
eb40211582 | ||
|
|
bf35161c9f | ||
|
|
456c50f69e | ||
|
|
587212cf46 | ||
|
|
fe800a259d | ||
|
|
c193741013 | ||
|
|
a7b752264f | ||
|
|
9075027f69 | ||
|
|
28faf4277a | ||
|
|
120ab8ca54 | ||
|
|
a3fc1a6a74 | ||
|
|
ec1fe07fea | ||
|
|
e3d054ae3e | ||
|
|
795d4b0801 | ||
|
|
0cf0f07f2d | ||
|
|
2cc5872ec7 | ||
|
|
6a151e00ac | ||
|
|
1e99862d40 | ||
|
|
3a1b12306b | ||
|
|
e31db6d506 | ||
|
|
b22c0a5d3d | ||
|
|
e7d856acf4 | ||
|
|
ee8b087b61 | ||
|
|
4e86314df5 | ||
|
|
cf5fbe3b55 | ||
|
|
952416fefc | ||
|
|
1645ce4486 | ||
|
|
2bde95b4d2 | ||
|
|
2de003c5bb | ||
|
|
a04e16c572 | ||
|
|
eb41d77d54 | ||
|
|
9325590319 | ||
|
|
d32a57a18d | ||
|
|
13b5462f63 | ||
|
|
480d4ad904 | ||
|
|
7fa52247e8 | ||
|
|
5a547015e6 | ||
|
|
513736f765 | ||
|
|
c6164b1bcd | ||
|
|
87342782d7 | ||
|
|
a4b18de72c | ||
|
|
3064b549cd | ||
|
|
6666f82329 | ||
|
|
ad87efa7e2 | ||
|
|
d06cf1bb1e | ||
|
|
096aa23f69 | ||
|
|
2464042329 | ||
|
|
928b04eda6 | ||
|
|
a31e33415e | ||
|
|
87ce6b8bb1 | ||
|
|
bb08d6585c | ||
|
|
94fa1133fd | ||
|
|
83822b8f69 | ||
|
|
9f3bd186ba | ||
|
|
58cb338cb2 | ||
|
|
6f447909eb | ||
|
|
af953e294d | ||
|
|
7504a1b9cb | ||
|
|
60ee781004 | ||
|
|
4b88ce5115 | ||
|
|
3842ecb0d1 | ||
|
|
3713063ce3 | ||
|
|
83b089457e | ||
|
|
ed9813f093 | ||
|
|
e45f3f30f3 | ||
|
|
df44d4cc4f | ||
|
|
b666048603 | ||
|
|
153542e1b4 | ||
|
|
57e0b96f36 | ||
|
|
ff65d150e3 | ||
|
|
88474ba826 | ||
|
|
dd92f1b66f | ||
|
|
7c7f3cc42a | ||
|
|
734a8049a5 | ||
|
|
417faa66f9 | ||
|
|
7223a13d08 | ||
|
|
a55002da0c | ||
|
|
ce89733f2d | ||
|
|
18811ec32a | ||
|
|
e65e6163ba | ||
|
|
9c3db24d2f | ||
|
|
19abbe199b | ||
|
|
b33003f7b0 | ||
|
|
9a5747efc8 | ||
|
|
980503ed57 | ||
|
|
c2dd858de8 | ||
|
|
d2ef6fb567 | ||
|
|
9c996b3568 | ||
|
|
2387d84bc0 | ||
|
|
3bd69b5447 | ||
|
|
71f6311598 | ||
|
|
e808977717 | ||
|
|
8594e34bb5 | ||
|
|
4591f06d63 | ||
|
|
b9c3143c6f | ||
|
|
adefb0e567 | ||
|
|
5a8fed3c06 | ||
|
|
2c1b8da475 | ||
|
|
707c51e4d6 | ||
|
|
1a075e32de | ||
|
|
73869b6ea2 | ||
|
|
3980329112 | ||
|
|
6107b21d3b | ||
|
|
42a0b881af | ||
|
|
2076818220 | ||
|
|
513bea7959 | ||
|
|
a48dc18df5 | ||
|
|
c55639e966 | ||
|
|
1fc89d7448 | ||
|
|
4fcc07fcd6 | ||
|
|
b50b1c33da | ||
|
|
25460191b0 | ||
|
|
1206ce6efc | ||
|
|
3cdcffe03d | ||
|
|
b09aab4a1d | ||
|
|
d27fe4ebd1 | ||
|
|
ab2cee08fe | ||
|
|
1fa42fd20f | ||
|
|
e343131670 | ||
|
|
f157313d9a | ||
|
|
9bd4433942 | ||
|
|
c9cc8e23c1 | ||
|
|
786dbaf92c | ||
|
|
8dd22e1853 | ||
|
|
34a4dd6d1f | ||
|
|
6e13e592d0 | ||
|
|
485ab4ee22 | ||
|
|
59a8d1d462 | ||
|
|
f4e5baf94d | ||
|
|
a782160dd3 | ||
|
|
871c17cbe2 | ||
|
|
047e72ce9c | ||
|
|
6c1424055f | ||
|
|
369902ffe5 | ||
|
|
747439999d | ||
|
|
4d836f8032 | ||
|
|
f97ab73c5d | ||
|
|
147fb94442 | ||
|
|
975dc94d41 | ||
|
|
a5c1053c58 | ||
|
|
1bfbb4bf38 | ||
|
|
c2950ace90 | ||
|
|
f0846465c2 | ||
|
|
ddc4512116 | ||
|
|
1c9e4fe561 | ||
|
|
35299a7b3f | ||
|
|
3a1b71e95c | ||
|
|
54ec1a6cf7 | ||
|
|
bed3e987b7 | ||
|
|
639ddb3f80 | ||
|
|
e645abb771 | ||
|
|
762adce054 | ||
|
|
263bde658e | ||
|
|
3951acf12e | ||
|
|
5cb640a387 | ||
|
|
c65b9ff873 | ||
|
|
13a80fb536 | ||
|
|
23e49c52e5 | ||
|
|
9fcc73984b | ||
|
|
67338b6c85 | ||
|
|
101e7efd74 | ||
|
|
a996a24b7f | ||
|
|
44dcc9fe2b | ||
|
|
58f79e06ef | ||
|
|
f197c8201d | ||
|
|
2fc5669203 | ||
|
|
0065b93060 | ||
|
|
3f9dbd6fe2 | ||
|
|
7ceff3eaa4 | ||
|
|
7cdddf06bc | ||
|
|
77e7b136ff | ||
|
|
c8d160fc35 | ||
|
|
ee0737c9c7 | ||
|
|
0f85be7114 | ||
|
|
34ba4ceb16 | ||
|
|
f2a536d0ea | ||
|
|
815c4d4cc9 | ||
|
|
969b91bba9 | ||
|
|
f24abde76e | ||
|
|
f486b1a9ce | ||
|
|
3dd6638ef3 | ||
|
|
bcfb63b57c | ||
|
|
c7f5f6827a | ||
|
|
8ed88b2b29 | ||
|
|
75db9f4623 | ||
|
|
b69015a25a | ||
|
|
1cdcc8794c | ||
|
|
a0c26b748a | ||
|
|
ce5e733c05 | ||
|
|
de485272c5 | ||
|
|
eab53b805e | ||
|
|
a60046f6ef | ||
|
|
b0d223c47c | ||
|
|
185a8c776b | ||
|
|
f510ee3b4d | ||
|
|
e7a29824e8 | ||
|
|
69b86dd98c | ||
|
|
55807dc7c6 | ||
|
|
9a6ee719c4 | ||
|
|
1b02af382c | ||
|
|
4fe87a9888 | ||
|
|
91995155e9 | ||
|
|
aae0ff5aa7 | ||
|
|
821d9b8a5e | ||
|
|
2070aed38f | ||
|
|
986979eefc | ||
|
|
ed3ce54b24 | ||
|
|
c80ebf2eda | ||
|
|
42fcd6df51 | ||
|
|
1f4031da61 | ||
|
|
d0ebee74ca | ||
|
|
e8ec042d96 | ||
|
|
bd61bf32b6 | ||
|
|
8b12fac766 | ||
|
|
ab7e6b3332 | ||
|
|
548da48615 | ||
|
|
0b6128bcdd | ||
|
|
198a7d5ad3 | ||
|
|
040f244e15 | ||
|
|
98bc6f14a9 | ||
|
|
b92e6d2c48 | ||
|
|
ac3875fe08 | ||
|
|
6fdbafc67b | ||
|
|
5aebdcaa6a | ||
|
|
2969a3e4fd | ||
|
|
e1fcf44aa6 | ||
|
|
ddbfe9de57 | ||
|
|
b2ee1527af | ||
|
|
c94706745f | ||
|
|
8aef61ff09 | ||
|
|
5d6e245ec2 | ||
|
|
8dd4d1a41d | ||
|
|
10f9230139 | ||
|
|
32c94c8948 | ||
|
|
4b947dd1f9 | ||
|
|
93a7d86f78 | ||
|
|
6ed3a57c58 | ||
|
|
ecdba3898f | ||
|
|
d82a9b25cf | ||
|
|
da4849e526 | ||
|
|
da1b47ea0d | ||
|
|
a617693f93 | ||
|
|
f4c573a95e | ||
|
|
d3476c1473 | ||
|
|
d58247d996 | ||
|
|
ee441d5b4a | ||
|
|
97a5c6f5cb | ||
|
|
b6566a2bcc | ||
|
|
af389f7a47 | ||
|
|
45577fc423 | ||
|
|
c00b9b3035 | ||
|
|
f17879783c | ||
|
|
08dfcdf508 | ||
|
|
c97e8fffc4 | ||
|
|
120585954f | ||
|
|
247669644c | ||
|
|
ff15bdeaea | ||
|
|
8b67320e20 | ||
|
|
48828ed1b7 | ||
|
|
1e615db77e | ||
|
|
629416ef2f | ||
|
|
d6b1c88085 | ||
|
|
3b7079be17 | ||
|
|
48d26017b6 | ||
|
|
8bdbe1c4d7 | ||
|
|
4093443bf8 | ||
|
|
c6709aba56 | ||
|
|
a817e03fa0 | ||
|
|
a757607f35 | ||
|
|
ecc9c45f6e | ||
|
|
2e157a9e68 | ||
|
|
8a39154bd3 | ||
|
|
b5e2aa8b7f | ||
|
|
aaea709201 | ||
|
|
c2a0f5e8bc | ||
|
|
2873a66450 | ||
|
|
7331a5a0cd | ||
|
|
c331cc95c2 | ||
|
|
91aa425f43 | ||
|
|
fd5165428e | ||
|
|
6df57aebf4 | ||
|
|
40b2e4c42e | ||
|
|
a762408df8 | ||
|
|
9fb6a0261b | ||
|
|
d90edc8cf1 | ||
|
|
eecc1242be | ||
|
|
6eedb0e156 | ||
|
|
519894a461 | ||
|
|
fe3052a359 | ||
|
|
245b91bebd | ||
|
|
2e47147367 | ||
|
|
8fec3fe56c | ||
|
|
c4d56179f3 | ||
|
|
64d24f6002 | ||
|
|
074efb0813 | ||
|
|
98b96c78d7 | ||
|
|
3a962c7c05 | ||
|
|
99e3658938 | ||
|
|
9cd7ad3601 | ||
|
|
96db2b7a8b | ||
|
|
a881f23253 | ||
|
|
54106c497b | ||
|
|
1554c6d422 | ||
|
|
dccd9dcb97 | ||
|
|
16c0866f7f | ||
|
|
db88de206b | ||
|
|
92f353e1c5 | ||
|
|
71c80dd381 | ||
|
|
d2be917bd4 | ||
|
|
ea3dc32e98 | ||
|
|
be86f1e96f | ||
|
|
d626d45f5c | ||
|
|
f007bdb39c | ||
|
|
a0cbfe9a36 | ||
|
|
21f99081f2 | ||
|
|
45952ef143 | ||
|
|
fedaaa6fc8 | ||
|
|
1eb08e40be | ||
|
|
6a4936853b | ||
|
|
b277eb4990 | ||
|
|
74efc5c332 | ||
|
|
6653dd97a6 | ||
|
|
d031acabf5 | ||
|
|
852f666b78 | ||
|
|
9f729e9ef0 | ||
|
|
027f19e710 | ||
|
|
14786f796b | ||
|
|
411e39a096 | ||
|
|
8a72c5b9ca | ||
|
|
9e88e28a59 | ||
|
|
df73c5ad8d | ||
|
|
98707dde76 | ||
|
|
444a9afabe | ||
|
|
315bcd5b1a | ||
|
|
c719ac22da | ||
|
|
f4596998aa | ||
|
|
5414f5cf41 | ||
|
|
d919827bc8 | ||
|
|
7b40bc157d | ||
|
|
c3ec64d1ff | ||
|
|
3464cb4a05 | ||
|
|
21f40e5b42 | ||
|
|
6d8ecab766 | ||
|
|
fc2e5112c0 | ||
|
|
8f6c57d5c9 | ||
|
|
8455dc7bd2 | ||
|
|
4677cef580 | ||
|
|
9ab6bf3da1 | ||
|
|
bc65fa6654 | ||
|
|
9fe5960449 | ||
|
|
b684dd5a3b | ||
|
|
5fff0cbdb0 | ||
|
|
99dc479d78 | ||
|
|
275eef912c | ||
|
|
b9de7632e6 | ||
|
|
7a000f2a44 | ||
|
|
9d91b8c0fb | ||
|
|
84d213bdd1 | ||
|
|
a2302ad318 | ||
|
|
42eacea4be | ||
|
|
664bfe895e | ||
|
|
9c1812ce08 | ||
|
|
49c83581f9 | ||
|
|
6aa0428879 | ||
|
|
838d0c678b | ||
|
|
35555d1362 | ||
|
|
6c6b5d7be9 | ||
|
|
037b8cc54e | ||
|
|
afb518cf8e | ||
|
|
b36c7375dd | ||
|
|
069356418d | ||
|
|
6b1c725ebd | ||
|
|
a46f8f1c20 | ||
|
|
3b3d98c4be | ||
|
|
3d4742e8f7 | ||
|
|
a6f1c21e1c | ||
|
|
a0b62ab434 | ||
|
|
0b8501e92b | ||
|
|
9a0851cb06 | ||
|
|
e4340f5015 | ||
|
|
0cd5d12d42 | ||
|
|
014398e050 | ||
|
|
4f77370977 | ||
|
|
5937215d3a | ||
|
|
ade1ce8e05 | ||
|
|
a4581dc61b | ||
|
|
50d4130b3f | ||
|
|
1147087531 | ||
|
|
4653a22635 | ||
|
|
e79501857f | ||
|
|
a8c05f6a32 | ||
|
|
c1d98cad00 | ||
|
|
fb54948f86 | ||
|
|
26297fbb5b | ||
|
|
cd342d1034 | ||
|
|
029650ef2d | ||
|
|
ca5f189e70 | ||
|
|
ac24f636df | ||
|
|
1688168bc1 | ||
|
|
46b842afc4 | ||
|
|
3f773a52cc | ||
|
|
48664bb580 | ||
|
|
094cd67728 | ||
|
|
a70bd4f906 | ||
|
|
4adac359e3 | ||
|
|
9adaf12c00 | ||
|
|
8bbfa2e417 | ||
|
|
9d800106cc | ||
|
|
e1ca97f323 | ||
|
|
68bb23e3b4 | ||
|
|
68397bd487 | ||
|
|
3104ddb4b6 | ||
|
|
9bddd6b274 | ||
|
|
677621f2da | ||
|
|
d40138dd99 | ||
|
|
d957e8f2fc | ||
|
|
681c327306 | ||
|
|
80c9afec7b | ||
|
|
eea8041abe | ||
|
|
1309bfe1ee | ||
|
|
70d3ef9984 | ||
|
|
52ac5f16e5 | ||
|
|
ea43070e6d | ||
|
|
14cd23c28b | ||
|
|
793668021e | ||
|
|
f0ea6ef43e | ||
|
|
2b2e4845a1 | ||
|
|
2dccec99cc | ||
|
|
b060894a6c | ||
|
|
92872edb58 | ||
|
|
c5fcf49eda | ||
|
|
d66a4c0920 | ||
|
|
e8a31cf867 | ||
|
|
c1f9a88ef4 | ||
|
|
e6200e186b | ||
|
|
5564502125 | ||
|
|
7947e7689c | ||
|
|
b53ada7ea2 | ||
|
|
584f28534a | ||
|
|
770fde7aac | ||
|
|
3d7f918132 | ||
|
|
29b8cedc7c | ||
|
|
33b65c3bf3 | ||
|
|
34ab1bcd9c | ||
|
|
cfdc88174b | ||
|
|
15123d8924 | ||
|
|
20086d76ce | ||
|
|
1ca4fb5c37 | ||
|
|
009016a835 | ||
|
|
33dfb2a30d | ||
|
|
1604c067fd | ||
|
|
0c7419e2b3 | ||
|
|
7cf30ccb98 | ||
|
|
aa2c8c5624 | ||
|
|
875695c239 | ||
|
|
61049a1302 | ||
|
|
28db90aa82 | ||
|
|
f0e7fc5e3b | ||
|
|
2169afa8e7 | ||
|
|
508ec06d93 | ||
|
|
9fb39d9403 | ||
|
|
4879d74f80 | ||
|
|
ba3f6c4f95 | ||
|
|
481241c4f6 | ||
|
|
5798587dc6 | ||
|
|
066e3e08a2 | ||
|
|
16d6c14633 | ||
|
|
80a4a3551c | ||
|
|
74f3bb8708 | ||
|
|
c3e398b3c2 | ||
|
|
dcfa812c83 | ||
|
|
065e65d708 | ||
|
|
bca0dab381 | ||
|
|
4a45c1055e | ||
|
|
7c789746ce | ||
|
|
f46ce5576c | ||
|
|
730e6fc1fa | ||
|
|
cb36cc042c | ||
|
|
5d586418f9 | ||
|
|
44f1d026d6 | ||
|
|
defaa1095c | ||
|
|
c67f2f8027 | ||
|
|
452128565f | ||
|
|
6322d3c984 | ||
|
|
d68c820e58 | ||
|
|
79f37b4813 | ||
|
|
87460a2fb6 | ||
|
|
6774a642d9 | ||
|
|
f0bd9233b7 | ||
|
|
66efe750a8 | ||
|
|
3658fc423b | ||
|
|
3c3ad7447e | ||
|
|
b570064b99 | ||
|
|
d9f6ef69fe | ||
|
|
bdac1d5bb4 | ||
|
|
1eee1ead5e | ||
|
|
0ec51f5b34 | ||
|
|
6c6fb05a7a | ||
|
|
09a0faacba | ||
|
|
d0d1d15de5 | ||
|
|
e4e9516d5d | ||
|
|
05eceecbea | ||
|
|
71ba1bb0d5 | ||
|
|
2160a26648 | ||
|
|
5433eac9c9 | ||
|
|
0a68f86200 | ||
|
|
c91dae0346 | ||
|
|
e1df7e5077 | ||
|
|
0560b54559 | ||
|
|
c78db7e835 | ||
|
|
c837a2d4b6 | ||
|
|
70b91b7a9a | ||
|
|
27079a7ec5 | ||
|
|
9563df0574 | ||
|
|
638209cc13 | ||
|
|
224c731afa | ||
|
|
0bbf937531 | ||
|
|
3556c92c3e | ||
|
|
87c5b23196 | ||
|
|
c83910c885 | ||
|
|
586622e90d | ||
|
|
e5e2430e03 | ||
|
|
04bfdba50e | ||
|
|
7abf15e9e0 | ||
|
|
6b680831b8 | ||
|
|
6cbf100828 | ||
|
|
3e7bbebe7f | ||
|
|
56d344045a | ||
|
|
7ab634cc08 | ||
|
|
9f0db3ebb5 | ||
|
|
6415eb8590 | ||
|
|
87c77b84a4 | ||
|
|
0b7bb16f22 | ||
|
|
5164b5ba78 | ||
|
|
f3c28bc66a | ||
|
|
239f7eb9e7 | ||
|
|
d6daf7a553 | ||
|
|
dfb3b230e6 | ||
|
|
484a5c878f | ||
|
|
3f27cfb13b | ||
|
|
38e2ba6ccd | ||
|
|
3dad38e614 | ||
|
|
0865c9d1bd | ||
|
|
20a8783d84 | ||
|
|
0b96fb05fc | ||
|
|
8767d62de7 | ||
|
|
74fb04e2d4 | ||
|
|
2537460e16 | ||
|
|
be3dfde3be | ||
|
|
42025035ad | ||
|
|
6a667fdf32 | ||
|
|
bfafac3d4f | ||
|
|
0cafbe9f91 | ||
|
|
2fbf172729 | ||
|
|
bb9755f4af | ||
|
|
2a01377a8a | ||
|
|
61cc6d5d07 | ||
|
|
1d74a37f60 | ||
|
|
ef9645f9e7 | ||
|
|
6a103ca3f3 | ||
|
|
c22772121b | ||
|
|
de7bc69d2a | ||
|
|
2eccd572c9 | ||
|
|
824a62024b | ||
|
|
3a3cfda919 | ||
|
|
e29120cc51 | ||
|
|
197d5c6bc3 | ||
|
|
d143cc75db | ||
|
|
1635a06c54 | ||
|
|
76de0d8c70 | ||
|
|
402a995b8f | ||
|
|
f580ba7779 | ||
|
|
bc3869b920 | ||
|
|
020f4a5a1a | ||
|
|
b054caa967 | ||
|
|
82b7633650 | ||
|
|
33497864f2 | ||
|
|
4c9d1544fa | ||
|
|
bce2367cfc | ||
|
|
390ecc48fb | ||
|
|
9ed99edd6e | ||
|
|
4362490539 | ||
|
|
f5d225fc3e | ||
|
|
063e9287fd | ||
|
|
ba376908cd | ||
|
|
caddf0021c | ||
|
|
90645f4d90 | ||
|
|
1316fcae22 | ||
|
|
27dee7297b | ||
|
|
13ecba40ae | ||
|
|
e15dd0d8b3 | ||
|
|
1ab26bc665 | ||
|
|
e6758d8c01 | ||
|
|
4621787e34 | ||
|
|
10ad35a285 | ||
|
|
d10145a6ba | ||
|
|
c9792ced32 | ||
|
|
a3fb09a33c | ||
|
|
6d875fd890 | ||
|
|
5d87fb7b67 | ||
|
|
4cbb59850b | ||
|
|
a2022b25e5 | ||
|
|
0d168f93ed | ||
|
|
94ac5b9bb7 | ||
|
|
024d358213 | ||
|
|
5562c93855 | ||
|
|
98e897d6a8 | ||
|
|
4aac6aa4f4 | ||
|
|
2bb4616e40 | ||
|
|
56e8476d2e | ||
|
|
97d81eb1b2 | ||
|
|
ffa21b26af | ||
|
|
9917712f66 | ||
|
|
11cdce6c90 | ||
|
|
8e82cf1e99 | ||
|
|
9767b11626 | ||
|
|
0f95694083 | ||
|
|
7dfc7dd9ef | ||
|
|
0407e958f1 | ||
|
|
e6a5fa1c3f | ||
|
|
6f48a7c4a4 | ||
|
|
80c56d71cb | ||
|
|
f77d9dcee2 | ||
|
|
f7195c7787 | ||
|
|
ca92cc6dc1 | ||
|
|
cd31b2ae5a | ||
|
|
00bec7174a | ||
|
|
236acab54f | ||
|
|
ba362f4457 | ||
|
|
8ed93baf8d | ||
|
|
bf953e96fa | ||
|
|
6b89a747e2 | ||
|
|
2fa1d54268 | ||
|
|
02ef34b451 | ||
|
|
1701fc71c4 | ||
|
|
fe200996db | ||
|
|
659333342f | ||
|
|
1ca5b6def2 | ||
|
|
4e8e3ee440 | ||
|
|
86dd724222 | ||
|
|
8242995027 | ||
|
|
49962a4734 | ||
|
|
509b16aee1 | ||
|
|
f3f5e4a887 | ||
|
|
7aabc1fa76 | ||
|
|
dcb5e36041 | ||
|
|
e0c072ab9c | ||
|
|
0231903868 | ||
|
|
f63bbeee79 | ||
|
|
db9e427444 | ||
|
|
4474a584df | ||
|
|
ab00ad68f1 | ||
|
|
d1e77efa1c | ||
|
|
de00353864 | ||
|
|
feec459d47 | ||
|
|
ad68d7e4f2 | ||
|
|
cf27c6bbf3 | ||
|
|
0115656d67 | ||
|
|
002687d2b1 | ||
|
|
a3267f6cd3 | ||
|
|
0ca9c536cd | ||
|
|
382a23c0b6 | ||
|
|
1f51331f67 | ||
|
|
cce6ba0746 | ||
|
|
be3c12dfb3 | ||
|
|
bfd87cf94e | ||
|
|
857bb1e483 | ||
|
|
75a131b675 | ||
|
|
d98b1c5ee1 | ||
|
|
1eeab25b7d | ||
|
|
82cc0c3c09 | ||
|
|
e102faff6c | ||
|
|
34369bd7e9 | ||
|
|
c71b620402 | ||
|
|
21b4bf23a1 | ||
|
|
d034311f2d | ||
|
|
2deed69766 | ||
|
|
bfbd21b826 | ||
|
|
ba8683301d | ||
|
|
0ed178167b | ||
|
|
b34e34de51 | ||
|
|
ba38e21e07 | ||
|
|
90bef7fddb | ||
|
|
c1b382ef34 | ||
|
|
028b88aa24 | ||
|
|
9d0ce33f5e | ||
|
|
dbb23d952c | ||
|
|
7fe7e47d53 | ||
|
|
d0c93dfd4d | ||
|
|
acdccaf80a | ||
|
|
769293ce1a | ||
|
|
8d0fe18b70 | ||
|
|
6926432a6c | ||
|
|
83f12b0840 | ||
|
|
290b7db7e4 | ||
|
|
f352c20ed9 | ||
|
|
2ccbffa165 | ||
|
|
06cd80a352 | ||
|
|
de97493e6a | ||
|
|
3a24ff0d15 | ||
|
|
c463a3fc39 | ||
|
|
fc845685cc | ||
|
|
0ef0aa1a44 | ||
|
|
337689aa45 | ||
|
|
f7e3423f9c | ||
|
|
b465c09cc8 | ||
|
|
ac6c0651d6 | ||
|
|
18af6f5a12 | ||
|
|
d11ee3a702 | ||
|
|
6d9f9ce2d2 | ||
|
|
ec1496a4cc | ||
|
|
41e19185e8 | ||
|
|
e15dd6024f | ||
|
|
e52dffeece | ||
|
|
5b85bb427d | ||
|
|
4d62388617 | ||
|
|
04b8055474 | ||
|
|
3c34b6a7d2 | ||
|
|
de4964c2cd | ||
|
|
fbcaa05c03 | ||
|
|
883f28696e | ||
|
|
df52230837 | ||
|
|
a90f26a37a | ||
|
|
8c1f76d7fa | ||
|
|
f384d44f8f | ||
|
|
4ab6ed55f5 | ||
|
|
cf99bf5152 | ||
|
|
10779717cf | ||
|
|
4e5c2a9ecf | ||
|
|
db4c1bfe47 | ||
|
|
27afba1cf2 | ||
|
|
4895425b40 | ||
|
|
004c414fba | ||
|
|
c8e38b134c | ||
|
|
de5a911286 | ||
|
|
606cd7442e | ||
|
|
3ebc972268 | ||
|
|
4e39bb381c | ||
|
|
b6178681b0 | ||
|
|
29abf70cec | ||
|
|
8d63be513d | ||
|
|
e63b9d0dd6 | ||
|
|
b1fda17ac7 | ||
|
|
bad44b145c | ||
|
|
77669cedf6 | ||
|
|
19238c389f | ||
|
|
1747ff98b5 | ||
|
|
8fa5824e3e | ||
|
|
6a674d7a7e | ||
|
|
dad3b8cd6b | ||
|
|
9179d2198d | ||
|
|
d096bef234 | ||
|
|
f2c47a1b84 | ||
|
|
bc2ac4e915 | ||
|
|
ff215412c8 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,9 +1,8 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: LucasGGamerM
|
||||||
patreon: # mastodon
|
patreon: # mastodon
|
||||||
open_collective: # Replace with a single Open Collective username e.g., user1
|
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
|
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
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
liberapay: # Replace with a single Liberapay username e.g., user1
|
liberapay: # Replace with a single Liberapay username e.g., user1
|
||||||
|
|||||||
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -8,35 +8,25 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To reproduce**
|
**To Reproduce**
|
||||||
|
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**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.
|
|
||||||
> If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
|
|
||||||
|
|
||||||
**Screenshots and screen recordings**
|
**Screenshots and screen recordings**
|
||||||
|
|
||||||
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
|
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
|
||||||
|
|
||||||
**Version**
|
**Version**
|
||||||
|
|
||||||
Megalodon version: [e.g. v1.1.4+fork.#]
|
Megalodon version: [e.g. v1.1.4+fork.#]
|
||||||
|
|
||||||
**Crash log**
|
**Additional context**
|
||||||
|
- 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)) No / Yes (`mastodon#…`)
|
||||||
|
|
||||||
|
> In this case, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead. If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
|
||||||
|
|
||||||
|
**Crash log**
|
||||||
If you know your way around Android development tools, please consider attaching a crash log, if possible.
|
If you know your way around Android development tools, please consider attaching a crash log, if possible.
|
||||||
|
|||||||
11
.github/workflows/validate-gradle-wrapper.yml
vendored
11
.github/workflows/validate-gradle-wrapper.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
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
|
|
||||||
150
README.md
150
README.md
@@ -1,25 +1,37 @@
|
|||||||

|

|
||||||
|
|
||||||
# Megalodon
|
# Moshidon, the material you mastodon client!
|
||||||
|
|
||||||
[](https://translate.codeberg.org/engage/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://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>
|
|
||||||
|
|
||||||
> 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.
|
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
|
|
||||||
|
### **Material you theme support on Android 12+ devices!**
|
||||||
|
|
||||||
|
### **Translate button**
|
||||||
|
|
||||||
|
**Allows you to translate posts in instances with the translate feature!**
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
### **Color themes**
|
||||||
|
|
||||||
|
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
||||||
|
|
||||||
### **Unlisted posting**
|
### **Unlisted posting**
|
||||||
|
|
||||||
**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”).**
|
**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”).**
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
@@ -31,12 +43,6 @@ 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!
|
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**
|
### **Image description viewer**
|
||||||
|
|
||||||
**Allows you to quickly check whether an image or video has an alternative text attached to it.**
|
**Allows you to quickly check whether an image or video has an alternative text attached to it.**
|
||||||
@@ -49,71 +55,29 @@ 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.
|
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
|
## Installation
|
||||||
|
|
||||||
### IzzyOnDroid
|
**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.**
|
||||||
|
|
||||||
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
|
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.
|
||||||
|
|
||||||
<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>
|
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!
|
||||||
|
|
||||||
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!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Release variants
|
## Release variants
|
||||||
|
|
||||||
All downloads can be found on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
All downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||||
|
|
||||||
**`megalodon.apk`**
|
**`moshidon.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`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -131,7 +95,7 @@ There's also a handful of custom strings exclusive to this projects that would n
|
|||||||
* [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))
|
* [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 “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 “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 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))
|
* [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))
|
||||||
* [Follow and unfollow hashtags](https://github.com/sk22/megalodon/commit/7d38f031f197aa6cefaf53e39d929538689c1e4e) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/233))
|
* [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))
|
* [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)
|
* [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)
|
||||||
@@ -141,19 +105,7 @@ There's also a handful of custom strings exclusive to this projects that would n
|
|||||||
* [Add notifications tab for posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/posts-notifications-tab)
|
* [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)
|
* [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/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
|
||||||
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
|
* [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 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
|
### Behavior
|
||||||
@@ -165,20 +117,6 @@ There's also a handful of custom strings exclusive to this projects that would n
|
|||||||
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/hide-interaction-numbers)
|
* [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 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)
|
* [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
|
### Visual
|
||||||
@@ -186,16 +124,6 @@ There's also a handful of custom strings exclusive to this projects that would n
|
|||||||
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:compact-extended-footer)
|
* [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)
|
* [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)
|
* [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
|
## Building
|
||||||
@@ -212,4 +140,6 @@ This project is released under the [GPL-3 License](./LICENSE).
|
|||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
<a rel="me" href="https://floss.social/@megalodon">@megalodon<wbr>@floss.social</a>
|
[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>
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
title: Megalodon
|
title: Moshidon
|
||||||
layout: default
|
layout: default
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Megalodon</title>
|
<title>Moshidon</title>
|
||||||
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
<link rel="icon" href="mastodon/src/main/res/mipmap-mdpi/ic_launcher_round.png">
|
||||||
<link rel="me" href="https://floss.social/@megalodon">
|
<link rel="me" href="https://floss.social/@mastodon">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.min.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="markdown-body">
|
<body class="markdown-body">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.0.0'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
|||||||
5
crowdin.yml
Normal file
5
crowdin.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
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%
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
find metadata -name '*.txt' -exec sed -Ei 's/^[–—─•·*]\s+/- /' {} \;
|
|
||||||
@@ -16,7 +16,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=false
|
android.enableJetifier=true
|
||||||
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,7 +1,6 @@
|
|||||||
|
#Thu Jan 13 11:33:43 MSK 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=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
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
276
gradlew
vendored
276
gradlew
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright © 2015-2021 the original authors.
|
# Copyright 2015 the original author or authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,98 +17,67 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
##
|
||||||
# Gradle start up script for POSIX generated by Gradle.
|
## Gradle start up script for UN*X
|
||||||
#
|
##
|
||||||
# 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
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
app_path=$0
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
# Need this for daisy-chained symlinks.
|
while [ -h "$PRG" ] ; do
|
||||||
while
|
ls=`ls -ld "$PRG"`
|
||||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
[ -h "$app_path" ]
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
do
|
PRG="$link"
|
||||||
ls=$( ls -ld "$app_path" )
|
else
|
||||||
link=${ls#*' -> '}
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
case $link in #(
|
fi
|
||||||
/*) app_path=$link ;; #(
|
|
||||||
*) app_path=$APP_HOME$link ;;
|
|
||||||
esac
|
|
||||||
done
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
# This is normally unused
|
APP_NAME="Gradle"
|
||||||
# shellcheck disable=SC2034
|
APP_BASE_NAME=`basename "$0"`
|
||||||
APP_BASE_NAME=${0##*/}
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# 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"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD="maximum"
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
} >&2
|
}
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
} >&2
|
}
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "$( uname )" in #(
|
case "`uname`" in
|
||||||
CYGWIN* ) cygwin=true ;; #(
|
CYGWIN* )
|
||||||
Darwin* ) darwin=true ;; #(
|
cygwin=true
|
||||||
MSYS* | MINGW* ) msys=true ;; #(
|
;;
|
||||||
NONSTOP* ) nonstop=true ;;
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -118,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
else
|
else
|
||||||
JAVACMD=$JAVA_HOME/bin/java
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -129,7 +98,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
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.
|
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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -137,109 +106,80 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
case $MAX_FD in #(
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
max*)
|
if [ $? -eq 0 ] ; then
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
# shellcheck disable=SC3045
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
fi
|
||||||
warn "Could not query maximum file descriptor limit"
|
ulimit -n $MAX_FD
|
||||||
esac
|
if [ $? -ne 0 ] ; then
|
||||||
case $MAX_FD in #(
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
'' | soft) :;; #(
|
fi
|
||||||
*)
|
else
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
# shellcheck disable=SC3045
|
fi
|
||||||
ulimit -n "$MAX_FD" ||
|
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
|
||||||
esac
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect all arguments for the java command, stacking in reverse order:
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
# * args from the command line
|
if $darwin; then
|
||||||
# * the main class name
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
# * -classpath
|
fi
|
||||||
# * -D...appname settings
|
|
||||||
# * --module-path (only if needed)
|
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
for arg do
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
if
|
SEP=""
|
||||||
case $arg in #(
|
for dir in $ROOTDIRSRAW ; do
|
||||||
-*) false ;; # don't mess with options #(
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
SEP="|"
|
||||||
[ -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
|
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" ;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
# 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" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
11
gradlew.bat
vendored
11
gradlew.bat
vendored
@@ -26,7 +26,6 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
@rem This is normally unused
|
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
exit /b 1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
|
||||||
exit /b %EXIT_CODE%
|
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
@@ -2,23 +2,17 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
archivesBaseName = "megalodon"
|
archivesBaseName = "moshidon"
|
||||||
applicationId "org.joinmastodon.android.sk"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 85
|
versionCode 87
|
||||||
versionName "1.2.3+fork.85"
|
versionName "1.1.4+fork.87.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
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']
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -55,19 +49,14 @@ android {
|
|||||||
setRoot "src/github"
|
setRoot "src/github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace 'org.joinmastodon.android'
|
lintOptions{
|
||||||
lint {
|
|
||||||
abortOnError false
|
|
||||||
checkReleaseBuilds false
|
checkReleaseBuilds false
|
||||||
}
|
abortOnError false
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
buildConfig true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'androidx.annotation:annotation:1.6.0'
|
api 'androidx.annotation:annotation:1.3.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
||||||
implementation 'me.grishka.litex:recyclerview:1.2.1.1'
|
implementation 'me.grishka.litex:recyclerview:1.2.1.1'
|
||||||
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0.1'
|
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0.1'
|
||||||
@@ -76,12 +65,11 @@ dependencies {
|
|||||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||||
implementation 'me.grishka.appkit:appkit:1.2.7'
|
implementation 'me.grishka.appkit:appkit:1.2.7'
|
||||||
implementation 'com.google.code.gson:gson:2.9.0'
|
implementation 'com.google.code.gson:gson:2.8.9'
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.14.3'
|
||||||
implementation 'com.squareup:otto:1.3.8'
|
implementation 'com.squareup:otto:1.3.8'
|
||||||
implementation 'de.psdev:async-otto:1.0.3'
|
implementation 'de.psdev:async-otto:1.0.3'
|
||||||
implementation 'org.parceler:parceler-api:1.1.12'
|
implementation 'org.parceler:parceler-api:1.1.12'
|
||||||
implementation 'com.github.bottom-software-foundation:bottom-java:2.1.0'
|
|
||||||
annotationProcessor 'org.parceler:parceler:1.1.12'
|
annotationProcessor 'org.parceler:parceler:1.1.12'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
package org.joinmastodon.android.utils;
|
|
||||||
|
|
||||||
import static org.joinmastodon.android.model.Filter.FilterAction.*;
|
|
||||||
import static org.joinmastodon.android.model.Filter.FilterContext.*;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class StatusFilterPredicateTest {
|
|
||||||
|
|
||||||
private static final Filter hideMeFilter = new Filter(), warnMeFilter = new Filter();
|
|
||||||
private static final List<Filter> allFilters = List.of(hideMeFilter, warnMeFilter);
|
|
||||||
|
|
||||||
private static final Status
|
|
||||||
hideInHomePublic = Status.ofFake(null, "hide me, please", Instant.now()),
|
|
||||||
warnInHomePublic = Status.ofFake(null, "display me with a warning", Instant.now());
|
|
||||||
|
|
||||||
static {
|
|
||||||
hideMeFilter.phrase = "hide me";
|
|
||||||
hideMeFilter.filterAction = HIDE;
|
|
||||||
hideMeFilter.context = EnumSet.of(PUBLIC, HOME);
|
|
||||||
|
|
||||||
warnMeFilter.phrase = "warning";
|
|
||||||
warnMeFilter.filterAction = WARN;
|
|
||||||
warnMeFilter.context = EnumSet.of(PUBLIC, HOME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHide() {
|
|
||||||
assertFalse("should not pass because matching filter applies to given context",
|
|
||||||
new StatusFilterPredicate(allFilters, HOME).test(hideInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHideRegardlessOfContext() {
|
|
||||||
assertTrue("filters without context should always pass",
|
|
||||||
new StatusFilterPredicate(allFilters, null).test(hideInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHideInDifferentContext() {
|
|
||||||
assertTrue("should pass because matching filter does not apply to given context",
|
|
||||||
new StatusFilterPredicate(allFilters, THREAD).test(hideInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHideWithWarningText() {
|
|
||||||
assertTrue("should pass because matching filter is for warnings",
|
|
||||||
new StatusFilterPredicate(allFilters, HOME).test(warnInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWarn() {
|
|
||||||
assertFalse("should not pass because filter applies to given context",
|
|
||||||
new StatusFilterPredicate(allFilters, HOME, WARN).test(warnInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWarnRegardlessOfContext() {
|
|
||||||
assertTrue("filters without context should always pass",
|
|
||||||
new StatusFilterPredicate(allFilters, null, WARN).test(warnInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWarnInDifferentContext() {
|
|
||||||
assertTrue("should pass because filter does not apply to given context",
|
|
||||||
new StatusFilterPredicate(allFilters, THREAD, WARN).test(warnInHomePublic));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testWarnWithHideText() {
|
|
||||||
assertTrue("should pass because matching filter is for hiding",
|
|
||||||
new StatusFilterPredicate(allFilters, HOME, WARN).test(hideInHomePublic));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.joinmastodon.android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
|
|
||||||
|
|||||||
BIN
mastodon/src/github/ic_launcher-playstore.png
Normal file
BIN
mastodon/src/github/ic_launcher-playstore.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -14,14 +14,12 @@ import android.os.Build;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
@@ -115,17 +113,13 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
|||||||
|
|
||||||
private void actuallyCheckForUpdates(){
|
private void actuallyCheckForUpdates(){
|
||||||
Request req=new Request.Builder()
|
Request req=new Request.Builder()
|
||||||
.url("https://api.github.com/repos/sk22/megalodon/releases")
|
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases/latest")
|
||||||
.build();
|
.build();
|
||||||
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||||
try(Response resp=call.execute()){
|
try(Response resp=call.execute()){
|
||||||
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
|
JsonObject obj=JsonParser.parseReader(resp.body().charStream()).getAsJsonObject();
|
||||||
for (JsonElement jsonElement : arr) {
|
|
||||||
JsonObject obj = jsonElement.getAsJsonObject();
|
|
||||||
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
|
|
||||||
|
|
||||||
String tag=obj.get("tag_name").getAsString();
|
|
||||||
String changelog=obj.get("body").getAsString();
|
String changelog=obj.get("body").getAsString();
|
||||||
|
String tag=obj.get("tag_name").getAsString();
|
||||||
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
||||||
Matcher matcher=pattern.matcher(tag);
|
Matcher matcher=pattern.matcher(tag);
|
||||||
if(!matcher.find()){
|
if(!matcher.find()){
|
||||||
@@ -153,7 +147,7 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
|||||||
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||||
for(JsonElement el:obj.getAsJsonArray("assets")){
|
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||||
JsonObject asset=el.getAsJsonObject();
|
JsonObject asset=el.getAsJsonObject();
|
||||||
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())){
|
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())){
|
||||||
long size=asset.get("size").getAsLong();
|
long size=asset.get("size").getAsLong();
|
||||||
String url=asset.get("browser_download_url").getAsString();
|
String url=asset.get("browser_download_url").getAsString();
|
||||||
|
|
||||||
@@ -167,8 +161,8 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
|||||||
.putLong("apkSize", size)
|
.putLong("apkSize", size)
|
||||||
.putString("version", version)
|
.putString("version", version)
|
||||||
.putString("apkURL", url)
|
.putString("apkURL", url)
|
||||||
.putString("changelog", changelog)
|
|
||||||
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
||||||
|
.putString("changelog", changelog)
|
||||||
.remove("downloadID")
|
.remove("downloadID")
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
@@ -177,8 +171,6 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}catch(Exception x){
|
}catch(Exception x){
|
||||||
Log.w(TAG, "actuallyCheckForUpdates", x);
|
Log.w(TAG, "actuallyCheckForUpdates", x);
|
||||||
}finally{
|
}finally{
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<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,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.joinmastodon.android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
@@ -11,20 +12,10 @@
|
|||||||
|
|
||||||
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
<permission android:name="${applicationId}.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<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
|
<application
|
||||||
android:name=".MastodonApp"
|
android:name=".MastodonApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:label="@string/sk_app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:localeConfig="@xml/locales_config"
|
android:localeConfig="@xml/locales_config"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
@@ -38,32 +29,15 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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">
|
<activity android:name=".OAuthActivity" android:exported="true" android:configChanges="orientation|screenSize" android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="megalodon-android-auth" android:host="callback"/>
|
<data android:scheme="moshidon-android-auth" android:host="callback"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize"
|
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
||||||
android:theme="@style/TransparentDialog">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|||||||
85
mastodon/src/main/assets/blocks.tsv
Normal file
85
mastodon/src/main/assets/blocks.tsv
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# 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
|
||||||
|
@@ -1,171 +0,0 @@
|
|||||||
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: 358 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,24 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package org.joinmastodon.android;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -11,9 +12,8 @@ import android.widget.Toast;
|
|||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
import org.joinmastodon.android.ui.AccountSwitcherSheet;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.jsoup.internal.StringUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -28,34 +28,21 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
UiUtils.setUserPreferredTheme(this);
|
UiUtils.setUserPreferredTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
|
|
||||||
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
|
|
||||||
boolean isMastodonURL = UiUtils.looksLikeMastodonUrl(text);
|
|
||||||
|
|
||||||
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
||||||
if(sessions.isEmpty()){
|
if(sessions.isEmpty()){
|
||||||
Toast.makeText(this, R.string.err_not_logged_in, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.err_not_logged_in, Toast.LENGTH_SHORT).show();
|
||||||
finish();
|
finish();
|
||||||
}else if(sessions.size()==1 && !isMastodonURL){
|
}else if(sessions.size()==1){
|
||||||
openComposeFragment(sessions.get(0).getID());
|
openComposeFragment(sessions.get(0).getID());
|
||||||
}else{
|
}else{
|
||||||
new AccountSwitcherSheet(this, null, true, isMastodonURL, (accountId, open) -> {
|
getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000));
|
||||||
if (open) {
|
new M3AlertDialogBuilder(this)
|
||||||
UiUtils.lookupURL(this, accountId, text, false, (clazz, args) -> {
|
.setItems(sessions.stream().map(as->"@"+as.self.username+"@"+as.domain).toArray(String[]::new), (dialog, which)->{
|
||||||
if (clazz == null) {
|
openComposeFragment(sessions.get(which).getID());
|
||||||
finish();
|
})
|
||||||
return;
|
.setTitle(R.string.choose_account)
|
||||||
}
|
.setOnCancelListener(dialog -> finish())
|
||||||
args.putString("fromExternalShare", clazz.getSimpleName());
|
.show();
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
|
||||||
intent.putExtras(args);
|
|
||||||
finish();
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
openComposeFragment(accountId);
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,15 +55,9 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
String subject = "";
|
String subject = "";
|
||||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
|
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
|
||||||
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
||||||
if (!StringUtil.isBlank(subject)) builder.append(subject).append("\n\n");
|
if (!subject.isBlank()) 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();
|
String text=builder.toString();
|
||||||
List<Uri> mediaUris;
|
List<Uri> mediaUris;
|
||||||
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
||||||
@@ -103,7 +84,8 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
if(!TextUtils.isEmpty(text))
|
if(!TextUtils.isEmpty(text))
|
||||||
args.putString("prefilledText", text);
|
args.putString("prefilledText", text);
|
||||||
args.putInt("selectionStart", StringUtil.isBlank(subject) ? 0 : subject.length());
|
if(!subject.isBlank())
|
||||||
|
args.putInt("selectionEnd", subject.length());
|
||||||
if(mediaUris!=null && !mediaUris.isEmpty())
|
if(mediaUris!=null && !mediaUris.isEmpty())
|
||||||
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
args.putParcelableArrayList("mediaAttachments", toArrayList(mediaUris));
|
||||||
Fragment fragment=new ComposeFragment();
|
Fragment fragment=new ComposeFragment();
|
||||||
|
|||||||
@@ -4,19 +4,15 @@ import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.model.ContentType;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GlobalUserPreferences{
|
public class GlobalUserPreferences{
|
||||||
public static boolean playGifs;
|
public static boolean playGifs;
|
||||||
@@ -25,68 +21,37 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean showReplies;
|
public static boolean showReplies;
|
||||||
public static boolean showBoosts;
|
public static boolean showBoosts;
|
||||||
public static boolean loadNewPosts;
|
public static boolean loadNewPosts;
|
||||||
public static boolean showNewPostsButton;
|
public static boolean showFederatedTimeline;
|
||||||
public static boolean showInteractionCounts;
|
public static boolean showInteractionCounts;
|
||||||
public static boolean alwaysExpandContentWarnings;
|
public static boolean alwaysExpandContentWarnings;
|
||||||
public static boolean disableMarquee;
|
public static boolean disableMarquee;
|
||||||
public static boolean disableSwipe;
|
public static boolean disableSwipe;
|
||||||
|
public static boolean disableDividers;
|
||||||
public static boolean voteButtonForSingleChoice;
|
public static boolean voteButtonForSingleChoice;
|
||||||
public static boolean enableDeleteNotifications;
|
|
||||||
public static boolean translateButtonOpenedOnly;
|
|
||||||
public static boolean uniformNotificationIcon;
|
public static boolean uniformNotificationIcon;
|
||||||
|
public static boolean enableDeleteNotifications;
|
||||||
|
public static boolean relocatePublishButton;
|
||||||
public static boolean reduceMotion;
|
public static boolean reduceMotion;
|
||||||
public static boolean keepOnlyLatestNotification;
|
public static boolean keepOnlyLatestNotification;
|
||||||
public static boolean disableAltTextReminder;
|
|
||||||
public static boolean showAltIndicator;
|
|
||||||
public static boolean showNoAltIndicator;
|
|
||||||
public static boolean enablePreReleases;
|
|
||||||
public static boolean prefixRepliesWithRe;
|
|
||||||
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 String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
|
||||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
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<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();
|
||||||
* Pleroma
|
public static Map<String, Integer> recentEmojis;
|
||||||
*/
|
|
||||||
public static String replyVisibility;
|
|
||||||
|
|
||||||
private static SharedPreferences getPrefs(){
|
private static SharedPreferences getPrefs(){
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> T fromJson(String json, Type type, T orElse) {
|
private static <T> T fromJson(String json, Type type, T orElse) {
|
||||||
if (json == null) return orElse;
|
|
||||||
try { return gson.fromJson(json, type); }
|
try { return gson.fromJson(json, type); }
|
||||||
catch (JsonSyntaxException ignored) { return orElse; }
|
catch (JsonSyntaxException ignored) { return orElse; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeAccount(String accountId) {
|
|
||||||
recentLanguages.remove(accountId);
|
|
||||||
pinnedTimelines.remove(accountId);
|
|
||||||
accountsInGlitchMode.remove(accountId);
|
|
||||||
accountsWithLocalOnlySupport.remove(accountId);
|
|
||||||
accountsWithContentTypesEnabled.remove(accountId);
|
|
||||||
accountsDefaultContentTypes.remove(accountId);
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void load(){
|
public static void load(){
|
||||||
SharedPreferences prefs=getPrefs();
|
SharedPreferences prefs=getPrefs();
|
||||||
playGifs=prefs.getBoolean("playGifs", true);
|
playGifs=prefs.getBoolean("playGifs", true);
|
||||||
@@ -95,44 +60,32 @@ public class GlobalUserPreferences{
|
|||||||
showReplies=prefs.getBoolean("showReplies", true);
|
showReplies=prefs.getBoolean("showReplies", true);
|
||||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||||
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
|
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
|
||||||
|
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
|
||||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||||
disableSwipe=prefs.getBoolean("disableSwipe", false);
|
disableSwipe=prefs.getBoolean("disableSwipe", false);
|
||||||
|
disableDividers=prefs.getBoolean("disableDividers", true);
|
||||||
|
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
|
||||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||||
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", false);
|
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", true);
|
||||||
translateButtonOpenedOnly=prefs.getBoolean("translateButtonOpenedOnly", false);
|
|
||||||
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
|
|
||||||
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
reduceMotion=prefs.getBoolean("reduceMotion", false);
|
||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
|
|
||||||
showAltIndicator=prefs.getBoolean("showAltIndicator", true);
|
|
||||||
showNoAltIndicator=prefs.getBoolean("showNoAltIndicator", true);
|
|
||||||
enablePreReleases=prefs.getBoolean("enablePreReleases", false);
|
|
||||||
prefixRepliesWithRe=prefs.getBoolean("prefixRepliesWithRe", false);
|
|
||||||
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)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
pinnedTimelines=fromJson(prefs.getString("pinnedTimelines", null), pinnedTimelinesType, new HashMap<>());
|
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
|
||||||
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
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 {
|
try {
|
||||||
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
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()));
|
||||||
|
}
|
||||||
} catch (IllegalArgumentException|ClassCastException ignored) {
|
} catch (IllegalArgumentException|ClassCastException ignored) {
|
||||||
// invalid color name or color was previously saved as integer
|
// invalid color name or color was previously saved as integer
|
||||||
color=ColorPreference.PINK;
|
color=ColorPreference.PURPLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,39 +96,23 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("showReplies", showReplies)
|
.putBoolean("showReplies", showReplies)
|
||||||
.putBoolean("showBoosts", showBoosts)
|
.putBoolean("showBoosts", showBoosts)
|
||||||
.putBoolean("loadNewPosts", loadNewPosts)
|
.putBoolean("loadNewPosts", loadNewPosts)
|
||||||
.putBoolean("showNewPostsButton", showNewPostsButton)
|
.putBoolean("showFederatedTimeline", showFederatedTimeline)
|
||||||
.putBoolean("trueBlackTheme", trueBlackTheme)
|
.putBoolean("trueBlackTheme", trueBlackTheme)
|
||||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||||
.putBoolean("disableMarquee", disableMarquee)
|
.putBoolean("disableMarquee", disableMarquee)
|
||||||
.putBoolean("disableSwipe", disableSwipe)
|
.putBoolean("disableSwipe", disableSwipe)
|
||||||
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
.putBoolean("disableDividers", disableDividers)
|
||||||
.putBoolean("translateButtonOpenedOnly", translateButtonOpenedOnly)
|
.putBoolean("relocatePublishButton", relocatePublishButton)
|
||||||
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
|
||||||
|
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
|
||||||
.putBoolean("reduceMotion", reduceMotion)
|
.putBoolean("reduceMotion", reduceMotion)
|
||||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||||
.putBoolean("disableAltTextReminder", disableAltTextReminder)
|
|
||||||
.putBoolean("showAltIndicator", showAltIndicator)
|
|
||||||
.putBoolean("showNoAltIndicator", showNoAltIndicator)
|
|
||||||
.putBoolean("enablePreReleases", enablePreReleases)
|
|
||||||
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
|
||||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
|
||||||
.putBoolean("spectatorMode", spectatorMode)
|
|
||||||
.putBoolean("autoHideFab", autoHideFab)
|
|
||||||
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
|
||||||
.putString("publishButtonText", publishButtonText)
|
.putString("publishButtonText", publishButtonText)
|
||||||
.putBoolean("bottomEncoding", bottomEncoding)
|
|
||||||
.putBoolean("replyLineAboveHeader", replyLineAboveHeader)
|
|
||||||
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
|
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putString("color", color.name())
|
.putString("color", color.name())
|
||||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
.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();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +124,8 @@ public class GlobalUserPreferences{
|
|||||||
BLUE,
|
BLUE,
|
||||||
BROWN,
|
BROWN,
|
||||||
RED,
|
RED,
|
||||||
YELLOW
|
YELLOW,
|
||||||
|
NORD
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ThemePreference{
|
public enum ThemePreference{
|
||||||
@@ -196,3 +134,4 @@ public class GlobalUserPreferences{
|
|||||||
DARK
|
DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -18,18 +17,16 @@ import org.joinmastodon.android.fragments.ProfileFragment;
|
|||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
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.fragments.onboarding.CustomWelcomeFragment;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
|
|
||||||
public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
|
public class MainActivity extends FragmentStackActivity{
|
||||||
private Fragment currentFragment;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState){
|
protected void onCreate(@Nullable Bundle savedInstanceState){
|
||||||
UiUtils.setUserPreferredTheme(this);
|
UiUtils.setUserPreferredTheme(this);
|
||||||
@@ -39,42 +36,32 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||||
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
||||||
}else{
|
}else{
|
||||||
|
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||||
AccountSession session;
|
AccountSession session;
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
Intent intent=getIntent();
|
Intent intent=getIntent();
|
||||||
if(intent.hasExtra("fromExternalShare")) {
|
if(intent.getBooleanExtra("fromNotification", false)){
|
||||||
AccountSessionManager.getInstance()
|
|
||||||
.setLastActiveAccountID(intent.getStringExtra("account"));
|
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
|
||||||
AccountSessionManager.getInstance().getLastActiveAccount());
|
|
||||||
showFragmentForExternalShare(intent.getExtras());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
|
||||||
boolean hasNotification = intent.hasExtra("notification");
|
|
||||||
if(fromNotification){
|
|
||||||
String accountID=intent.getStringExtra("accountID");
|
String accountID=intent.getStringExtra("accountID");
|
||||||
try{
|
try{
|
||||||
session=AccountSessionManager.getInstance().getAccount(accountID);
|
session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
if(!hasNotification) args.putString("tab", "notifications");
|
if(!intent.hasExtra("notification"))
|
||||||
|
args.putString("tab", "notifications");
|
||||||
}catch(IllegalStateException x){
|
}catch(IllegalStateException x){
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||||
}
|
}
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
|
||||||
args.putString("account", session.getID());
|
args.putString("account", session.getID());
|
||||||
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
||||||
fragment.setArguments(args);
|
fragment.setArguments(args);
|
||||||
if(fromNotification && hasNotification){
|
showFragmentClearingBackStack(fragment);
|
||||||
|
if(intent.getBooleanExtra("fromNotification", false) && intent.hasExtra("notification")){
|
||||||
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||||
showFragmentForNotification(notification, session.getID());
|
showFragmentForNotification(notification, session.getID());
|
||||||
}else if(intent.getBooleanExtra("compose", false)){
|
}else if(intent.getBooleanExtra("compose", false)){
|
||||||
showCompose();
|
showCompose();
|
||||||
}else{
|
}else{
|
||||||
showFragmentClearingBackStack(fragment);
|
|
||||||
maybeRequestNotificationsPermission();
|
maybeRequestNotificationsPermission();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,12 +75,11 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
@Override
|
@Override
|
||||||
protected void onNewIntent(Intent intent){
|
protected void onNewIntent(Intent intent){
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
if(intent.getBooleanExtra("fromNotification", false)){
|
||||||
if (intent.hasExtra("fromExternalShare")) showFragmentForExternalShare(intent.getExtras());
|
|
||||||
else if (intent.getBooleanExtra("fromNotification", false)) {
|
|
||||||
String accountID=intent.getStringExtra("accountID");
|
String accountID=intent.getStringExtra("accountID");
|
||||||
|
AccountSession accountSession;
|
||||||
try{
|
try{
|
||||||
AccountSessionManager.getInstance().getAccount(accountID);
|
accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
}catch(IllegalStateException x){
|
}catch(IllegalStateException x){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,19 +124,6 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
showFragment(fragment);
|
showFragment(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFragmentForExternalShare(Bundle args) {
|
|
||||||
String clazz = args.getString("fromExternalShare");
|
|
||||||
Fragment fragment = switch (clazz) {
|
|
||||||
case "ThreadFragment" -> new ThreadFragment();
|
|
||||||
case "ProfileFragment" -> new ProfileFragment();
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
if (fragment == null) return;
|
|
||||||
args.putBoolean("_can_go_back", true);
|
|
||||||
fragment.setArguments(args);
|
|
||||||
showFragment(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showCompose(){
|
private void showCompose(){
|
||||||
AccountSession session=AccountSessionManager.getInstance().getLastActiveAccount();
|
AccountSession session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||||
if(session==null || !session.activated)
|
if(session==null || !session.activated)
|
||||||
@@ -167,48 +140,4 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
|
requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* when opening app through a notification: if (thread) fragment "can go back", clear back stack
|
|
||||||
* and show home fragment. upstream's implementation doesn't require this as it opens home first
|
|
||||||
* and then immediately switches to the notification's ThreadFragment. this causes a black
|
|
||||||
* screen in megalodon, for some reason, so i'm working around this that way.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
Fragment currentFragment = getFragmentManager().findFragmentById(
|
|
||||||
(fragmentContainers.get(fragmentContainers.size() - 1)).getId()
|
|
||||||
);
|
|
||||||
Bundle currentArgs = currentFragment.getArguments();
|
|
||||||
if (fragmentContainers.size() != 1
|
|
||||||
|| currentArgs == null
|
|
||||||
|| !currentArgs.getBoolean("_can_go_back", false)) {
|
|
||||||
super.onBackPressed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (currentArgs.getBoolean("_finish_on_back", false)) {
|
|
||||||
finish();
|
|
||||||
} else if (currentArgs.containsKey("account")) {
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putString("account", currentArgs.getString("account"));
|
|
||||||
if (getIntent().getBooleanExtra("fromNotification", false)) {
|
|
||||||
args.putString("tab", "notifications");
|
|
||||||
}
|
|
||||||
Fragment fragment=new HomeFragment();
|
|
||||||
fragment.setArguments(args);
|
|
||||||
showFragmentClearingBackStack(fragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showFragment(Fragment fragment) {
|
|
||||||
super.showFragment(fragment);
|
|
||||||
this.currentFragment = fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
super.onProvideAssistContent(assistContent);
|
|
||||||
callFragmentToProvideAssistContent(currentFragment, assistContent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
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,7 +5,6 @@ import android.app.NotificationChannel;
|
|||||||
import android.app.NotificationChannelGroup;
|
import android.app.NotificationChannelGroup;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.RemoteInput;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -17,28 +16,15 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
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.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Mention;
|
|
||||||
import org.joinmastodon.android.model.NotificationAction;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
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.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -51,14 +37,11 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
private static final String TAG="PushNotificationReceive";
|
private static final String TAG="PushNotificationReceive";
|
||||||
|
|
||||||
public static final int NOTIFICATION_ID=178;
|
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 final int SUMMARY_ID = 791;
|
||||||
private static int notificationId = 0;
|
private static int notificationId = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent){
|
public void onReceive(Context context, Intent intent){
|
||||||
UiUtils.setUserPreferredTheme(context);
|
|
||||||
if(BuildConfig.DEBUG){
|
if(BuildConfig.DEBUG){
|
||||||
Log.e(TAG, "received: "+intent);
|
Log.e(TAG, "received: "+intent);
|
||||||
Bundle extras=intent.getExtras();
|
Bundle extras=intent.getExtras();
|
||||||
@@ -88,7 +71,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
}
|
}
|
||||||
String accountID=account.getID();
|
String accountID=account.getID();
|
||||||
PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s);
|
PushNotification pn=AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().decryptNotification(k, p, s);
|
||||||
E.post(new NotificationReceivedEvent(accountID, pn.notificationId+""));
|
|
||||||
new GetNotificationByID(pn.notificationId+"")
|
new GetNotificationByID(pn.notificationId+"")
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
@@ -110,35 +92,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
Log.w(TAG, "onReceive: invalid push notification format");
|
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){
|
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||||
@@ -146,6 +99,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
Notification.Builder builder;
|
Notification.Builder builder;
|
||||||
|
Notification.Builder summaryNotification;
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
||||||
boolean hasGroup=false;
|
boolean hasGroup=false;
|
||||||
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
|
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
|
||||||
@@ -168,42 +122,48 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
nm.createNotificationChannels(channels);
|
nm.createNotificationChannels(channels);
|
||||||
}
|
}
|
||||||
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
|
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
|
||||||
|
// summaryNotification=new Notification.Builder(context, accountID);
|
||||||
}else{
|
}else{
|
||||||
builder=new Notification.Builder(context)
|
builder=new Notification.Builder(context)
|
||||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||||
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
|
.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)));
|
Drawable avatar=ImageCache.getInstance(context).get(new UrlImageLoaderRequest(pn.icon, V.dp(50), V.dp(50)));
|
||||||
Intent contentIntent=new Intent(context, MainActivity.class);
|
Intent contentIntent=new Intent(context, MainActivity.class);
|
||||||
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
contentIntent.putExtra("fromNotification", true);
|
contentIntent.putExtra("fromNotification", true);
|
||||||
contentIntent.putExtra("accountID", accountID);
|
contentIntent.putExtra("accountID", accountID);
|
||||||
|
contentIntent.putExtra("notificationID", notificationId);
|
||||||
if(notification!=null){
|
if(notification!=null){
|
||||||
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
contentIntent.putExtra("notification", Parcels.wrap(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setContentTitle(pn.title)
|
builder.setContentTitle(pn.title)
|
||||||
.setContentText(pn.body)
|
.setContentText(pn.body)
|
||||||
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
|
.setContentTitle(pn.title)
|
||||||
.setSmallIcon(R.drawable.ic_ntf_logo)
|
.setStyle(new Notification.InboxStyle()
|
||||||
|
.addLine(pn.body))
|
||||||
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
.setGroup(accountID)
|
||||||
|
.setColor(context.getColor(R.color.shortcut_icon_background));
|
||||||
if(!GlobalUserPreferences.uniformNotificationIcon){
|
if(!GlobalUserPreferences.uniformNotificationIcon){
|
||||||
builder.setSmallIcon(switch (pn.notificationType) {
|
switch (pn.notificationType) {
|
||||||
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
|
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
|
||||||
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
|
||||||
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
|
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
|
||||||
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
|
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
|
||||||
case POLL -> R.drawable.ic_fluent_poll_24_filled;
|
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
|
||||||
case STATUS -> R.drawable.ic_fluent_chat_24_filled;
|
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
||||||
case UPDATE -> R.drawable.ic_fluent_history_24_filled;
|
}
|
||||||
case REPORT -> R.drawable.ic_fluent_warning_24_filled;
|
}else{
|
||||||
case SIGN_UP -> R.drawable.ic_fluent_person_available_24_filled;
|
builder.setSmallIcon(R.drawable.ic_ntf_logo);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(avatar!=null){
|
if(avatar!=null){
|
||||||
@@ -213,122 +173,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
builder.setSubText(accountName);
|
builder.setSubText(accountName);
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId++;
|
notificationId++;
|
||||||
|
nm.notify(accountID, GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId, builder.build());
|
||||||
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);
|
|
||||||
|
|
||||||
// copied from ComposeFragment - TODO: generalize?
|
|
||||||
ArrayList<String> mentions=new ArrayList<>();
|
|
||||||
Status status = notification.status;
|
|
||||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
|
||||||
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;
|
|
||||||
if(!mentions.contains(m))
|
|
||||||
mentions.add(m);
|
|
||||||
}
|
|
||||||
String initialText=mentions.isEmpty() ? "" : TextUtils.join(" ", mentions)+" ";
|
|
||||||
|
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
|
||||||
req.status = initialText + 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<>() {
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ import org.joinmastodon.android.BuildConfig;
|
|||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
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.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.SearchResult;
|
import org.joinmastodon.android.model.SearchResult;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
@@ -73,8 +72,10 @@ public class CacheController{
|
|||||||
int flags=cursor.getInt(1);
|
int flags=cursor.getInt(1);
|
||||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||||
newMaxID=status.id;
|
newMaxID=status.id;
|
||||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.HOME).test(status))
|
for(Filter filter:filters){
|
||||||
|
if(filter.matches(status))
|
||||||
continue outer;
|
continue outer;
|
||||||
|
}
|
||||||
result.add(status);
|
result.add(status);
|
||||||
}while(cursor.moveToNext());
|
}while(cursor.moveToNext());
|
||||||
String _newMaxID=newMaxID;
|
String _newMaxID=newMaxID;
|
||||||
@@ -89,7 +90,7 @@ public class CacheController{
|
|||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
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));
|
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||||
putHomeTimeline(result, maxID==null);
|
putHomeTimeline(result, maxID==null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,12 +126,11 @@ public class CacheController{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<CacheablePaginatedResponse<List<Notification>>> callback){
|
public void getNotifications(String maxID, int count, boolean onlyMentions, boolean onlyPosts, boolean forceReload, Callback<PaginatedResponse<List<Notification>>> callback){
|
||||||
cancelDelayedClose();
|
cancelDelayedClose();
|
||||||
databaseThread.postRunnable(()->{
|
databaseThread.postRunnable(()->{
|
||||||
try{
|
try{
|
||||||
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(accountID);
|
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
||||||
List<Filter> filters=accountSession.wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
|
|
||||||
if(!forceReload){
|
if(!forceReload){
|
||||||
SQLiteDatabase db=getOrOpenDatabase();
|
SQLiteDatabase db=getOrOpenDatabase();
|
||||||
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
|
String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all";
|
||||||
@@ -145,30 +145,35 @@ public class CacheController{
|
|||||||
ntf.postprocess();
|
ntf.postprocess();
|
||||||
newMaxID=ntf.id;
|
newMaxID=ntf.id;
|
||||||
if(ntf.status!=null){
|
if(ntf.status!=null){
|
||||||
if (!new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status))
|
for(Filter filter:filters){
|
||||||
|
if(filter.matches(ntf.status))
|
||||||
continue outer;
|
continue outer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result.add(ntf);
|
result.add(ntf);
|
||||||
}while(cursor.moveToNext());
|
}while(cursor.moveToNext());
|
||||||
String _newMaxID=newMaxID;
|
String _newMaxID=newMaxID;
|
||||||
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
uiHandler.post(()->callback.onSuccess(new PaginatedResponse<>(result, _newMaxID)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}catch(IOException x){
|
}catch(IOException x){
|
||||||
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
|
Log.w(TAG, "getNotifications: corrupted notification object in database", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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))
|
||||||
new GetNotifications(maxID, count, onlyPosts ? EnumSet.of(Notification.Type.STATUS) : onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class), instance.isPleroma())
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Notification> result){
|
public void onSuccess(List<Notification> result){
|
||||||
callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(ntf->{
|
callback.onSuccess(new PaginatedResponse<>(result.stream().filter(ntf->{
|
||||||
if(ntf.status!=null){
|
if(ntf.status!=null){
|
||||||
return new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status);
|
for(Filter filter:filters){
|
||||||
|
if(filter.matches(ntf.status)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
}).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id));
|
||||||
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
|
putNotifications(result, onlyMentions, onlyPosts, maxID==null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.joinmastodon.android.MastodonApp;
|
|||||||
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
|
||||||
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -28,7 +27,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -42,19 +40,14 @@ import okhttp3.ResponseBody;
|
|||||||
|
|
||||||
public class MastodonAPIController{
|
public class MastodonAPIController{
|
||||||
private static final String TAG="MastodonAPIController";
|
private static final String TAG="MastodonAPIController";
|
||||||
public static final Gson gsonWithoutDeserializer = new GsonBuilder()
|
public static final Gson gson=new GsonBuilder()
|
||||||
.disableHtmlEscaping()
|
.disableHtmlEscaping()
|
||||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
|
.registerTypeAdapter(Instant.class, new IsoInstantTypeAdapter())
|
||||||
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
|
.registerTypeAdapter(LocalDate.class, new IsoLocalDateTypeAdapter())
|
||||||
.create();
|
.create();
|
||||||
public static final Gson gson = gsonWithoutDeserializer.newBuilder()
|
|
||||||
.registerTypeAdapter(Status.class, new Status.StatusDeserializer())
|
|
||||||
.create();
|
|
||||||
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
private static WorkerThread thread=new WorkerThread("MastodonAPIController");
|
||||||
private static OkHttpClient httpClient=new OkHttpClient.Builder()
|
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
|
||||||
.readTimeout(5, TimeUnit.MINUTES)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private AccountSession session;
|
private AccountSession session;
|
||||||
private static List<String> badDomains = new ArrayList<>();
|
private static List<String> badDomains = new ArrayList<>();
|
||||||
@@ -63,7 +56,7 @@ public class MastodonAPIController{
|
|||||||
thread.start();
|
thread.start();
|
||||||
try {
|
try {
|
||||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
MastodonApp.context.getAssets().open("blocks.txt")
|
MastodonApp.context.getAssets().open("blocks.tsv")
|
||||||
));
|
));
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
@@ -94,7 +87,7 @@ public class MastodonAPIController{
|
|||||||
Request.Builder builder=new Request.Builder()
|
Request.Builder builder=new Request.Builder()
|
||||||
.url(req.getURL().toString())
|
.url(req.getURL().toString())
|
||||||
.method(req.getMethod(), req.getRequestBody())
|
.method(req.getMethod(), req.getRequestBody())
|
||||||
.header("User-Agent", "MegalodonAndroid/"+BuildConfig.VERSION_NAME);
|
.header("User-Agent", "MastodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||||
|
|
||||||
String token=null;
|
String token=null;
|
||||||
if(session!=null)
|
if(session!=null)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import android.view.View;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
|
||||||
public class MastodonErrorResponse extends ErrorResponse{
|
public class MastodonErrorResponse extends ErrorResponse{
|
||||||
@@ -20,7 +22,7 @@ public class MastodonErrorResponse extends ErrorResponse{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindErrorView(View view){
|
public void bindErrorView(View view){
|
||||||
TextView text=view.findViewById(me.grishka.appkit.R.id.error_text);
|
TextView text=view.findViewById(R.id.error_text);
|
||||||
text.setText(error);
|
text.setText(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ public class PushSubscriptionManager{
|
|||||||
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||||
if(session.pushSubscription==null || forceReRegister)
|
if(session.pushSubscription==null || forceReRegister)
|
||||||
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
|
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
|
||||||
else
|
else if(session.needUpdatePushSettings)
|
||||||
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
|
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
|
||||||
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
public class SetAccountFollowed extends MastodonAPIRequest<Relationship>{
|
||||||
public SetAccountFollowed(String id, boolean followed, boolean showReblogs){
|
|
||||||
this(id, followed, showReblogs, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
public SetAccountFollowed(String id, boolean followed, boolean showReblogs, boolean notify){
|
||||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
super(HttpMethod.POST, "/accounts/"+id+"/"+(followed ? "follow" : "unfollow"), Relationship.class);
|
||||||
if(followed)
|
if(followed)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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 org.joinmastodon.android.model.ListTimeline;
|
|
||||||
|
|
||||||
public class GetList extends MastodonAPIRequest<ListTimeline> {
|
|
||||||
public GetList(String id) {
|
|
||||||
super(HttpMethod.GET, "/lists/" + id, ListTimeline.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.joinmastodon.android.api.requests.notifications;
|
package org.joinmastodon.android.api.requests.notifications;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.ApiUtils;
|
import org.joinmastodon.android.api.ApiUtils;
|
||||||
@@ -10,25 +11,19 @@ import java.util.EnumSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
|
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
|
||||||
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes, boolean isPleromaInstance){
|
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> includeTypes){
|
||||||
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
|
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
|
||||||
if(maxID!=null)
|
if(maxID!=null)
|
||||||
addQueryParameter("max_id", maxID);
|
addQueryParameter("max_id", maxID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(includeTypes!=null){
|
if(includeTypes!=null){
|
||||||
if(!isPleromaInstance) {
|
|
||||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
||||||
addQueryParameter("types[]", type);
|
addQueryParameter("types[]", type);
|
||||||
}
|
}
|
||||||
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){
|
||||||
addQueryParameter("exclude_types[]", type);
|
addQueryParameter("exclude_types[]", type);
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){
|
|
||||||
addQueryParameter("include_types[]", type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
removeUnsupportedItems=true;
|
removeUnsupportedItems=true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.notifications;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import okhttp3.MultipartBody;
|
|
||||||
import okhttp3.RequestBody;
|
|
||||||
|
|
||||||
public class PleromaMarkNotificationsRead extends MastodonAPIRequest<List<Notification>> {
|
|
||||||
private String maxID;
|
|
||||||
public PleromaMarkNotificationsRead(String maxID) {
|
|
||||||
super(HttpMethod.POST, "/pleroma/notifications/read", new TypeToken<>(){});
|
|
||||||
this.maxID = maxID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestBody getRequestBody() {
|
|
||||||
MultipartBody.Builder builder=new MultipartBody.Builder()
|
|
||||||
.setType(MultipartBody.FORM);
|
|
||||||
if(!TextUtils.isEmpty(maxID))
|
|
||||||
builder.addFormDataPart("max_id", maxID);
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
|
|||||||
Request r=new Request();
|
Request r=new Request();
|
||||||
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
|
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
|
||||||
r.data.alerts=alerts;
|
r.data.alerts=alerts;
|
||||||
r.policy=policy;
|
r.data.policy=policy;
|
||||||
r.subscription.keys.p256dh=encryptionKey;
|
r.subscription.keys.p256dh=encryptionKey;
|
||||||
r.subscription.keys.auth=authKey;
|
r.subscription.keys.auth=authKey;
|
||||||
setRequestBody(r);
|
setRequestBody(r);
|
||||||
@@ -18,7 +18,6 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
|
|||||||
private static class Request{
|
private static class Request{
|
||||||
public Subscription subscription=new Subscription();
|
public Subscription subscription=new Subscription();
|
||||||
public Data data=new Data();
|
public Data data=new Data();
|
||||||
public PushSubscription.Policy policy;
|
|
||||||
|
|
||||||
private static class Keys{
|
private static class Keys{
|
||||||
public String p256dh;
|
public String p256dh;
|
||||||
@@ -32,6 +31,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
|
|||||||
|
|
||||||
private static class Data{
|
private static class Data{
|
||||||
public PushSubscription.Alerts alerts;
|
public PushSubscription.Alerts alerts;
|
||||||
|
public PushSubscription.Policy policy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,36 +3,23 @@ package org.joinmastodon.android.api.requests.notifications;
|
|||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import okhttp3.Response;
|
|
||||||
|
|
||||||
public class UpdatePushSettings extends MastodonAPIRequest<PushSubscription>{
|
public class UpdatePushSettings extends MastodonAPIRequest<PushSubscription>{
|
||||||
private final PushSubscription.Policy policy;
|
|
||||||
|
|
||||||
public UpdatePushSettings(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
|
public UpdatePushSettings(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
|
||||||
super(HttpMethod.PUT, "/push/subscription", PushSubscription.class);
|
super(HttpMethod.PUT, "/push/subscription", PushSubscription.class);
|
||||||
setRequestBody(new Request(alerts, policy));
|
setRequestBody(new Request(alerts, policy));
|
||||||
this.policy=policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void validateAndPostprocessResponse(PushSubscription respObj, Response httpResponse) throws IOException{
|
|
||||||
super.validateAndPostprocessResponse(respObj, httpResponse);
|
|
||||||
respObj.policy=policy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Request{
|
private static class Request{
|
||||||
public Data data=new Data();
|
public Data data=new Data();
|
||||||
public PushSubscription.Policy policy;
|
|
||||||
|
|
||||||
public Request(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
|
public Request(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
|
||||||
this.data.alerts=alerts;
|
this.data.alerts=alerts;
|
||||||
this.policy=policy;
|
this.data.policy=policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Data{
|
private static class Data{
|
||||||
public PushSubscription.Alerts alerts;
|
public PushSubscription.Alerts alerts;
|
||||||
|
public PushSubscription.Policy policy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ public class CreateOAuthApp extends MastodonAPIRequest<Application>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class Request{
|
private static class Request{
|
||||||
public String clientName="Megalodon";
|
public String clientName="Moshidon";
|
||||||
public String redirectUris=AccountSessionManager.REDIRECT_URI;
|
public String redirectUris=AccountSessionManager.REDIRECT_URI;
|
||||||
public String scopes=AccountSessionManager.SCOPE;
|
public String scopes=AccountSessionManager.SCOPE;
|
||||||
public String website="https://sk22.github.io/megalodon";
|
public String website="https://github.com/LucasGGamerM/moshidon";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.api.requests.statuses;
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.ContentType;
|
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
@@ -40,15 +39,11 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
|||||||
public Poll poll;
|
public Poll poll;
|
||||||
public String inReplyToId;
|
public String inReplyToId;
|
||||||
public boolean sensitive;
|
public boolean sensitive;
|
||||||
public boolean localOnly;
|
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
public StatusPrivacy visibility;
|
public StatusPrivacy visibility;
|
||||||
public Instant scheduledAt;
|
public Instant scheduledAt;
|
||||||
public String language;
|
public String language;
|
||||||
|
|
||||||
public String quoteId;
|
|
||||||
public ContentType contentType;
|
|
||||||
|
|
||||||
public static class Poll{
|
public static class Poll{
|
||||||
public ArrayList<String> options=new ArrayList<>();
|
public ArrayList<String> options=new ArrayList<>();
|
||||||
public int expiresIn;
|
public int expiresIn;
|
||||||
|
|||||||
@@ -2,22 +2,17 @@ package org.joinmastodon.android.api.requests.statuses;
|
|||||||
|
|
||||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
|
||||||
import org.joinmastodon.android.model.BaseModel;
|
import org.joinmastodon.android.model.BaseModel;
|
||||||
import org.joinmastodon.android.model.ContentType;
|
|
||||||
|
|
||||||
public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.Response>{
|
public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.Response>{
|
||||||
public GetStatusSourceText(String id){
|
public GetStatusSourceText(String id){
|
||||||
super(HttpMethod.GET, "/statuses/"+id+"/source", Response.class);
|
super(HttpMethod.GET, "/statuses/"+id+"/source", Response.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AllFieldsAreRequired
|
||||||
public static class Response extends BaseModel{
|
public static class Response extends BaseModel{
|
||||||
@RequiredField
|
|
||||||
public String id;
|
public String id;
|
||||||
@RequiredField
|
|
||||||
public String text;
|
public String text;
|
||||||
@RequiredField
|
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
public ContentType contentType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.timelines;
|
|
||||||
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class GetBubbleTimeline extends MastodonAPIRequest<List<Status>> {
|
|
||||||
public GetBubbleTimeline(String maxID, int limit) {
|
|
||||||
super(HttpMethod.GET, "/timelines/bubble", new TypeToken<>(){});
|
|
||||||
if(!TextUtils.isEmpty(maxID))
|
|
||||||
addQueryParameter("max_id", maxID);
|
|
||||||
if(limit>0)
|
|
||||||
addQueryParameter("limit", limit+"");
|
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.timelines;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetConversationsTimeline extends MastodonAPIRequest<List<Status>>{
|
||||||
|
public GetConversationsTimeline(String maxID, String minID, int limit, String sinceID){
|
||||||
|
super(HttpMethod.GET, "/conversations", new TypeToken<>(){});
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(minID!=null)
|
||||||
|
addQueryParameter("min_id", minID);
|
||||||
|
if(sinceID!=null)
|
||||||
|
addQueryParameter("since_id", sinceID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", ""+limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.api.requests.timelines;
|
|||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -17,7 +16,5 @@ public class GetHashtagTimeline extends MastodonAPIRequest<List<Status>>{
|
|||||||
addQueryParameter("min_id", minID);
|
addQueryParameter("min_id", minID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.api.requests.timelines;
|
|||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -19,7 +18,5 @@ public class GetHomeTimeline extends MastodonAPIRequest<List<Status>>{
|
|||||||
addQueryParameter("since_id", sinceID);
|
addQueryParameter("since_id", sinceID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.api.requests.timelines;
|
|||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -19,7 +18,5 @@ public class GetListTimeline extends MastodonAPIRequest<List<Status>> {
|
|||||||
addQueryParameter("limit", ""+limit);
|
addQueryParameter("limit", ""+limit);
|
||||||
if(sinceID!=null)
|
if(sinceID!=null)
|
||||||
addQueryParameter("since_id", sinceID);
|
addQueryParameter("since_id", sinceID);
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -21,7 +20,5 @@ public class GetPublicTimeline extends MastodonAPIRequest<List<Status>>{
|
|||||||
addQueryParameter("max_id", maxID);
|
addQueryParameter("max_id", maxID);
|
||||||
if(limit>0)
|
if(limit>0)
|
||||||
addQueryParameter("limit", limit+"");
|
addQueryParameter("limit", limit+"");
|
||||||
if(GlobalUserPreferences.replyVisibility != null)
|
|
||||||
addQueryParameter("reply_visibility", GlobalUserPreferences.replyVisibility);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.joinmastodon.android.api.session;
|
package org.joinmastodon.android.api.session;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.CacheController;
|
import org.joinmastodon.android.api.CacheController;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
@@ -9,15 +7,12 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
|||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Application;
|
import org.joinmastodon.android.model.Application;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Markers;
|
|
||||||
import org.joinmastodon.android.model.Preferences;
|
import org.joinmastodon.android.model.Preferences;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class AccountSession{
|
public class AccountSession{
|
||||||
public Token token;
|
public Token token;
|
||||||
@@ -36,7 +31,6 @@ public class AccountSession{
|
|||||||
public String pushAccountID;
|
public String pushAccountID;
|
||||||
public Preferences preferences;
|
public Preferences preferences;
|
||||||
public AccountActivationInfo activationInfo;
|
public AccountActivationInfo activationInfo;
|
||||||
public Markers markers;
|
|
||||||
private transient MastodonAPIController apiController;
|
private transient MastodonAPIController apiController;
|
||||||
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
|
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
|
||||||
private transient CacheController cacheController;
|
private transient CacheController cacheController;
|
||||||
@@ -91,15 +85,4 @@ public class AccountSession{
|
|||||||
pushSubscriptionManager=new PushSubscriptionManager(getID());
|
pushSubscriptionManager=new PushSubscriptionManager(getID());
|
||||||
return pushSubscriptionManager;
|
return pushSubscriptionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Instance> getInstance() {
|
|
||||||
return Optional.ofNullable(AccountSessionManager.getInstance().getInstanceInfo(domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uri getInstanceUri() {
|
|
||||||
return new Uri.Builder()
|
|
||||||
.scheme("https")
|
|
||||||
.authority(domain)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.MainActivity;
|
import org.joinmastodon.android.MainActivity;
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
@@ -26,7 +25,6 @@ import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
|||||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
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.api.requests.oauth.CreateOAuthApp;
|
||||||
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
import org.joinmastodon.android.events.EmojiUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -35,8 +33,6 @@ import org.joinmastodon.android.model.Emoji;
|
|||||||
import org.joinmastodon.android.model.EmojiCategory;
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Instance;
|
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.Preferences;
|
||||||
import org.joinmastodon.android.model.Token;
|
import org.joinmastodon.android.model.Token;
|
||||||
|
|
||||||
@@ -50,7 +46,6 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -66,7 +61,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
public class AccountSessionManager{
|
public class AccountSessionManager{
|
||||||
private static final String TAG="AccountSessionManager";
|
private static final String TAG="AccountSessionManager";
|
||||||
public static final String SCOPE="read write follow push";
|
public static final String SCOPE="read write follow push";
|
||||||
public static final String REDIRECT_URI="megalodon-android-auth://callback";
|
public static final String REDIRECT_URI="moshidon-android-auth://callback";
|
||||||
|
|
||||||
private static final AccountSessionManager instance=new AccountSessionManager();
|
private static final AccountSessionManager instance=new AccountSessionManager();
|
||||||
|
|
||||||
@@ -111,12 +106,6 @@ public class AccountSessionManager{
|
|||||||
sessions.put(session.getID(), session);
|
sessions.put(session.getID(), session);
|
||||||
lastActiveAccountID=session.getID();
|
lastActiveAccountID=session.getID();
|
||||||
writeAccountsFile();
|
writeAccountsFile();
|
||||||
|
|
||||||
// write initial instance info to file immediately to avoid sessions without instance info
|
|
||||||
InstanceInfoStorageWrapper wrapper = new InstanceInfoStorageWrapper();
|
|
||||||
wrapper.instance = instance;
|
|
||||||
MastodonAPIController.runInBackground(()->writeInstanceInfoFile(wrapper, instance.uri));
|
|
||||||
|
|
||||||
updateMoreInstanceInfo(instance, instance.uri);
|
updateMoreInstanceInfo(instance, instance.uri);
|
||||||
if(PushSubscriptionManager.arePushNotificationsAvailable()){
|
if(PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||||
session.getPushSubscriptionManager().registerAccountForPush(null);
|
session.getPushSubscriptionManager().registerAccountForPush(null);
|
||||||
@@ -125,16 +114,14 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void writeAccountsFile(){
|
public synchronized void writeAccountsFile(){
|
||||||
File tmpFile = new File(MastodonApp.context.getFilesDir(), "accounts.json~");
|
|
||||||
File file=new File(MastodonApp.context.getFilesDir(), "accounts.json");
|
File file=new File(MastodonApp.context.getFilesDir(), "accounts.json");
|
||||||
try{
|
try{
|
||||||
try(FileOutputStream out=new FileOutputStream(tmpFile)){
|
try(FileOutputStream out=new FileOutputStream(file)){
|
||||||
SessionsStorageWrapper w=new SessionsStorageWrapper();
|
SessionsStorageWrapper w=new SessionsStorageWrapper();
|
||||||
w.accounts=new ArrayList<>(sessions.values());
|
w.accounts=new ArrayList<>(sessions.values());
|
||||||
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
||||||
MastodonAPIController.gson.toJson(w, writer);
|
MastodonAPIController.gson.toJson(w, writer);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
if (!tmpFile.renameTo(file)) Log.e(TAG, "Error renaming " + tmpFile.getPath() + " to " + file.getPath());
|
|
||||||
}
|
}
|
||||||
}catch(IOException x){
|
}catch(IOException x){
|
||||||
Log.e(TAG, "Error writing accounts file", x);
|
Log.e(TAG, "Error writing accounts file", x);
|
||||||
@@ -187,7 +174,6 @@ public class AccountSessionManager{
|
|||||||
AccountSession session=getAccount(id);
|
AccountSession session=getAccount(id);
|
||||||
session.getCacheController().closeDatabase();
|
session.getCacheController().closeDatabase();
|
||||||
MastodonApp.context.deleteDatabase(id+".db");
|
MastodonApp.context.deleteDatabase(id+".db");
|
||||||
GlobalUserPreferences.removeAccount(id);
|
|
||||||
sessions.remove(id);
|
sessions.remove(id);
|
||||||
if(lastActiveAccountID.equals(id)){
|
if(lastActiveAccountID.equals(id)){
|
||||||
if(sessions.isEmpty())
|
if(sessions.isEmpty())
|
||||||
@@ -225,7 +211,7 @@ public class AccountSessionManager{
|
|||||||
.path("/oauth/authorize")
|
.path("/oauth/authorize")
|
||||||
.appendQueryParameter("response_type", "code")
|
.appendQueryParameter("response_type", "code")
|
||||||
.appendQueryParameter("client_id", result.clientId)
|
.appendQueryParameter("client_id", result.clientId)
|
||||||
.appendQueryParameter("redirect_uri", "megalodon-android-auth://callback")
|
.appendQueryParameter("redirect_uri", "moshidon-android-auth://callback")
|
||||||
.appendQueryParameter("scope", SCOPE)
|
.appendQueryParameter("scope", SCOPE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -258,44 +244,30 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void maybeUpdateLocalInfo(){
|
public void maybeUpdateLocalInfo(){
|
||||||
maybeUpdateLocalInfo(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void maybeUpdateLocalInfo(AccountSession activeSession){
|
|
||||||
long now=System.currentTimeMillis();
|
long now=System.currentTimeMillis();
|
||||||
HashSet<String> domains=new HashSet<>();
|
HashSet<String> domains=new HashSet<>();
|
||||||
for(AccountSession session:sessions.values()){
|
for(AccountSession session:sessions.values()){
|
||||||
domains.add(session.domain.toLowerCase());
|
domains.add(session.domain.toLowerCase());
|
||||||
if(now-session.infoLastUpdated>24L*3600_000L || session == activeSession){
|
// if(now-session.infoLastUpdated>24L*3600_000L){
|
||||||
updateSessionPreferences(session);
|
updateSessionPreferences(session);
|
||||||
updateSessionLocalInfo(session);
|
updateSessionLocalInfo(session);
|
||||||
}
|
// }
|
||||||
if(now-session.filtersLastUpdated>3600_000L || session == activeSession){
|
// if(now-session.filtersLastUpdated>3600_000L){
|
||||||
updateSessionWordFilters(session);
|
updateSessionWordFilters(session);
|
||||||
}
|
// }
|
||||||
updateSessionMarkers(session);
|
|
||||||
}
|
}
|
||||||
if(loadedInstances){
|
if(loadedInstances){
|
||||||
maybeUpdateCustomEmojis(domains, activeSession != null ? activeSession.domain : null);
|
maybeUpdateCustomEmojis(domains);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeUpdateCustomEmojis(Set<String> domains, String activeDomain){
|
private void maybeUpdateCustomEmojis(Set<String> domains){
|
||||||
long now=System.currentTimeMillis();
|
long now=System.currentTimeMillis();
|
||||||
for(String domain:domains){
|
for(String domain:domains){
|
||||||
Long lastUpdated=instancesLastUpdated.get(domain);
|
// Long lastUpdated=instancesLastUpdated.get(domain);
|
||||||
if(lastUpdated==null || now-lastUpdated>24L*3600_000L || domain.equals(activeDomain)){
|
// if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
|
||||||
updateInstanceInfo(domain);
|
updateInstanceInfo(domain);
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,12 +278,13 @@ public class AccountSessionManager{
|
|||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
session.self=result;
|
session.self=result;
|
||||||
session.infoLastUpdated=System.currentTimeMillis();
|
session.infoLastUpdated=System.currentTimeMillis();
|
||||||
preferencesFromSource(session, result);
|
|
||||||
writeAccountsFile();
|
writeAccountsFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){}
|
public void onError(ErrorResponse error){
|
||||||
|
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.exec(session.getID());
|
.exec(session.getID());
|
||||||
}
|
}
|
||||||
@@ -321,14 +294,10 @@ public class AccountSessionManager{
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Preferences preferences) {
|
public void onSuccess(Preferences preferences) {
|
||||||
session.preferences=preferences;
|
session.preferences=preferences;
|
||||||
preferencesFromSource(session, session.self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {}
|
||||||
session.preferences = new Preferences();
|
|
||||||
preferencesFromSource(session, session.self);
|
|
||||||
}
|
|
||||||
}).exec(session.getID());
|
}).exec(session.getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,21 +319,6 @@ public class AccountSessionManager{
|
|||||||
.exec(session.getID());
|
.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){
|
public void updateInstanceInfo(String domain){
|
||||||
new GetInstance()
|
new GetInstance()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@@ -414,9 +368,7 @@ public class AccountSessionManager{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
InstanceInfoStorageWrapper wrapper=new InstanceInfoStorageWrapper();
|
|
||||||
wrapper.instance = instance;
|
|
||||||
MastodonAPIController.runInBackground(()->writeInstanceInfoFile(wrapper, domain));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.execNoAuth(domain);
|
.execNoAuth(domain);
|
||||||
@@ -427,13 +379,10 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeInstanceInfoFile(InstanceInfoStorageWrapper emojis, String domain){
|
private void writeInstanceInfoFile(InstanceInfoStorageWrapper emojis, String domain){
|
||||||
File file = getInstanceInfoFile(domain);
|
try(FileOutputStream out=new FileOutputStream(getInstanceInfoFile(domain))){
|
||||||
File tmpFile = new File(file.getPath() + "~");
|
|
||||||
try(FileOutputStream out=new FileOutputStream(tmpFile)){
|
|
||||||
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
||||||
MastodonAPIController.gson.toJson(emojis, writer);
|
MastodonAPIController.gson.toJson(emojis, writer);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
if (!tmpFile.renameTo(file)) Log.e(TAG, "Error renaming " + tmpFile.getPath() + " to " + file.getPath());
|
|
||||||
}catch(IOException x){
|
}catch(IOException x){
|
||||||
Log.w(TAG, "Error writing instance info file for "+domain, x);
|
Log.w(TAG, "Error writing instance info file for "+domain, x);
|
||||||
}
|
}
|
||||||
@@ -453,7 +402,7 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
if(!loadedInstances){
|
if(!loadedInstances){
|
||||||
loadedInstances=true;
|
loadedInstances=true;
|
||||||
maybeUpdateCustomEmojis(domains, null);
|
maybeUpdateCustomEmojis(domains);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,6 +426,10 @@ public class AccountSessionManager{
|
|||||||
return instances.get(domain);
|
return instances.get(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Instance getInstanceInfoForAccount(String account) {
|
||||||
|
return AccountSessionManager.getInstance().getInstanceInfo(instance.getAccount(account).domain);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateAccountInfo(String id, Account account){
|
public void updateAccountInfo(String id, Account account){
|
||||||
AccountSession session=getAccount(id);
|
AccountSession session=getAccount(id);
|
||||||
session.self=account;
|
session.self=account;
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class AllNotificationsSeenEvent {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class HashtagUpdatedEvent {
|
|
||||||
public final String name;
|
|
||||||
public final boolean following;
|
|
||||||
|
|
||||||
public HashtagUpdatedEvent(String name, boolean following) {
|
|
||||||
this.name = name;
|
|
||||||
this.following = following;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class ListDeletedEvent {
|
|
||||||
public final String id;
|
|
||||||
|
|
||||||
public ListDeletedEvent(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
|
|
||||||
public class ListUpdatedCreatedEvent {
|
|
||||||
public final String id;
|
|
||||||
public final String title;
|
|
||||||
public final ListTimeline.RepliesPolicy repliesPolicy;
|
|
||||||
|
|
||||||
public ListUpdatedCreatedEvent(String id, String title, ListTimeline.RepliesPolicy repliesPolicy) {
|
|
||||||
this.id = id;
|
|
||||||
this.title = title;
|
|
||||||
this.repliesPolicy = repliesPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.joinmastodon.android.events;
|
|
||||||
|
|
||||||
public class NotificationReceivedEvent {
|
|
||||||
public String account, id;
|
|
||||||
public NotificationReceivedEvent(String account, String id) {
|
|
||||||
this.account = account;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
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.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
@@ -12,15 +15,12 @@ import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
|||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
@@ -60,13 +60,8 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(getActivity()==null)
|
||||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
return;
|
||||||
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());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -76,6 +71,7 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab = ((ProfileFragment) getParentFragment()).getFab();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,8 +82,7 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void onStatusCreated(StatusCreatedEvent ev){
|
protected void onStatusCreated(StatusCreatedEvent ev){
|
||||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
if(!AccountSessionManager.getInstance().isSelf(accountID, ev.status.account))
|
||||||
if(!asm.isSelf(accountID, ev.status.account) || !asm.isSelf(accountID, user))
|
|
||||||
return;
|
return;
|
||||||
if(filter==GetAccountStatuses.Filter.PINNED) return;
|
if(filter==GetAccountStatuses.Filter.PINNED) return;
|
||||||
if(filter==GetAccountStatuses.Filter.DEFAULT){
|
if(filter==GetAccountStatuses.Filter.DEFAULT){
|
||||||
@@ -125,19 +120,4 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.ACCOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
// could return different uris based on filter (e.g. media -> "/media"), but i want to
|
|
||||||
// return the remote url to the user, and i don't know whether i'd need to append
|
|
||||||
// '#media' (akkoma/pleroma) or '/media' (glitch/mastodon) since i don't know anything
|
|
||||||
// about the remote instance. so, just returning the base url to the user instead
|
|
||||||
return Uri.parse(user.url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.joinmastodon.android.fragments;
|
|||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -78,7 +77,12 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
public void onMarkAsRead(String id) {
|
public void onMarkAsRead(String id) {
|
||||||
if (unreadIDs == null) return;
|
if (unreadIDs == null) return;
|
||||||
unreadIDs.remove(id);
|
unreadIDs.remove(id);
|
||||||
if (unreadIDs.isEmpty()) setResult(true, null);
|
if (unreadIDs.size() == 0) setResult(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,20 +97,13 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Announcement> result){
|
public void onSuccess(List<Announcement> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
|
||||||
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
|
||||||
onDataLoaded(unread, true);
|
onDataLoaded(unread, true);
|
||||||
onDataLoaded(read, false);
|
onDataLoaded(read, false);
|
||||||
if (unread.isEmpty()) setResult(true, null);
|
unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
||||||
else unreadIDs = unread.stream().map(a -> a.id).collect(toList());
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return isInstanceAkkoma() ? base.path("/announcements").build() : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
@@ -14,6 +13,7 @@ import android.text.Layout;
|
|||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -26,7 +26,6 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||||
@@ -34,21 +33,20 @@ import org.joinmastodon.android.model.Poll;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
|
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.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
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.PhotoViewer;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
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.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
import org.joinmastodon.android.ui.views.ImageAttachmentFrameLayout;
|
||||||
import org.joinmastodon.android.utils.TypedObjectPool;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -59,11 +57,11 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -71,31 +69,24 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends RecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop, HasFab, ProvidesAssistContent.ProvidesWebUri {
|
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost, ScrollableToTop{
|
||||||
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
|
||||||
protected DisplayItemsAdapter adapter;
|
protected DisplayItemsAdapter adapter;
|
||||||
protected String accountID;
|
protected String accountID;
|
||||||
protected PhotoViewer currentPhotoViewer;
|
protected PhotoViewer currentPhotoViewer;
|
||||||
protected ImageButton fab;
|
protected ImageButton fab;
|
||||||
protected int scrollDiff = 0;
|
protected boolean isScrollingUp = false;
|
||||||
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
protected HashMap<String, Account> knownAccounts=new HashMap<>();
|
||||||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||||
protected Rect tmpRect=new Rect();
|
protected Rect tmpRect=new Rect();
|
||||||
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
|
|
||||||
|
|
||||||
public BaseStatusListFragment(){
|
public BaseStatusListFragment(){
|
||||||
super(20);
|
super(20);
|
||||||
if (wantsComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean wantsComposeButton() {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
UiUtils.loadMaxWidth(getContext());
|
|
||||||
if(GlobalUserPreferences.disableMarquee){
|
if(GlobalUserPreferences.disableMarquee){
|
||||||
setTitleMarqueeEnabled(false);
|
setTitleMarqueeEnabled(false);
|
||||||
setSubtitleMarqueeEnabled(false);
|
setSubtitleMarqueeEnabled(false);
|
||||||
@@ -190,21 +181,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){
|
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex){
|
||||||
final Status status=_status.getContentStatus();
|
final Status status=_status.reblog!=null ? _status.reblog : _status;
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
|
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
|
||||||
private MediaAttachmentViewController transitioningHolder;
|
private ImageStatusDisplayItem.Holder<?> transitioningHolder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPhotoViewVisibility(int index, boolean visible){
|
public void setPhotoViewVisibility(int index, boolean visible){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
holder.photo.setAlpha(visible ? 1f : 0f);
|
holder.photo.setAlpha(visible ? 1f : 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null){
|
if(holder!=null){
|
||||||
transitioningHolder=holder;
|
transitioningHolder=holder;
|
||||||
View view=transitioningHolder.photo;
|
View view=transitioningHolder.photo;
|
||||||
@@ -212,8 +203,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
view.getLocationOnScreen(pos);
|
view.getLocationOnScreen(pos);
|
||||||
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
|
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
|
||||||
list.setClipChildren(false);
|
list.setClipChildren(false);
|
||||||
gridHolder.setClipChildren(false);
|
transitioningHolder.itemView.setElevation(1f);
|
||||||
transitioningHolder.view.setElevation(1f);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -240,16 +230,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
view.setTranslationY(0f);
|
view.setTranslationY(0f);
|
||||||
view.setScaleX(1f);
|
view.setScaleX(1f);
|
||||||
view.setScaleY(1f);
|
view.setScaleY(1f);
|
||||||
transitioningHolder.view.setElevation(0f);
|
transitioningHolder.itemView.setElevation(0f);
|
||||||
if(list!=null)
|
if(list!=null)
|
||||||
list.setClipChildren(true);
|
list.setClipChildren(true);
|
||||||
gridHolder.setClipChildren(true);
|
|
||||||
transitioningHolder=null;
|
transitioningHolder=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Drawable getPhotoViewCurrentDrawable(int index){
|
public Drawable getPhotoViewCurrentDrawable(int index){
|
||||||
MediaAttachmentViewController holder=findPhotoViewHolder(index);
|
ImageStatusDisplayItem.Holder<?> holder=findPhotoViewHolder(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
return holder.photo.getDrawable();
|
return holder.photo.getDrawable();
|
||||||
return null;
|
return null;
|
||||||
@@ -265,22 +254,53 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaAttachmentViewController findPhotoViewHolder(int index){
|
private ImageStatusDisplayItem.Holder<?> findPhotoViewHolder(int index){
|
||||||
return gridHolder.getViewController(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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable View getFab() {
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
if (getParentFragment() instanceof HasFab l) return l.getFab();
|
super.onViewCreated(view, savedInstanceState);
|
||||||
else return fab;
|
fab=view.findViewById(R.id.fab);
|
||||||
}
|
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showFab() {
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
View fab = getFab();
|
if(currentPhotoViewer!=null)
|
||||||
if (fab == null || fab.getVisibility() == View.VISIBLE) return;
|
currentPhotoViewer.offsetView(-dx, -dy);
|
||||||
|
|
||||||
|
if (fab!=null) {
|
||||||
|
if (dy >= 0 ) {
|
||||||
|
if (isScrollingUp) {
|
||||||
|
fab.setVisibility(View.INVISIBLE);
|
||||||
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
fab.getHeight() * 2);
|
||||||
|
animate.setDuration(300);
|
||||||
|
animate.setFillAfter(true);
|
||||||
|
fab.startAnimation(animate);
|
||||||
|
isScrollingUp = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isScrollingUp) {
|
||||||
fab.setVisibility(View.VISIBLE);
|
fab.setVisibility(View.VISIBLE);
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
0,
|
0,
|
||||||
@@ -288,45 +308,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
fab.getHeight() * 2,
|
fab.getHeight() * 2,
|
||||||
0);
|
0);
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
|
animate.setFillAfter(true);
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
}
|
isScrollingUp = true;
|
||||||
|
|
||||||
@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);
|
|
||||||
fab=view.findViewById(R.id.fab);
|
|
||||||
|
|
||||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
|
||||||
@Override
|
|
||||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
|
||||||
if(currentPhotoViewer!=null)
|
|
||||||
currentPhotoViewer.offsetView(-dx, -dy);
|
|
||||||
|
|
||||||
View fab = getFab();
|
|
||||||
if (fab!=null && GlobalUserPreferences.autoHideFab) {
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
|
||||||
hideFab();
|
|
||||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
|
||||||
if (list.getChildAt(0).getTop() == 0 || scrollDiff > 400) {
|
|
||||||
showFab();
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else {
|
|
||||||
scrollDiff += Math.abs(dy);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,14 +349,31 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
list.setItemAnimator(new BetterItemAnimator());
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
|
||||||
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
|
@Override
|
||||||
@@ -493,7 +494,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRevealSpoilerClick(MediaGridStatusDisplayItem.Holder holder){
|
public void onRevealSpoilerClick(ImageStatusDisplayItem.Holder<?> holder){
|
||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
@@ -522,58 +523,23 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
updateImagesSpoilerState(status, holder.getItemID());
|
updateImagesSpoilerState(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnableExpandable(TextStatusDisplayItem.Holder holder, boolean expandable) {
|
|
||||||
if (holder.getItem().status.textExpandable != expandable && list != null) {
|
|
||||||
holder.getItem().status.textExpandable = expandable;
|
|
||||||
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
|
||||||
if (header != null) header.rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onToggleExpanded(Status status, String itemID) {
|
|
||||||
status.textExpanded = !status.textExpanded;
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
|
||||||
if (text != null) text.rebind();
|
|
||||||
if (header != null) header.rebind();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateImagesSpoilerState(Status status, String itemID){
|
protected void updateImagesSpoilerState(Status status, String itemID){
|
||||||
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
ArrayList<Integer> updatedPositions=new ArrayList<>();
|
||||||
MediaGridStatusDisplayItem.Holder mediaGrid=findHolderOfType(itemID, MediaGridStatusDisplayItem.Holder.class);
|
for(ImageStatusDisplayItem.Holder photo:(List<ImageStatusDisplayItem.Holder>)findAllHoldersOfType(itemID, ImageStatusDisplayItem.Holder.class)){
|
||||||
if(mediaGrid!=null){
|
photo.setRevealed(status.spoilerRevealed);
|
||||||
mediaGrid.setRevealed(status.spoilerRevealed);
|
updatedPositions.add(photo.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||||
updatedPositions.add(mediaGrid.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
|
||||||
}
|
}
|
||||||
int i=0;
|
int i=0;
|
||||||
for(StatusDisplayItem item:displayItems){
|
for(StatusDisplayItem item:displayItems){
|
||||||
if(itemID.equals(item.parentID) && item instanceof MediaGridStatusDisplayItem && !updatedPositions.contains(i)){
|
if(itemID.equals(item.parentID) && item instanceof ImageStatusDisplayItem && !updatedPositions.contains(i)){
|
||||||
adapter.notifyItemChanged(i);
|
adapter.notifyItemChanged(i);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onImageUpdated(MediaGridStatusDisplayItem.Holder holder, int index) {
|
|
||||||
holder.rebind();
|
|
||||||
MediaGridStatusDisplayItem.Holder mediaGrid = findHolderOfType(holder.getItemID(), MediaGridStatusDisplayItem.Holder.class);
|
|
||||||
if(mediaGrid!=null){
|
|
||||||
adapter.notifyItemChanged(mediaGrid.getAbsoluteAdapterPosition());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
||||||
|
|
||||||
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
|
||||||
int startPos = warning.getAbsoluteAdapterPosition();
|
|
||||||
displayItems.remove(startPos);
|
|
||||||
displayItems.addAll(startPos, warning.filteredItems);
|
|
||||||
adapter.notifyItemRangeInserted(startPos, warning.filteredItems.size() - 1);
|
|
||||||
if (startPos == 0) scrollToTop();
|
|
||||||
warning.getItem().status.filterRevealed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID(){
|
public String getAccountID(){
|
||||||
return accountID;
|
return accountID;
|
||||||
}
|
}
|
||||||
@@ -689,29 +655,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
currentPhotoViewer.onPause();
|
currentPhotoViewer.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public DisplayItemsAdapter(){
|
public DisplayItemsAdapter(){
|
||||||
@@ -749,6 +692,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
return displayItems.get(position).getImageRequest(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{
|
private class StatusListItemDecoration extends RecyclerView.ItemDecoration{
|
||||||
@@ -758,7 +711,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
private int currentMediaHiddenLayoutsWidth=0;
|
private int currentMediaHiddenLayoutsWidth=0;
|
||||||
|
|
||||||
{
|
{
|
||||||
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorPollVoted));
|
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), GlobalUserPreferences.disableDividers ? R.attr.colorWindowBackground : R.attr.colorPollVoted));
|
||||||
dividerPaint.setStyle(Paint.Style.STROKE);
|
dividerPaint.setStyle(Paint.Style.STROKE);
|
||||||
dividerPaint.setStrokeWidth(V.dp(1));
|
dividerPaint.setStrokeWidth(V.dp(1));
|
||||||
}
|
}
|
||||||
@@ -782,21 +735,25 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
for(int i=0;i<parent.getChildCount();i++){
|
for(int i=0;i<parent.getChildCount();i++){
|
||||||
View child=parent.getChildAt(i);
|
View child=parent.getChildAt(i);
|
||||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||||
if(!imgHolder.getItem().status.spoilerRevealed && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
if(!imgHolder.getItem().status.spoilerRevealed && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||||
hiddenMediaPaint.setColor(0x80000000);
|
hiddenMediaPaint.setColor(0x80000000);
|
||||||
c.drawRect(child.getX(), child.getY(), child.getX()+child.getWidth(), child.getY()+child.getHeight(), hiddenMediaPaint);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int i=0;i<parent.getChildCount();i++){
|
for(int i=0;i<parent.getChildCount();i++){
|
||||||
View child=parent.getChildAt(i);
|
View child=parent.getChildAt(i);
|
||||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||||
if(holder instanceof MediaGridStatusDisplayItem.Holder imgHolder){
|
if(holder instanceof ImageStatusDisplayItem.Holder<?> imgHolder){
|
||||||
if(!imgHolder.getItem().status.spoilerRevealed){
|
if(!imgHolder.getItem().status.spoilerRevealed){
|
||||||
if(TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
PhotoLayoutHelper.TiledLayoutResult.Tile tile=imgHolder.getItem().thisTile;
|
||||||
|
if(tile.startCol==0 && tile.startRow==0 && TextUtils.isEmpty(imgHolder.getItem().status.spoilerText)){
|
||||||
int listWidth=getListWidthForMediaLayout();
|
int listWidth=getListWidthForMediaLayout();
|
||||||
int width=Math.min(listWidth, UiUtils.MAX_WIDTH);
|
int width=Math.min(listWidth, V.dp(ImageAttachmentFrameLayout.MAX_WIDTH));
|
||||||
if(currentMediaHiddenLayoutsWidth!=width)
|
if(currentMediaHiddenLayoutsWidth!=width)
|
||||||
rebuildMediaHiddenLayouts(width-V.dp(32));
|
rebuildMediaHiddenLayouts(width-V.dp(32));
|
||||||
c.save();
|
c.save();
|
||||||
@@ -821,6 +778,47 @@ 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){
|
private void rebuildMediaHiddenLayouts(int width){
|
||||||
currentMediaHiddenLayoutsWidth=width;
|
currentMediaHiddenLayoutsWidth=width;
|
||||||
String title=getString(R.string.sensitive_content);
|
String title=getString(R.string.sensitive_content);
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetBookmarkedStatuses;
|
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.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -27,7 +25,6 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -37,14 +34,4 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
|||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.ACCOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path("/bookmarks").build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import static android.os.ext.SdkExtensions.getExtensionVersion;
|
||||||
|
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
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.DRAFTS_AFTER_INSTANT;
|
||||||
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
|
||||||
import static org.joinmastodon.android.ui.utils.UiUtils.isPhotoPickerAvailable;
|
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
||||||
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
||||||
|
|
||||||
@@ -42,7 +43,6 @@ import android.text.TextWatcher;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -67,7 +67,6 @@ import android.widget.ScrollView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
|
||||||
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
@@ -91,7 +90,6 @@ import org.joinmastodon.android.events.StatusCreatedEvent;
|
|||||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.ContentType;
|
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.EmojiCategory;
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
@@ -110,7 +108,7 @@ import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan;
|
|||||||
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
import org.joinmastodon.android.utils.TransferSpeedTracker;
|
import org.joinmastodon.android.ui.utils.TransferSpeedTracker;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ComposeEditText;
|
import org.joinmastodon.android.ui.views.ComposeEditText;
|
||||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||||
@@ -118,7 +116,6 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
|
|||||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||||
import org.joinmastodon.android.utils.MastodonLanguage;
|
import org.joinmastodon.android.utils.MastodonLanguage;
|
||||||
import org.joinmastodon.android.utils.StatusTextEncoder;
|
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -155,21 +152,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
private static final int IMAGE_DESCRIPTION_RESULT=363;
|
||||||
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
|
||||||
private static final int MAX_ATTACHMENTS=4;
|
private static final int MAX_ATTACHMENTS=4;
|
||||||
private static final String GLITCH_LOCAL_ONLY_SUFFIX = "👁";
|
|
||||||
private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*");
|
|
||||||
private static final String TAG="ComposeFragment";
|
private static final String TAG="ComposeFragment";
|
||||||
|
|
||||||
public static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
// from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift
|
||||||
public static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+)|:([a-zA-Z0-9_]+))");
|
||||||
public static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
|
||||||
|
|
||||||
@SuppressLint("NewApi") // this class actually exists on 6.0
|
@SuppressLint("NewApi") // this class actually exists on 6.0
|
||||||
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
private final BreakIterator breakIterator=BreakIterator.getCharacterInstance();
|
||||||
|
|
||||||
private SizeListenerLinearLayout contentView;
|
private SizeListenerLinearLayout contentView;
|
||||||
private TextView selfName, selfUsername, selfExtraText, extraText;
|
private TextView selfName, selfUsername;
|
||||||
private ImageView selfAvatar;
|
private ImageView selfAvatar;
|
||||||
private Account self;
|
private Account self;
|
||||||
private String instanceDomain;
|
private String instanceDomain;
|
||||||
@@ -180,8 +175,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private int charCount, charLimit, trimmedCharCount;
|
private int charCount, charLimit, trimmedCharCount;
|
||||||
|
|
||||||
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
|
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
|
||||||
private PopupMenu languagePopup, contentTypePopup, visibilityPopup, draftOptionsPopup;
|
private PopupMenu languagePopup, visibilityPopup, draftOptionsPopup;
|
||||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss, contentTypeBtn;
|
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss;
|
||||||
private ImageView sensitiveIcon;
|
private ImageView sensitiveIcon;
|
||||||
private ComposeMediaLayout attachmentsView;
|
private ComposeMediaLayout attachmentsView;
|
||||||
private TextView replyText;
|
private TextView replyText;
|
||||||
@@ -205,7 +200,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private List<EmojiCategory> customEmojis;
|
private List<EmojiCategory> customEmojis;
|
||||||
private CustomEmojiPopupKeyboard emojiKeyboard;
|
private CustomEmojiPopupKeyboard emojiKeyboard;
|
||||||
private Status replyTo;
|
private Status replyTo;
|
||||||
private Status quote;
|
|
||||||
private String initialText;
|
private String initialText;
|
||||||
private String uuid;
|
private String uuid;
|
||||||
private int pollDuration=24*3600;
|
private int pollDuration=24*3600;
|
||||||
@@ -219,7 +213,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private View sendingOverlay;
|
private View sendingOverlay;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
private StatusPrivacy statusVisibility=StatusPrivacy.PUBLIC;
|
||||||
private boolean localOnly;
|
|
||||||
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
private ComposeAutocompleteSpan currentAutocompleteSpan;
|
||||||
private FrameLayout mainEditTextWrap;
|
private FrameLayout mainEditTextWrap;
|
||||||
private ComposeAutocompleteViewController autocompleteViewController;
|
private ComposeAutocompleteViewController autocompleteViewController;
|
||||||
@@ -234,18 +227,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private boolean ignoreSelectionChanges=false;
|
private boolean ignoreSelectionChanges=false;
|
||||||
private Runnable updateUploadEtaRunnable;
|
private Runnable updateUploadEtaRunnable;
|
||||||
|
|
||||||
private String language, encoding;
|
private String language;
|
||||||
private ContentType contentType;
|
|
||||||
private MastodonLanguage.LanguageResolver languageResolver;
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
|
|
||||||
|
private int navigationBarColorBefore;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
navigationBarColorBefore = getActivity().getWindow().getNavigationBarColor();
|
||||||
|
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
contentType = GlobalUserPreferences.accountsDefaultContentTypes.get(accountID);
|
|
||||||
|
|
||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
self=session.self;
|
self=session.self;
|
||||||
instanceDomain=session.domain;
|
instanceDomain=session.domain;
|
||||||
@@ -253,16 +247,16 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
||||||
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
||||||
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
redraftStatus=getArguments().getBoolean("redraftStatus", false);
|
||||||
if(getArguments().containsKey("editStatus"))
|
if(getArguments().containsKey("editStatus")){
|
||||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
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){
|
if(instance==null){
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(customEmojis.isEmpty()){
|
||||||
|
AccountSessionManager.getInstance().updateInstanceInfo(instanceDomain);
|
||||||
|
}
|
||||||
|
|
||||||
Bundle bundle = savedInstanceState != null ? savedInstanceState : getArguments();
|
Bundle bundle = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||||
if (bundle.containsKey("scheduledStatus")) scheduledStatus=Parcels.unwrap(bundle.getParcelable("scheduledStatus"));
|
if (bundle.containsKey("scheduledStatus")) scheduledStatus=Parcels.unwrap(bundle.getParcelable("scheduledStatus"));
|
||||||
@@ -287,6 +281,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
UiUtils.removeCallbacks(updateUploadEtaRunnable);
|
UiUtils.removeCallbacks(updateUploadEtaRunnable);
|
||||||
updateUploadEtaRunnable=null;
|
updateUploadEtaRunnable=null;
|
||||||
}
|
}
|
||||||
|
getActivity().getWindow().setNavigationBarColor(navigationBarColorBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -304,16 +299,30 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
emojiKeyboard.setListener(this::onCustomEmojiClick);
|
emojiKeyboard.setListener(this::onCustomEmojiClick);
|
||||||
|
|
||||||
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
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);
|
mainEditText=view.findViewById(R.id.toot_text);
|
||||||
mainEditTextWrap=view.findViewById(R.id.toot_text_wrap);
|
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);
|
scrollView=view.findViewById(R.id.scroll_view);
|
||||||
|
|
||||||
selfName=view.findViewById(R.id.self_name);
|
selfName=view.findViewById(R.id.self_name);
|
||||||
selfUsername=view.findViewById(R.id.self_username);
|
selfUsername=view.findViewById(R.id.self_username);
|
||||||
selfAvatar=view.findViewById(R.id.self_avatar);
|
selfAvatar=view.findViewById(R.id.self_avatar);
|
||||||
selfExtraText=view.findViewById(R.id.self_extra_text);
|
|
||||||
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
|
||||||
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
selfUsername.setText('@'+self.username+'@'+instanceDomain);
|
||||||
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
|
||||||
@@ -331,45 +340,22 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
emojiBtn=view.findViewById(R.id.btn_emoji);
|
emojiBtn=view.findViewById(R.id.btn_emoji);
|
||||||
spoilerBtn=view.findViewById(R.id.btn_spoiler);
|
spoilerBtn=view.findViewById(R.id.btn_spoiler);
|
||||||
visibilityBtn=view.findViewById(R.id.btn_visibility);
|
visibilityBtn=view.findViewById(R.id.btn_visibility);
|
||||||
contentTypeBtn=view.findViewById(R.id.btn_content_type);
|
|
||||||
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
|
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
|
||||||
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
|
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
|
||||||
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
|
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
|
||||||
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
|
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
|
||||||
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
|
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
|
||||||
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
sensitiveItem=view.findViewById(R.id.sensitive_item);
|
||||||
replyText=view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text : R.id.reply_text_below);
|
replyText=view.findViewById(R.id.reply_text);
|
||||||
view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text_below : R.id.reply_text)
|
|
||||||
.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (isPhotoPickerAvailable()) {
|
mediaBtn.setOnClickListener(v->openFilePicker());
|
||||||
PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn);
|
|
||||||
attachPopup.inflate(R.menu.attach);
|
|
||||||
attachPopup.setOnMenuItemClickListener(i -> {
|
|
||||||
openFilePicker(i.getItemId() == R.id.media);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), attachPopup);
|
|
||||||
mediaBtn.setOnClickListener(v->attachPopup.show());
|
|
||||||
mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener());
|
|
||||||
} else {
|
|
||||||
mediaBtn.setOnClickListener(v -> openFilePicker(false));
|
|
||||||
}
|
|
||||||
pollBtn.setOnClickListener(v->togglePoll());
|
pollBtn.setOnClickListener(v->togglePoll());
|
||||||
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText));
|
||||||
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
spoilerBtn.setOnClickListener(v->toggleSpoiler());
|
||||||
|
|
||||||
localOnly = savedInstanceState != null ? savedInstanceState.getBoolean("localOnly") :
|
|
||||||
editingStatus != null ? editingStatus.localOnly : replyTo != null && replyTo.localOnly;
|
|
||||||
|
|
||||||
buildVisibilityPopup(visibilityBtn);
|
buildVisibilityPopup(visibilityBtn);
|
||||||
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||||
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||||
|
|
||||||
buildContentTypePopup(contentTypeBtn);
|
|
||||||
contentTypeBtn.setOnClickListener(v->contentTypePopup.show());
|
|
||||||
contentTypeBtn.setOnTouchListener(contentTypePopup.getDragToOpenListener());
|
|
||||||
|
|
||||||
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
|
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
|
||||||
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
|
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
|
||||||
|
|
||||||
@@ -440,7 +426,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable());
|
||||||
spoilerEdit.setBackground(spoilerBg);
|
spoilerEdit.setBackground(spoilerBg);
|
||||||
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
if((savedInstanceState!=null && savedInstanceState.getBoolean("hasSpoiler", false)) || hasSpoiler){
|
||||||
hasSpoiler=true;
|
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
||||||
@@ -472,9 +457,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if(editingStatus!=null && editingStatus.visibility!=null) {
|
||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
|
||||||
} else if (editingStatus != null && editingStatus.visibility != null) {
|
|
||||||
statusVisibility=editingStatus.visibility;
|
statusVisibility=editingStatus.visibility;
|
||||||
} else {
|
} else {
|
||||||
loadDefaultStatusVisibility(savedInstanceState);
|
loadDefaultStatusVisibility(savedInstanceState);
|
||||||
@@ -486,23 +469,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
case UNLISTED -> R.id.vis_unlisted;
|
case UNLISTED -> R.id.vis_unlisted;
|
||||||
case PRIVATE -> R.id.vis_followers;
|
case PRIVATE -> R.id.vis_followers;
|
||||||
case DIRECT -> R.id.vis_private;
|
case DIRECT -> R.id.vis_private;
|
||||||
case LOCAL -> R.id.vis_local;
|
|
||||||
}).setChecked(true);
|
}).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=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||||
@@ -529,7 +496,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
||||||
}
|
}
|
||||||
outState.putBoolean("sensitive", sensitive);
|
outState.putBoolean("sensitive", sensitive);
|
||||||
outState.putBoolean("localOnly", localOnly);
|
|
||||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||||
outState.putString("language", language);
|
outState.putString("language", language);
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
@@ -540,7 +506,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
outState.putParcelableArrayList("attachments", serializedAttachments);
|
outState.putParcelableArrayList("attachments", serializedAttachments);
|
||||||
}
|
}
|
||||||
outState.putSerializable("visibility", statusVisibility);
|
outState.putSerializable("visibility", statusVisibility);
|
||||||
outState.putSerializable("contentType", contentType);
|
|
||||||
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
|
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
|
||||||
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
|
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
|
||||||
}
|
}
|
||||||
@@ -633,8 +598,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||||
if(replyTo!=null || quote!=null){
|
if(replyTo!=null){
|
||||||
Status status = quote!=null ? quote : replyTo;
|
|
||||||
View replyWrap = view.findViewById(R.id.reply_wrap);
|
View replyWrap = view.findViewById(R.id.reply_wrap);
|
||||||
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||||
int scrollHeight = scrollView.getHeight();
|
int scrollHeight = scrollView.getHeight();
|
||||||
@@ -655,18 +619,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
View originalPost = view.findViewById(R.id.original_post);
|
View originalPost = view.findViewById(R.id.original_post);
|
||||||
extraText = view.findViewById(R.id.extra_text);
|
|
||||||
originalPost.setVisibility(View.VISIBLE);
|
originalPost.setVisibility(View.VISIBLE);
|
||||||
originalPost.setOnClickListener(v->{
|
originalPost.setOnClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("status", Parcels.wrap(status));
|
args.putParcelable("status", Parcels.wrap(replyTo));
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageView avatar = view.findViewById(R.id.avatar);
|
ImageView avatar = view.findViewById(R.id.avatar);
|
||||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(status.account.avatar));
|
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(replyTo.account.avatar));
|
||||||
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
|
||||||
@Override
|
@Override
|
||||||
public void getOutline(View view, Outline outline){
|
public void getOutline(View view, Outline outline){
|
||||||
@@ -678,56 +641,51 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
avatar.setOnClickListener(v->{
|
avatar.setOnClickListener(v->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(status.account));
|
args.putParcelable("profileAccount", Parcels.wrap(replyTo.account));
|
||||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||||
});
|
});
|
||||||
|
|
||||||
((TextView) view.findViewById(R.id.name)).setText(status.account.displayName);
|
((TextView) view.findViewById(R.id.name)).setText(replyTo.account.displayName);
|
||||||
((TextView) view.findViewById(R.id.username)).setText(status.account.getDisplayUsername());
|
((TextView) view.findViewById(R.id.username)).setText(replyTo.account.getDisplayUsername());
|
||||||
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
view.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||||
Drawable visibilityIcon = getActivity().getDrawable(switch(status.visibility){
|
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular;
|
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled;
|
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
|
||||||
case LOCAL -> R.drawable.ic_fluent_eye_20_regular;
|
|
||||||
});
|
});
|
||||||
ImageView moreBtn = view.findViewById(R.id.more);
|
ImageView moreBtn = view.findViewById(R.id.more);
|
||||||
moreBtn.setImageDrawable(visibilityIcon);
|
moreBtn.setImageDrawable(visibilityIcon);
|
||||||
moreBtn.setBackground(null);
|
moreBtn.setBackground(null);
|
||||||
TextView timestamp = view.findViewById(R.id.timestamp);
|
TextView timestamp = view.findViewById(R.id.timestamp);
|
||||||
if (status.editedAt!=null) timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt)));
|
if (replyTo.editedAt==null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), replyTo.createdAt));
|
||||||
else if (status.createdAt!=null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), status.createdAt));
|
else timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), replyTo.editedAt)));
|
||||||
else timestamp.setText("");
|
if (replyTo.spoilerText != null && !replyTo.spoilerText.isBlank()) {
|
||||||
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
|
|
||||||
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
|
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
|
||||||
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(status.spoilerText);
|
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(replyTo.spoilerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpannableStringBuilder content = HtmlParser.parse(status.content, status.emojis, status.mentions, status.tags, accountID);
|
SpannableStringBuilder content = HtmlParser.parse(replyTo.content, replyTo.emojis, replyTo.mentions, replyTo.tags, accountID);
|
||||||
LinkedTextView text = view.findViewById(R.id.text);
|
LinkedTextView text = view.findViewById(R.id.text);
|
||||||
if (content.length() > 0) text.setText(content);
|
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)));
|
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
|
||||||
|
|
||||||
replyText.setText(getString(quote!=null? R.string.sk_quoting_user : R.string.in_reply_to, status.account.displayName));
|
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||||
int visibilityNameRes = switch (status.visibility) {
|
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + UiUtils.getVisibilityText(replyTo));
|
||||||
case PUBLIC -> R.string.visibility_public;
|
replyText.setOnClickListener(v->{
|
||||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
scrollView.smoothScrollTo(0, 0);
|
||||||
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, status.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
|
||||||
replyText.setOnClickListener(v->{
|
replyText.setOnClickListener(v->{
|
||||||
scrollView.smoothScrollTo(0, 0);
|
scrollView.smoothScrollTo(0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ArrayList<String> mentions=new ArrayList<>();
|
ArrayList<String> mentions=new ArrayList<>();
|
||||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
||||||
if(!status.account.id.equals(ownID))
|
if(!replyTo.account.id.equals(ownID))
|
||||||
mentions.add('@'+status.account.acct);
|
mentions.add('@'+replyTo.account.acct);
|
||||||
for(Mention mention:status.mentions){
|
for(Mention mention:replyTo.mentions){
|
||||||
if(mention.id.equals(ownID))
|
if(mention.id.equals(ownID))
|
||||||
continue;
|
continue;
|
||||||
String m='@'+mention.acct;
|
String m='@'+mention.acct;
|
||||||
@@ -740,17 +698,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=true;
|
ignoreSelectionChanges=true;
|
||||||
mainEditText.setSelection(mainEditText.length());
|
mainEditText.setSelection(mainEditText.length());
|
||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
if(GlobalUserPreferences.prefixRepliesWithRe && !status.spoilerText.startsWith("re: ")){
|
spoilerEdit.setText(replyTo.spoilerText);
|
||||||
spoilerEdit.setText("re: " + status.spoilerText);
|
|
||||||
}else{
|
|
||||||
spoilerEdit.setText(status.spoilerText);
|
|
||||||
}
|
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}
|
}
|
||||||
if (status.language != null && !status.language.isEmpty()) updateLanguage(status.language);
|
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||||
}
|
}
|
||||||
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
||||||
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
||||||
@@ -801,7 +755,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSensitive();
|
updateSensitive();
|
||||||
updateHeaders();
|
|
||||||
|
|
||||||
if(editingStatus!=null){
|
if(editingStatus!=null){
|
||||||
updateCharCounter();
|
updateCharCounter();
|
||||||
@@ -817,7 +770,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
item.setActionView(wrap);
|
item.setActionView(wrap);
|
||||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
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 = 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);
|
||||||
draftOptionsPopup = new PopupMenu(getContext(), draftsBtn);
|
draftOptionsPopup = new PopupMenu(getContext(), draftsBtn);
|
||||||
draftOptionsPopup.inflate(R.menu.compose_more);
|
draftOptionsPopup.inflate(R.menu.compose_more);
|
||||||
draftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.draft);
|
draftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.draft);
|
||||||
@@ -834,21 +800,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
});
|
});
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), draftOptionsPopup);
|
UiUtils.enablePopupMenuIcons(getContext(), draftOptionsPopup);
|
||||||
|
|
||||||
publishButton = wrap.findViewById(R.id.publish_btn);
|
|
||||||
languageButton = wrap.findViewById(R.id.language_btn);
|
languageButton = wrap.findViewById(R.id.language_btn);
|
||||||
sendProgress = wrap.findViewById(R.id.send_progress);
|
sendProgress = wrap.findViewById(R.id.send_progress);
|
||||||
sendError = wrap.findViewById(R.id.send_error);
|
sendError = wrap.findViewById(R.id.send_error);
|
||||||
|
|
||||||
publishButton.setOnClickListener(this::onPublishClick);
|
|
||||||
draftsBtn.setOnClickListener(v-> draftOptionsPopup.show());
|
draftsBtn.setOnClickListener(v-> draftOptionsPopup.show());
|
||||||
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
|
||||||
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
|
||||||
buildLanguageSelector(languageButton);
|
buildLanguageSelector(languageButton);
|
||||||
|
|
||||||
if (editingStatus != null && scheduledStatus == null) {
|
|
||||||
// editing an already published post
|
|
||||||
draftsBtn.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateToUnsentPosts() {
|
private void navigateToUnsentPosts() {
|
||||||
@@ -872,13 +832,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateLanguage(MastodonLanguage loc) {
|
private void updateLanguage(MastodonLanguage loc) {
|
||||||
updateLanguage(loc.getLanguage(), loc.getLanguageName(), loc.getDefaultName());
|
language = loc.getLanguage();
|
||||||
}
|
languageButton.setText(loc.getLanguageName());
|
||||||
|
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
|
||||||
private void updateLanguage(String languageTag, String languageName, String defaultName) {
|
|
||||||
language = languageTag;
|
|
||||||
languageButton.setText(languageName);
|
|
||||||
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, defaultName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@@ -888,20 +844,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
btn.setOnClickListener(v->languagePopup.show());
|
btn.setOnClickListener(v->languagePopup.show());
|
||||||
|
|
||||||
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
if (language != null) updateLanguage(language);
|
updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
||||||
else updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
|
|
||||||
? languageResolver.from(prefs.postingDefaultLanguage)
|
? languageResolver.from(prefs.postingDefaultLanguage)
|
||||||
: languageResolver.getDefault());
|
: languageResolver.getDefault());
|
||||||
|
|
||||||
Menu languageMenu = languagePopup.getMenu();
|
Menu languageMenu = languagePopup.getMenu();
|
||||||
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
||||||
if (recentLanguage.equals("bottom")) {
|
|
||||||
addBottomLanguage(languageMenu);
|
|
||||||
} else {
|
|
||||||
MastodonLanguage l = languageResolver.from(recentLanguage);
|
MastodonLanguage l = languageResolver.from(recentLanguage);
|
||||||
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
||||||
for (int i = 0; i < allLanguages.size(); i++) {
|
for (int i = 0; i < allLanguages.size(); i++) {
|
||||||
@@ -909,44 +860,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
|
||||||
|
|
||||||
btn.setOnLongClickListener(v->{
|
|
||||||
btn.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
||||||
if (!GlobalUserPreferences.bottomEncoding) addBottomLanguage(allLanguagesMenu);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
languagePopup.setOnMenuItemClickListener(i->{
|
languagePopup.setOnMenuItemClickListener(i->{
|
||||||
if (i.hasSubMenu()) return false;
|
if (i.hasSubMenu()) return false;
|
||||||
if (i.getItemId() == allLanguages.size()) {
|
|
||||||
updateLanguage(language, "\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48", "bottom");
|
|
||||||
encoding = "bottom";
|
|
||||||
} else {
|
|
||||||
updateLanguage(allLanguages.get(i.getItemId()));
|
updateLanguage(allLanguages.get(i.getItemId()));
|
||||||
encoding = null;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
return true;
|
return true;
|
||||||
@@ -976,9 +896,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(hasSpoiler){
|
if(hasSpoiler){
|
||||||
charCount+=spoilerEdit.length();
|
charCount+=spoilerEdit.length();
|
||||||
}
|
}
|
||||||
if (localOnly && GlobalUserPreferences.accountsInGlitchMode.contains(accountID)) {
|
|
||||||
charCount -= GLITCH_LOCAL_ONLY_SUFFIX.length();
|
|
||||||
}
|
|
||||||
charCounter.setText(String.valueOf(charLimit-charCount));
|
charCounter.setText(String.valueOf(charLimit-charCount));
|
||||||
trimmedCharCount=text.toString().trim().length();
|
trimmedCharCount=text.toString().trim().length();
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
@@ -986,6 +903,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
|
|
||||||
private void resetPublishButtonText() {
|
private void resetPublishButtonText() {
|
||||||
int publishText = editingStatus==null || redraftStatus ? R.string.publish : R.string.save;
|
int publishText = editingStatus==null || redraftStatus ? R.string.publish : R.string.save;
|
||||||
|
if(GlobalUserPreferences.relocatePublishButton){
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (publishText == R.string.publish && !GlobalUserPreferences.publishButtonText.isEmpty()) {
|
if (publishText == R.string.publish && !GlobalUserPreferences.publishButtonText.isEmpty()) {
|
||||||
publishButton.setText(GlobalUserPreferences.publishButtonText);
|
publishButton.setText(GlobalUserPreferences.publishButtonText);
|
||||||
} else {
|
} else {
|
||||||
@@ -1012,11 +932,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onCustomEmojiClick(Emoji emoji){
|
private void onCustomEmojiClick(Emoji emoji){
|
||||||
if(getActivity().getCurrentFocus() instanceof EditText edit){
|
int start=mainEditText.getSelectionStart();
|
||||||
int start=edit.getSelectionStart();
|
String prefix=start>0 && !Character.isWhitespace(mainEditText.getText().charAt(start-1)) ? " :" : ":";
|
||||||
String prefix=start>0 && !Character.isWhitespace(edit.getText().charAt(start-1)) ? " :" : ":";
|
mainEditText.getText().replace(start, mainEditText.getSelectionEnd(), prefix+emoji.shortcode+':');
|
||||||
edit.getText().replace(start, edit.getSelectionEnd(), prefix+emoji.shortcode+':');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1026,8 +944,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onPublishClick(View v){
|
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.sk_no_image_desc_title)
|
||||||
|
.setMessage(R.string.sk_no_image_desc)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.publish, (dialog, i)-> publish())
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
publish();
|
publish();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void publishErrorCallback(ErrorResponse error) {
|
private void publishErrorCallback(ErrorResponse error) {
|
||||||
wm.removeView(sendingOverlay);
|
wm.removeView(sendingOverlay);
|
||||||
@@ -1067,54 +996,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void publish(){
|
private void publish(){
|
||||||
publish(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void publish(boolean force){
|
|
||||||
String text=mainEditText.getText().toString();
|
String text=mainEditText.getText().toString();
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
if ("bottom".equals(encoding)) {
|
|
||||||
text = new StatusTextEncoder(Bottom::encode).encode(text);
|
|
||||||
req.spoilerText = "bottom-encoded emoji spam";
|
|
||||||
}
|
|
||||||
if (localOnly &&
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.contains(accountID) &&
|
|
||||||
!GLITCH_LOCAL_ONLY_PATTERN.matcher(text).matches()) {
|
|
||||||
text += " " + GLITCH_LOCAL_ONLY_SUFFIX;
|
|
||||||
}
|
|
||||||
req.status=text;
|
req.status=text;
|
||||||
req.localOnly=localOnly;
|
req.visibility=statusVisibility;
|
||||||
req.visibility=localOnly && instance.isPleroma() ? StatusPrivacy.LOCAL : statusVisibility;
|
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
req.language=language;
|
req.language=language;
|
||||||
req.contentType=contentType;
|
|
||||||
req.scheduledAt = scheduledAt;
|
req.scheduledAt = scheduledAt;
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||||
Optional<DraftMediaAttachment> withoutAltText = attachments.stream().filter(a -> a.description == null || a.description.isBlank()).findFirst();
|
|
||||||
boolean isDraft = scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT);
|
|
||||||
if (!force && !GlobalUserPreferences.disableAltTextReminder && !isDraft && withoutAltText.isPresent()) {
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_alt_text_missing_title)
|
|
||||||
.setMessage(R.string.sk_alt_text_missing)
|
|
||||||
.setPositiveButton(R.string.add_alt_text, (d, w) -> editMediaDescription(withoutAltText.get()))
|
|
||||||
.setNegativeButton(R.string.sk_publish_anyway, (d, w) -> publish(true))
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ask whether to publish now when editing an existing draft
|
|
||||||
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_save_draft)
|
|
||||||
.setMessage(R.string.sk_save_draft_message)
|
|
||||||
.setPositiveButton(R.string.save, (d, w) -> publish(true))
|
|
||||||
.setNegativeButton(R.string.publish, (d, w) -> {
|
|
||||||
updateScheduledAt(null);
|
|
||||||
publish();
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
if(replyTo!=null || (editingStatus != null && editingStatus.inReplyToId!=null)){
|
||||||
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
req.inReplyToId=editingStatus!=null ? editingStatus.inReplyToId : replyTo.id;
|
||||||
@@ -1129,9 +1019,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(hasSpoiler && spoilerEdit.length()>0){
|
if(hasSpoiler && spoilerEdit.length()>0){
|
||||||
req.spoilerText=spoilerEdit.getText().toString();
|
req.spoilerText=spoilerEdit.getText().toString();
|
||||||
}
|
}
|
||||||
if(quote != null){
|
|
||||||
req.quoteId=quote.id;
|
|
||||||
}
|
|
||||||
if(uuid==null)
|
if(uuid==null)
|
||||||
uuid=UUID.randomUUID().toString();
|
uuid=UUID.randomUUID().toString();
|
||||||
|
|
||||||
@@ -1164,9 +1051,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}else{
|
}else{
|
||||||
E.post(new StatusUpdatedEvent(result));
|
E.post(new StatusUpdatedEvent(result));
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) {
|
|
||||||
Nav.finish(ComposeFragment.this);
|
Nav.finish(ComposeFragment.this);
|
||||||
}
|
|
||||||
if (getArguments().getBoolean("navigateToStatus", false)) {
|
if (getArguments().getBoolean("navigateToStatus", false)) {
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -1183,6 +1068,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if(editingStatus!=null && !redraftStatus){
|
if(editingStatus!=null && !redraftStatus){
|
||||||
new EditStatus(req, editingStatus.id)
|
new EditStatus(req, editingStatus.id)
|
||||||
.setCallback(resCallback)
|
.setCallback(resCallback)
|
||||||
@@ -1224,14 +1110,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
||||||
newRecentLanguages.remove(language);
|
newRecentLanguages.remove(language);
|
||||||
newRecentLanguages.add(0, language);
|
newRecentLanguages.add(0, language);
|
||||||
if (encoding != null) {
|
|
||||||
newRecentLanguages.remove(encoding);
|
|
||||||
newRecentLanguages.add(0, encoding);
|
|
||||||
}
|
|
||||||
if ("bottom".equals(encoding) && !GlobalUserPreferences.bottomEncoding) {
|
|
||||||
GlobalUserPreferences.bottomEncoding = true;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}
|
}
|
||||||
@@ -1297,14 +1175,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void confirmDiscardDraftAndFinish(){
|
private void confirmDiscardDraftAndFinish(){
|
||||||
boolean attachmentsPending = attachments.stream().anyMatch(att -> att.state != AttachmentUploadState.DONE);
|
new M3AlertDialogBuilder(getActivity())
|
||||||
if (attachmentsPending) new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_unfinished_attachments)
|
|
||||||
.setMessage(R.string.sk_unfinished_attachments_message)
|
|
||||||
.setPositiveButton(R.string.edit, (d, w) -> {})
|
|
||||||
.setNegativeButton(R.string.discard, (d, w) -> Nav.finish(this))
|
|
||||||
.show();
|
|
||||||
else new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
|
||||||
.setPositiveButton(R.string.save, (d, w) -> {
|
.setPositiveButton(R.string.save, (d, w) -> {
|
||||||
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
|
||||||
@@ -1314,6 +1185,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if Android platform photopicker is available on the device\
|
||||||
|
* @return whether the device supports photopicker intents.
|
||||||
|
*/
|
||||||
|
private boolean isPhotoPickerAvailable() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return true;
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
return getExtensionVersion(Build.VERSION_CODES.R) >= 2;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the correct intent for the device version to select media.
|
* Builds the correct intent for the device version to select media.
|
||||||
@@ -1323,12 +1206,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
*
|
*
|
||||||
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
* <p>For earlier versions use the built in docs ui via {@link Intent#ACTION_GET_CONTENT}
|
||||||
*/
|
*/
|
||||||
private void openFilePicker(boolean photoPicker){
|
private void openFilePicker(){
|
||||||
Intent intent;
|
Intent intent;
|
||||||
boolean usePhotoPicker=photoPicker && isPhotoPickerAvailable();
|
boolean usePhotoPicker = isPhotoPickerAvailable();
|
||||||
if (usePhotoPicker) {
|
if (usePhotoPicker) {
|
||||||
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MAX_ATTACHMENTS-getMediaAttachmentsCount());
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit());
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
@@ -1582,7 +1465,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(att.isUploadingOrProcessing())
|
if(att.isUploadingOrProcessing())
|
||||||
att.cancelUpload();
|
att.cancelUpload();
|
||||||
attachments.remove(att);
|
attachments.remove(att);
|
||||||
if(!areThereAnyUploadingAttachments())
|
|
||||||
uploadNextQueuedAttachment();
|
uploadNextQueuedAttachment();
|
||||||
attachmentsView.removeView(att.view);
|
attachmentsView.removeView(att.view);
|
||||||
if(getMediaAttachmentsCount()==0)
|
if(getMediaAttachmentsCount()==0)
|
||||||
@@ -1688,10 +1570,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
DraftMediaAttachment att=(DraftMediaAttachment) v.getTag();
|
DraftMediaAttachment att=(DraftMediaAttachment) v.getTag();
|
||||||
if(att.serverAttachment==null)
|
if(att.serverAttachment==null)
|
||||||
return;
|
return;
|
||||||
editMediaDescription(att);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void editMediaDescription(DraftMediaAttachment att) {
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("attachment", att.serverAttachment.id);
|
args.putString("attachment", att.serverAttachment.id);
|
||||||
@@ -1740,24 +1618,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
pollChanged=true;
|
pollChanged=true;
|
||||||
updatePublishButtonState();
|
updatePublishButtonState();
|
||||||
}));
|
}));
|
||||||
|
option.edit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0 ? instance.configuration.polls.maxCharactersPerOption : 50)});
|
||||||
int maxCharactersPerOption = 50;
|
|
||||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
|
||||||
maxCharactersPerOption = instance.configuration.polls.maxCharactersPerOption;
|
|
||||||
else if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
|
||||||
maxCharactersPerOption = instance.pollLimits.maxOptionChars;
|
|
||||||
option.edit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxCharactersPerOption)});
|
|
||||||
|
|
||||||
pollOptionsView.addView(option.view);
|
pollOptionsView.addView(option.view);
|
||||||
pollOptions.add(option);
|
pollOptions.add(option);
|
||||||
|
if(pollOptions.size()==(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0 ? instance.configuration.polls.maxOptions : 4))
|
||||||
int maxPollOptions = 4;
|
|
||||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
|
||||||
maxPollOptions = instance.configuration.polls.maxOptions;
|
|
||||||
else if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
|
||||||
maxPollOptions = instance.pollLimits.maxOptions;
|
|
||||||
|
|
||||||
if(pollOptions.size()==maxPollOptions)
|
|
||||||
addPollOptionBtn.setVisibility(View.GONE);
|
addPollOptionBtn.setVisibility(View.GONE);
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
@@ -1781,20 +1646,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
menu.getMenu().add(0, 2, 0, getResources().getQuantityString(R.plurals.x_minutes, 30, 30));
|
||||||
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
menu.getMenu().add(0, 3, 0, getResources().getQuantityString(R.plurals.x_hours, 1, 1));
|
||||||
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
menu.getMenu().add(0, 4, 0, getResources().getQuantityString(R.plurals.x_hours, 6, 6));
|
||||||
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_hours, 12, 12));
|
menu.getMenu().add(0, 5, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
||||||
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 1, 1));
|
menu.getMenu().add(0, 6, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
||||||
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 3, 3));
|
menu.getMenu().add(0, 7, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
||||||
menu.getMenu().add(0, 8, 0, getResources().getQuantityString(R.plurals.x_days, 7, 7));
|
|
||||||
menu.setOnMenuItemClickListener(item->{
|
menu.setOnMenuItemClickListener(item->{
|
||||||
pollDuration=switch(item.getItemId()){
|
pollDuration=switch(item.getItemId()){
|
||||||
case 1 -> 5*60;
|
case 1 -> 5*60;
|
||||||
case 2 -> 30*60;
|
case 2 -> 30*60;
|
||||||
case 3 -> 3600;
|
case 3 -> 3600;
|
||||||
case 4 -> 6*3600;
|
case 4 -> 6*3600;
|
||||||
case 5 -> 12*3600;
|
case 5 -> 24*3600;
|
||||||
case 6 -> 24*3600;
|
case 6 -> 3*24*3600;
|
||||||
case 7 -> 3*24*3600;
|
case 7 -> 7*24*3600;
|
||||||
case 8 -> 7*24*3600;
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
default -> throw new IllegalStateException("Unexpected value: "+item.getItemId());
|
||||||
};
|
};
|
||||||
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr=item.getTitle().toString()));
|
||||||
@@ -1862,9 +1725,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
scheduleDraftText.setText(R.string.sk_compose_draft);
|
scheduleDraftText.setText(R.string.sk_compose_draft);
|
||||||
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_regular, 0, 0, 0);
|
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_regular, 0, 0, 0);
|
||||||
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_draft));
|
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_draft));
|
||||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_filled, 0, 0, 0);
|
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)
|
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
|
||||||
? R.string.save : R.string.sk_draft);
|
? R.string.save : R.string.sk_draft);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
scheduleMenuItem.setVisible(false);
|
scheduleMenuItem.setVisible(false);
|
||||||
unscheduleMenuItem.setVisible(true);
|
unscheduleMenuItem.setVisible(true);
|
||||||
@@ -1874,12 +1743,21 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
scheduleDraftText.setText(R.string.sk_compose_scheduled);
|
scheduleDraftText.setText(R.string.sk_compose_scheduled);
|
||||||
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||||
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_schedule));
|
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_schedule));
|
||||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_filled, 0, 0, 0);
|
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)
|
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.equals(scheduledAt)
|
||||||
? R.string.save : R.string.sk_schedule);
|
? R.string.save : R.string.sk_schedule);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_regular, 0, 0, 0);
|
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);
|
||||||
|
}
|
||||||
resetPublishButtonText();
|
resetPublishButtonText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1888,33 +1766,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
return attachments.size();
|
return attachments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHeaders() {
|
|
||||||
UiUtils.setExtraTextInfo(getContext(), selfExtraText, statusVisibility, localOnly);
|
|
||||||
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, replyTo.visibility, replyTo.localOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildVisibilityPopup(View v){
|
private void buildVisibilityPopup(View v){
|
||||||
visibilityPopup=new PopupMenu(getActivity(), v);
|
visibilityPopup=new PopupMenu(getActivity(), v);
|
||||||
visibilityPopup.inflate(R.menu.compose_visibility);
|
visibilityPopup.inflate(R.menu.compose_visibility);
|
||||||
Menu m=visibilityPopup.getMenu();
|
Menu m=visibilityPopup.getMenu();
|
||||||
MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only);
|
|
||||||
boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
|
||||||
if (instance.isPleroma()) {
|
|
||||||
m.findItem(R.id.vis_local).setVisible(true);
|
|
||||||
} else if (localOnly || prefsSaysSupported) {
|
|
||||||
localOnlyItem.setVisible(true);
|
|
||||||
localOnlyItem.setChecked(localOnly);
|
|
||||||
Status status = editingStatus != null ? editingStatus : replyTo;
|
|
||||||
if (!prefsSaysSupported) {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
|
||||||
if (GLITCH_LOCAL_ONLY_PATTERN.matcher(status.getStrippedText()).matches()) {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
}
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
UiUtils.enablePopupMenuIcons(getActivity(), visibilityPopup);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) m.setGroupDividerEnabled(true);
|
m.setGroupCheckable(0, true, true);
|
||||||
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
visibilityPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item){
|
public boolean onMenuItemClick(MenuItem item){
|
||||||
@@ -1927,61 +1784,35 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility=StatusPrivacy.PRIVATE;
|
statusVisibility=StatusPrivacy.PRIVATE;
|
||||||
}else if(id==R.id.vis_private){
|
}else if(id==R.id.vis_private){
|
||||||
statusVisibility=StatusPrivacy.DIRECT;
|
statusVisibility=StatusPrivacy.DIRECT;
|
||||||
}else if(id==R.id.vis_local){
|
|
||||||
statusVisibility=StatusPrivacy.LOCAL;
|
|
||||||
}
|
}
|
||||||
if (id == R.id.local_only) {
|
|
||||||
localOnly = !item.isChecked();
|
|
||||||
item.setChecked(localOnly);
|
|
||||||
} else {
|
|
||||||
item.setChecked(true);
|
item.setChecked(true);
|
||||||
}
|
|
||||||
updateVisibilityIcon();
|
updateVisibilityIcon();
|
||||||
updateHeaders();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@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) {
|
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||||
if(replyTo != null) statusVisibility = replyTo.visibility;
|
if(getArguments().containsKey("replyTo")){
|
||||||
|
replyTo=Parcels.unwrap(getArguments().getParcelable("replyTo"));
|
||||||
|
statusVisibility = replyTo.visibility;
|
||||||
|
}
|
||||||
|
|
||||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
// A saved privacy setting from a previous compose session wins over the reply visibility
|
||||||
Preferences prefs = asm.getAccount(accountID).preferences;
|
if(savedInstanceState !=null){
|
||||||
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
// Only override the reply visibility if our preference is more private
|
// Only override the reply visibility if our preference is more private
|
||||||
// (and we're not replying to ourselves, or not at all)
|
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
||||||
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility) &&
|
statusVisibility = switch (prefs.postingDefaultVisibility) {
|
||||||
(replyTo == null || !asm.isSelf(accountID, replyTo.account))) {
|
case PUBLIC -> StatusPrivacy.PUBLIC;
|
||||||
statusVisibility = prefs.postingDefaultVisibility;
|
case UNLISTED -> StatusPrivacy.UNLISTED;
|
||||||
}
|
case PRIVATE -> StatusPrivacy.PRIVATE;
|
||||||
|
case DIRECT -> StatusPrivacy.DIRECT;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over all
|
// A saved privacy setting from a previous compose session wins over all
|
||||||
@@ -1989,6 +1820,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateVisibilityIcon(){
|
private void updateVisibilityIcon(){
|
||||||
if(statusVisibility==null){ // TODO find out why this happens
|
if(statusVisibility==null){ // TODO find out why this happens
|
||||||
@@ -1996,10 +1828,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
visibilityBtn.setImageResource(switch(statusVisibility){
|
visibilityBtn.setImageResource(switch(statusVisibility){
|
||||||
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
case PUBLIC -> R.drawable.ic_fluent_earth_24_regular;
|
||||||
case UNLISTED -> R.drawable.ic_fluent_lock_open_24_regular;
|
case UNLISTED -> R.drawable.ic_fluent_people_community_24_regular;
|
||||||
case PRIVATE -> R.drawable.ic_fluent_lock_closed_24_filled;
|
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_24_regular;
|
||||||
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
case DIRECT -> R.drawable.ic_fluent_mention_24_regular;
|
||||||
case LOCAL -> R.drawable.ic_fluent_eye_24_regular;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetConversationsTimeline;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class ConversationsTimelineFragment extends FabStatusListFragment {
|
||||||
|
private HomeTabFragment parent;
|
||||||
|
private String maxID;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
if (getParentFragment() instanceof HomeTabFragment home) parent = home;
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Status> filterPosts(List<Status> items) {
|
||||||
|
// Disabling this for DMs, because there are no boosts on DMs, and most of them are replies
|
||||||
|
return items.stream().filter(i ->
|
||||||
|
(i.visibility == StatusPrivacy.DIRECT)
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
// return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
AccountSessionManager.getInstance()
|
||||||
|
.getAccount(accountID).getCacheController()
|
||||||
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
|
maxID=result.maxID;
|
||||||
|
if(result.isFromCache())
|
||||||
|
loadNewPosts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||||
|
if(parent != null && parent.isNewPostsBtnShown() && list.getChildAdapterPosition(list.getChildAt(0))<=getMainAdapterOffset()){
|
||||||
|
parent.hideNewPostsButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad")){
|
||||||
|
if(!loaded && !dataLoading){
|
||||||
|
loadData();
|
||||||
|
}else if(!dataLoading){
|
||||||
|
loadNewPosts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStatusCreated(StatusCreatedEvent ev){
|
||||||
|
prependItems(Collections.singletonList(ev.status), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadNewPosts(){
|
||||||
|
if (!GlobalUserPreferences.loadNewPosts) return;
|
||||||
|
dataLoading=true;
|
||||||
|
// The idea here is that we request the timeline such that if there are fewer than `limit` posts,
|
||||||
|
// we'll get the currently topmost post as last in the response. This way we know there's no gap
|
||||||
|
// between the existing and newly loaded parts of the timeline.
|
||||||
|
String sinceID=data.size()>1 ? data.get(1).id : "1";
|
||||||
|
currentRequest=new GetConversationsTimeline(null, null, 20, sinceID)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
result = filterPosts(result);
|
||||||
|
if(result.isEmpty() || getActivity()==null)
|
||||||
|
return;
|
||||||
|
Status last=result.get(result.size()-1);
|
||||||
|
List<Status> toAdd;
|
||||||
|
if(!data.isEmpty() && last.id.equals(data.get(0).id)){ // This part intersects with the existing one
|
||||||
|
toAdd=result.subList(0, result.size()-1); // Remove the already known last post
|
||||||
|
}else{
|
||||||
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
|
toAdd=result;
|
||||||
|
}
|
||||||
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||||
|
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||||
|
if(!toAdd.isEmpty()){
|
||||||
|
prependItems(toAdd, true);
|
||||||
|
if (parent != null) parent.showNewPostsButton();
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomTimeline(toAdd, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGapClick(GapStatusDisplayItem.Holder item){
|
||||||
|
if(dataLoading)
|
||||||
|
return;
|
||||||
|
item.getItem().loading=true;
|
||||||
|
V.setVisibilityAnimated(item.progress, View.VISIBLE);
|
||||||
|
V.setVisibilityAnimated(item.text, View.GONE);
|
||||||
|
GapStatusDisplayItem gap=item.getItem();
|
||||||
|
dataLoading=true;
|
||||||
|
currentRequest=new GetConversationsTimeline(item.getItemID(), null, 20, null)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
int gapPos=displayItems.indexOf(gap);
|
||||||
|
if(gapPos==-1)
|
||||||
|
return;
|
||||||
|
if(result.isEmpty()){
|
||||||
|
displayItems.remove(gapPos);
|
||||||
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
|
Status gapStatus=getStatusByID(gap.parentID);
|
||||||
|
if(gapStatus!=null){
|
||||||
|
gapStatus.hasGapAfter=false;
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(Collections.singletonList(gapStatus), false);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
Set<String> idsBelowGap=new HashSet<>();
|
||||||
|
boolean belowGap=false;
|
||||||
|
int gapPostIndex=0;
|
||||||
|
for(Status s:data){
|
||||||
|
if(belowGap){
|
||||||
|
idsBelowGap.add(s.id);
|
||||||
|
}else if(s.id.equals(gap.parentID)){
|
||||||
|
belowGap=true;
|
||||||
|
s.hasGapAfter=false;
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(Collections.singletonList(s), false);
|
||||||
|
}else{
|
||||||
|
gapPostIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int endIndex=0;
|
||||||
|
for(Status s:result){
|
||||||
|
endIndex++;
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(endIndex==result.size()){
|
||||||
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
|
}else{
|
||||||
|
result=result.subList(0, endIndex);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
for(Status s:result){
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
if(filterPredicate.test(s)){
|
||||||
|
targetList.addAll(buildDisplayItems(s));
|
||||||
|
insertedPosts.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(targetList.isEmpty()){
|
||||||
|
// oops. We didn't add new posts, but at least we know there are none.
|
||||||
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
|
||||||
|
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
|
||||||
|
}
|
||||||
|
// AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putConversationsTimeline(insertedPosts, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
gap.loading=false;
|
||||||
|
Activity a=getActivity();
|
||||||
|
if(a!=null){
|
||||||
|
error.showToast(a);
|
||||||
|
int gapPos=displayItems.indexOf(gap);
|
||||||
|
if(gapPos>=0)
|
||||||
|
adapter.notifyItemChanged(gapPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRefresh(){
|
||||||
|
if(currentRequest!=null){
|
||||||
|
currentRequest.cancel();
|
||||||
|
currentRequest=null;
|
||||||
|
dataLoading=false;
|
||||||
|
}
|
||||||
|
if (parent != null) parent.hideNewPostsButton();
|
||||||
|
super.onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import static android.view.Menu.NONE;
|
|
||||||
|
|
||||||
import static org.joinmastodon.android.ui.utils.UiUtils.makeBackItem;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Bundle;
|
|
||||||
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.widget.EditText;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.PopupMenu;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
|
||||||
|
|
||||||
public class EditTimelinesFragment extends RecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
|
||||||
private String accountID;
|
|
||||||
private TimelinesAdapter adapter;
|
|
||||||
private final ItemTouchHelper itemTouchHelper;
|
|
||||||
private Menu optionsMenu;
|
|
||||||
private boolean updated;
|
|
||||||
private final Map<MenuItem, TimelineDefinition> timelineByMenuItem = new HashMap<>();
|
|
||||||
private final List<ListTimeline> listTimelines = new ArrayList<>();
|
|
||||||
private final List<Hashtag> hashtags = new ArrayList<>();
|
|
||||||
|
|
||||||
public EditTimelinesFragment() {
|
|
||||||
super(10);
|
|
||||||
ItemTouchHelper.SimpleCallback itemTouchCallback = new ItemTouchHelperCallback() ;
|
|
||||||
itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
setTitle(R.string.sk_timelines);
|
|
||||||
accountID = getArguments().getString("account");
|
|
||||||
|
|
||||||
new GetLists().setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<ListTimeline> result) {
|
|
||||||
listTimelines.addAll(result);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
|
|
||||||
new GetFollowedHashtags().setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result) {
|
|
||||||
hashtags.addAll(result);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown(){
|
|
||||||
super.onShown();
|
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading) loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
itemTouchHelper.attachToRecyclerView(list);
|
|
||||||
refreshLayout.setEnabled(false);
|
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
this.optionsMenu = menu;
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.menu_back) {
|
|
||||||
updateOptionsMenu();
|
|
||||||
optionsMenu.performIdentifierAction(R.id.menu_add_timeline, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
TimelineDefinition tl = timelineByMenuItem.get(item);
|
|
||||||
if (tl != null) {
|
|
||||||
data.add(tl.copy());
|
|
||||||
adapter.notifyItemInserted(data.size());
|
|
||||||
saveTimelines();
|
|
||||||
updateOptionsMenu();
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTimelineToOptions(TimelineDefinition tl, Menu menu) {
|
|
||||||
if (data.contains(tl)) return;
|
|
||||||
MenuItem item = menu.add(0, View.generateViewId(), Menu.NONE, tl.getTitle(getContext()));
|
|
||||||
item.setIcon(tl.getIcon().iconRes);
|
|
||||||
timelineByMenuItem.put(item, tl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateOptionsMenu() {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
optionsMenu.clear();
|
|
||||||
timelineByMenuItem.clear();
|
|
||||||
|
|
||||||
SubMenu menu = optionsMenu.addSubMenu(0, R.id.menu_add_timeline, NONE, R.string.sk_timelines_add);
|
|
||||||
menu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
||||||
menu.getItem().setIcon(R.drawable.ic_fluent_add_24_regular);
|
|
||||||
|
|
||||||
SubMenu timelinesMenu = menu.addSubMenu(R.string.sk_timeline);
|
|
||||||
timelinesMenu.getItem().setIcon(R.drawable.ic_fluent_timeline_24_regular);
|
|
||||||
SubMenu listsMenu = menu.addSubMenu(R.string.sk_list);
|
|
||||||
listsMenu.getItem().setIcon(R.drawable.ic_fluent_people_24_regular);
|
|
||||||
SubMenu hashtagsMenu = menu.addSubMenu(R.string.sk_hashtag);
|
|
||||||
hashtagsMenu.getItem().setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
|
||||||
|
|
||||||
makeBackItem(timelinesMenu);
|
|
||||||
makeBackItem(listsMenu);
|
|
||||||
makeBackItem(hashtagsMenu);
|
|
||||||
|
|
||||||
TimelineDefinition.getAllTimelines(accountID).forEach(tl -> addTimelineToOptions(tl, timelinesMenu));
|
|
||||||
listTimelines.stream().map(TimelineDefinition::ofList).forEach(tl -> addTimelineToOptions(tl, listsMenu));
|
|
||||||
hashtags.stream().map(TimelineDefinition::ofHashtag).forEach(tl -> addTimelineToOptions(tl, hashtagsMenu));
|
|
||||||
|
|
||||||
timelinesMenu.getItem().setVisible(timelinesMenu.size() > 0);
|
|
||||||
listsMenu.getItem().setVisible(listsMenu.size() > 0);
|
|
||||||
hashtagsMenu.getItem().setVisible(hashtagsMenu.size() > 0);
|
|
||||||
|
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.menu_add_timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveTimelines() {
|
|
||||||
updated = true;
|
|
||||||
GlobalUserPreferences.pinnedTimelines.put(accountID, data.size() > 0 ? data : List.of(TimelineDefinition.HOME_TIMELINE));
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeTimeline(int position) {
|
|
||||||
data.remove(position);
|
|
||||||
adapter.notifyItemRemoved(position);
|
|
||||||
saveTimelines();
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){
|
|
||||||
onDataLoaded(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID)), false);
|
|
||||||
updateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RecyclerView.Adapter<TimelineViewHolder> getAdapter() {
|
|
||||||
return adapter = new TimelinesAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void scrollToTop() {
|
|
||||||
smoothScrollRecyclerViewToTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
if (updated) UiUtils.restartApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelinesAdapter extends RecyclerView.Adapter<TimelineViewHolder>{
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public TimelineViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return new TimelineViewHolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull TimelineViewHolder holder, int position) {
|
|
||||||
holder.bind(data.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelineViewHolder extends BindableViewHolder<TimelineDefinition> implements UsableRecyclerView.Clickable{
|
|
||||||
private final TextView title;
|
|
||||||
private final ImageView dragger;
|
|
||||||
|
|
||||||
public TimelineViewHolder(){
|
|
||||||
super(getActivity(), R.layout.item_text, list);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
dragger=findViewById(R.id.dragger_thingy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
@Override
|
|
||||||
public void onBind(TimelineDefinition item) {
|
|
||||||
title.setText(item.getTitle(getContext()));
|
|
||||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(item.getIcon().iconRes), null, null, null);
|
|
||||||
dragger.setVisibility(View.VISIBLE);
|
|
||||||
dragger.setOnTouchListener((View v, MotionEvent event) -> {
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
||||||
itemTouchHelper.startDrag(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
Context ctx = getContext();
|
|
||||||
LinearLayout view = (LinearLayout) getActivity().getLayoutInflater()
|
|
||||||
.inflate(R.layout.edit_timeline, (ViewGroup) itemView, false);
|
|
||||||
|
|
||||||
TextInputFrameLayout inputLayout = view.findViewById(R.id.input);
|
|
||||||
EditText editText = inputLayout.getEditText();
|
|
||||||
editText.setText(item.getCustomTitle());
|
|
||||||
editText.setHint(item.getDefaultTitle(ctx));
|
|
||||||
|
|
||||||
ImageButton btn = view.findViewById(R.id.button);
|
|
||||||
PopupMenu popup = new PopupMenu(ctx, btn);
|
|
||||||
TimelineDefinition.Icon currentIcon = item.getIcon();
|
|
||||||
btn.setImageResource(currentIcon.iconRes);
|
|
||||||
btn.setContentDescription(ctx.getString(currentIcon.nameRes));
|
|
||||||
btn.setOnTouchListener(popup.getDragToOpenListener());
|
|
||||||
btn.setOnClickListener(l -> popup.show());
|
|
||||||
|
|
||||||
Menu menu = popup.getMenu();
|
|
||||||
TimelineDefinition.Icon defaultIcon = item.getDefaultIcon();
|
|
||||||
menu.add(0, currentIcon.ordinal(), NONE, currentIcon.nameRes).setIcon(currentIcon.iconRes);
|
|
||||||
if (!currentIcon.equals(defaultIcon)) {
|
|
||||||
menu.add(0, defaultIcon.ordinal(), NONE, defaultIcon.nameRes).setIcon(defaultIcon.iconRes);
|
|
||||||
}
|
|
||||||
for (TimelineDefinition.Icon icon : TimelineDefinition.Icon.values()) {
|
|
||||||
if (icon.hidden || icon.equals(item.getIcon())) continue;
|
|
||||||
menu.add(0, icon.ordinal(), NONE, icon.nameRes).setIcon(icon.iconRes);
|
|
||||||
}
|
|
||||||
UiUtils.enablePopupMenuIcons(ctx, popup);
|
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener(menuItem -> {
|
|
||||||
TimelineDefinition.Icon icon = TimelineDefinition.Icon.values()[menuItem.getItemId()];
|
|
||||||
btn.setImageResource(icon.iconRes);
|
|
||||||
btn.setContentDescription(ctx.getString(icon.nameRes));
|
|
||||||
item.setIcon(icon);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
new M3AlertDialogBuilder(ctx)
|
|
||||||
.setTitle(R.string.sk_edit_timeline)
|
|
||||||
.setView(view)
|
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
|
||||||
item.setTitle(editText.getText().toString().trim());
|
|
||||||
rebind();
|
|
||||||
saveTimelines();
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.sk_remove, (d, which) ->
|
|
||||||
removeTimeline(getAbsoluteAdapterPosition()))
|
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
|
||||||
.show();
|
|
||||||
|
|
||||||
btn.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
|
|
||||||
public ItemTouchHelperCallback() {
|
|
||||||
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
|
||||||
int fromPosition = viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
int toPosition = target.getAbsoluteAdapterPosition();
|
|
||||||
if (Math.max(fromPosition, toPosition) >= data.size() || Math.min(fromPosition, toPosition) < 0) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
Collections.swap(data, fromPosition, toPosition);
|
|
||||||
adapter.notifyItemMoved(fromPosition, toPosition);
|
|
||||||
saveTimelines();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
|
|
||||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) {
|
|
||||||
viewHolder.itemView.animate().alpha(0.65f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
|
||||||
super.clearView(recyclerView, viewHolder);
|
|
||||||
viewHolder.itemView.animate().alpha(1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
|
||||||
int position = viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
removeTimeline(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
|
||||||
|
public abstract class FabStatusListFragment extends StatusListFragment {
|
||||||
|
protected ImageButton fab;
|
||||||
|
|
||||||
|
public FabStatusListFragment() {
|
||||||
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab = view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(this::onFabLongClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onFabClick(View v){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean onFabLongClick(View v) {
|
||||||
|
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetFavoritedStatuses;
|
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.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
@@ -27,7 +25,6 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -37,16 +34,4 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
|||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.ACCOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.encodedPath(isInstanceAkkoma()
|
|
||||||
? '/' + getSession().self.username + "#favorites"
|
|
||||||
: "/favourites").build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -25,7 +24,6 @@ import org.joinmastodon.android.ui.text.HtmlParser;
|
|||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -40,6 +38,7 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -48,7 +47,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop, ProvidesAssistContent.ProvidesWebUri {
|
public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowRequestsListFragment.AccountWrapper> implements ScrollableToTop{
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private Map<String, Relationship> relationships=Collections.emptyMap();
|
private Map<String, Relationship> relationships=Collections.emptyMap();
|
||||||
private GetAccountRelationships relationshipsRequest;
|
private GetAccountRelationships relationshipsRequest;
|
||||||
@@ -81,7 +80,6 @@ public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsL
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -150,16 +148,6 @@ public class FollowRequestsListFragment extends RecyclerFragment<FollowRequestsL
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path(isInstanceAkkoma() ? "/friend-requests" : "/follow_requests").build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
private class AccountsAdapter extends UsableRecyclerView.Adapter<AccountViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public AccountsAdapter(){
|
public AccountsAdapter(){
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -15,15 +14,15 @@ import org.joinmastodon.android.model.Hashtag;
|
|||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implements ScrollableToTop, ProvidesAssistContent.ProvidesWebUri {
|
public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
private String accountID;
|
private String accountId;
|
||||||
|
|
||||||
public FollowedHashtagsFragment() {
|
public FollowedHashtagsFragment() {
|
||||||
super(20);
|
super(20);
|
||||||
@@ -33,7 +32,7 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
Bundle args=getArguments();
|
Bundle args=getArguments();
|
||||||
accountID=args.getString("account");
|
accountId=args.getString("account");
|
||||||
setTitle(R.string.sk_hashtags_you_follow);
|
setTitle(R.string.sk_hashtags_you_follow);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +55,6 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -64,7 +62,7 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
onDataLoaded(result, nextMaxID!=null);
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,16 +75,6 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
smoothScrollRecyclerViewToTop(list);
|
smoothScrollRecyclerViewToTop(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return isInstanceAkkoma() ? null : base.path("/followed_tags").build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -121,7 +109,7 @@ public class FollowedHashtagsFragment extends RecyclerFragment<Hashtag> implemen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
UiUtils.openHashtagTimeline(getActivity(), accountId, item.name, item.following);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public interface HasAccountID {
|
|
||||||
String getAccountID();
|
|
||||||
|
|
||||||
default AccountSession getSession() {
|
|
||||||
return AccountSessionManager.getInstance().getAccount(getAccountID());
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isInstanceAkkoma() {
|
|
||||||
return getInstance().map(Instance::isPleroma).orElse(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
default Optional<Instance> getInstance() {
|
|
||||||
return getSession().getInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
public interface HasFab {
|
|
||||||
View getFab();
|
|
||||||
void showFab();
|
|
||||||
void hideFab();
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,24 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -33,14 +26,14 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
public class HashtagTimelineFragment extends StatusListFragment{
|
||||||
private String hashtag;
|
private String hashtag;
|
||||||
private boolean following;
|
private boolean following;
|
||||||
|
private ImageButton fab;
|
||||||
private MenuItem followButton;
|
private MenuItem followButton;
|
||||||
|
|
||||||
@Override
|
public HashtagTimelineFragment(){
|
||||||
protected boolean wantsComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,6 +41,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
updateTitle(getArguments().getString("hashtag"));
|
updateTitle(getArguments().getString("hashtag"));
|
||||||
following=getArguments().getBoolean("following", false);
|
following=getArguments().getBoolean("following", false);
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,42 +54,19 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
this.following = newFollowing;
|
this.following = newFollowing;
|
||||||
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||||
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||||
E.post(new HashtagUpdatedEvent(hashtag, following));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
followButton = menu.findItem(R.id.follow_hashtag);
|
followButton = menu.findItem(R.id.follow_hashtag);
|
||||||
updateFollowingState(following);
|
updateFollowingState(following);
|
||||||
|
|
||||||
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
followButton.setOnMenuItemClickListener(i -> {
|
||||||
@Override
|
|
||||||
public void onSuccess(Hashtag hashtag) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
updateTitle(hashtag.name);
|
|
||||||
updateFollowingState(hashtag.following);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (super.onOptionsItemSelected(item)) return true;
|
|
||||||
if (item.getItemId() == R.id.follow_hashtag) {
|
|
||||||
updateFollowingState(!following);
|
updateFollowingState(!following);
|
||||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Hashtag i) {
|
public void onSuccess(Hashtag i) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||||
updateFollowingState(i.following);
|
updateFollowingState(i.following);
|
||||||
}
|
}
|
||||||
@@ -107,13 +78,20 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
return true;
|
return true;
|
||||||
}
|
});
|
||||||
return false;
|
|
||||||
|
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Hashtag hashtag) {
|
||||||
|
updateTitle(hashtag.name);
|
||||||
|
updateFollowingState(hashtag.following);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TimelineDefinition makeTimelineDefinition() {
|
public void onError(ErrorResponse error) {
|
||||||
return TimelineDefinition.ofHashtag(hashtag);
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,8 +100,6 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -138,12 +114,14 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onFabLongClick(View v) {
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onFabClick(View v){
|
||||||
public void onFabClick(View v){
|
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("prefilledText", '#'+hashtag+' ');
|
args.putString("prefilledText", '#'+hashtag+' ');
|
||||||
@@ -154,14 +132,4 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
protected void onSetFabBottomInset(int inset){
|
protected void onSetFabBottomInset(int inset){
|
||||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.PUBLIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags") + hashtag).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -17,36 +16,22 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.IdRes;
|
import org.joinmastodon.android.PushNotificationReceiver;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
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.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
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.fragments.discover.DiscoverFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
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.AccountSwitcherSheet;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.TabBar;
|
import org.joinmastodon.android.ui.views.TabBar;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
|
import androidx.annotation.IdRes;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
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.AppKitFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
@@ -55,43 +40,44 @@ import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
|
|
||||||
public class HomeFragment extends AppKitFragment implements OnBackPressedListener, ProvidesAssistContent, HasAccountID {
|
public class HomeFragment extends AppKitFragment implements OnBackPressedListener{
|
||||||
private FragmentRootLinearLayout content;
|
private FragmentRootLinearLayout content;
|
||||||
|
|
||||||
private HomeTabFragment homeTabFragment;
|
private HomeTabFragment homeTabFragment;
|
||||||
|
|
||||||
|
// private HomeTimelineFragment homeTimelineFragment;
|
||||||
|
|
||||||
private NotificationsFragment notificationsFragment;
|
private NotificationsFragment notificationsFragment;
|
||||||
private DiscoverFragment searchFragment;
|
private DiscoverFragment searchFragment;
|
||||||
private ProfileFragment profileFragment;
|
private ProfileFragment profileFragment;
|
||||||
private TabBar tabBar;
|
private TabBar tabBar;
|
||||||
private View tabBarWrap;
|
private View tabBarWrap;
|
||||||
private ImageView tabBarAvatar;
|
private ImageView tabBarAvatar;
|
||||||
private ImageView notificationTabIcon;
|
|
||||||
@IdRes
|
@IdRes
|
||||||
private int currentTab=R.id.tab_home;
|
private int currentTab=R.id.tab_home;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean isPleroma;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
E.register(this);
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
setTitle(R.string.sk_app_name);
|
setTitle(R.string.sk_app_name);
|
||||||
isPleroma = AccountSessionManager.getInstance().getAccount(accountID).getInstance()
|
|
||||||
.map(Instance::isPleroma)
|
|
||||||
.orElse(false);
|
|
||||||
|
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|
||||||
// TODO: clean up
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
|
|
||||||
homeTabFragment=new HomeTabFragment();
|
homeTabFragment=new HomeTabFragment();
|
||||||
homeTabFragment.setArguments(args);
|
homeTabFragment.setArguments(args);
|
||||||
|
|
||||||
|
// homeTimelineFragment=new HomeTimelineFragment();
|
||||||
|
// homeTimelineFragment.setArguments(args);
|
||||||
|
|
||||||
args=new Bundle(args);
|
args=new Bundle(args);
|
||||||
args.putBoolean("disableDiscover", isPleroma);
|
|
||||||
args.putBoolean("noAutoLoad", true);
|
args.putBoolean("noAutoLoad", true);
|
||||||
searchFragment=new DiscoverFragment();
|
searchFragment=new DiscoverFragment();
|
||||||
searchFragment.setArguments(args);
|
searchFragment.setArguments(args);
|
||||||
@@ -113,7 +99,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
content.setOrientation(LinearLayout.VERTICAL);
|
content.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
FrameLayout fragmentContainer=new FrameLayout(getActivity());
|
FrameLayout fragmentContainer=new FrameLayout(getActivity());
|
||||||
fragmentContainer.setId(me.grishka.appkit.R.id.fragment_wrap);
|
fragmentContainer.setId(R.id.fragment_wrap);
|
||||||
content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
|
|
||||||
inflater.inflate(R.layout.tab_bar, content);
|
inflater.inflate(R.layout.tab_bar, content);
|
||||||
@@ -132,17 +118,21 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
|
||||||
ViewImageLoader.load(tabBarAvatar, null, new UrlImageLoaderRequest(self.avatar, V.dp(28), V.dp(28)));
|
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){
|
if(savedInstanceState==null){
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.add(me.grishka.appkit.R.id.fragment_wrap, homeTabFragment)
|
.add(R.id.fragment_wrap, homeTabFragment)
|
||||||
.add(me.grishka.appkit.R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
.add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||||
.add(me.grishka.appkit.R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
.add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||||
.add(me.grishka.appkit.R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||||
.commit();
|
.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");
|
String defaultTab=getArguments().getString("tab");
|
||||||
if("notifications".equals(defaultTab)){
|
if("notifications".equals(defaultTab)){
|
||||||
tabBar.selectTab(R.id.tab_notifications);
|
tabBar.selectTab(R.id.tab_notifications);
|
||||||
@@ -163,14 +153,21 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
@Override
|
@Override
|
||||||
public void onViewStateRestored(Bundle savedInstanceState){
|
public void onViewStateRestored(Bundle savedInstanceState){
|
||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
|
|
||||||
if(savedInstanceState==null) return;
|
if(savedInstanceState==null) return;
|
||||||
|
|
||||||
|
// if(savedInstanceState==null || homeTimelineFragment!=null)
|
||||||
|
// return;
|
||||||
|
|
||||||
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
|
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
|
||||||
|
|
||||||
|
// homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
|
||||||
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
|
||||||
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
|
||||||
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
|
||||||
currentTab=savedInstanceState.getInt("selectedTab");
|
currentTab=savedInstanceState.getInt("selectedTab");
|
||||||
tabBar.selectTab(currentTab);
|
|
||||||
Fragment current=fragmentForTab(currentTab);
|
Fragment current=fragmentForTab(currentTab);
|
||||||
|
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.hide(homeTabFragment)
|
.hide(homeTabFragment)
|
||||||
.hide(searchFragment)
|
.hide(searchFragment)
|
||||||
@@ -178,6 +175,14 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
.hide(profileFragment)
|
.hide(profileFragment)
|
||||||
.show(current)
|
.show(current)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
|
// getChildFragmentManager().beginTransaction()
|
||||||
|
// .hide(homeTimelineFragment)
|
||||||
|
// .hide(searchFragment)
|
||||||
|
// .hide(notificationsFragment)
|
||||||
|
// .hide(profileFragment)
|
||||||
|
// .show(current)
|
||||||
|
// .commit();
|
||||||
maybeTriggerLoading(current);
|
maybeTriggerLoading(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +212,11 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||||
}
|
}
|
||||||
WindowInsets topOnlyInsets=insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
|
WindowInsets topOnlyInsets=insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
|
||||||
|
|
||||||
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
|
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
|
|
||||||
|
// homeTimelineFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
|
|
||||||
searchFragment.onApplyWindowInsets(topOnlyInsets);
|
searchFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
|
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
profileFragment.onApplyWindowInsets(topOnlyInsets);
|
profileFragment.onApplyWindowInsets(topOnlyInsets);
|
||||||
@@ -216,6 +225,9 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
private Fragment fragmentForTab(@IdRes int tab){
|
private Fragment fragmentForTab(@IdRes int tab){
|
||||||
if(tab==R.id.tab_home){
|
if(tab==R.id.tab_home){
|
||||||
return homeTabFragment;
|
return homeTabFragment;
|
||||||
|
|
||||||
|
// if(tab==R.id.tab_home){
|
||||||
|
// return homeTimelineFragment;
|
||||||
}else if(tab==R.id.tab_search){
|
}else if(tab==R.id.tab_search){
|
||||||
return searchFragment;
|
return searchFragment;
|
||||||
}else if(tab==R.id.tab_notifications){
|
}else if(tab==R.id.tab_notifications){
|
||||||
@@ -226,28 +238,28 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentTab(@IdRes int tab){
|
|
||||||
if(tab==currentTab)
|
|
||||||
return;
|
|
||||||
tabBar.selectTab(tab);
|
|
||||||
onTabSelected(tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onTabSelected(@IdRes int tab){
|
private void onTabSelected(@IdRes int tab){
|
||||||
Fragment newFragment=fragmentForTab(tab);
|
Fragment newFragment=fragmentForTab(tab);
|
||||||
if(tab==currentTab){
|
if(tab==currentTab){
|
||||||
if (tab == R.id.tab_search)
|
if(tab == R.id.tab_search){
|
||||||
searchFragment.onSelect();
|
if(newFragment instanceof ScrollableToTop scrollable)
|
||||||
else 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)
|
||||||
scrollable.scrollToTop();
|
scrollable.scrollToTop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
|
getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit();
|
||||||
maybeTriggerLoading(newFragment);
|
maybeTriggerLoading(newFragment);
|
||||||
if (newFragment instanceof HasFab fabulous) fabulous.showFab();
|
|
||||||
currentTab=tab;
|
currentTab=tab;
|
||||||
((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this);
|
((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this);
|
||||||
if (tab == R.id.tab_search && isPleroma) searchFragment.selectSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeTriggerLoading(Fragment newFragment){
|
private void maybeTriggerLoading(Fragment newFragment){
|
||||||
@@ -274,7 +286,13 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||||
options.add(session.self.displayName+"\n("+session.self.username+"@"+session.domain+")");
|
options.add(session.self.displayName+"\n("+session.self.username+"@"+session.domain+")");
|
||||||
}
|
}
|
||||||
new AccountSwitcherSheet(getActivity(), this).show();
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -299,62 +317,15 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
public void onSaveInstanceState(Bundle outState){
|
public void onSaveInstanceState(Bundle outState){
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt("selectedTab", currentTab);
|
outState.putInt("selectedTab", currentTab);
|
||||||
|
|
||||||
if (homeTabFragment.isAdded()) getChildFragmentManager().putFragment(outState, "homeTabFragment", homeTabFragment);
|
if (homeTabFragment.isAdded()) getChildFragmentManager().putFragment(outState, "homeTabFragment", homeTabFragment);
|
||||||
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||||
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||||
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||||
}
|
|
||||||
|
|
||||||
public void updateNotificationBadge() {
|
// getChildFragmentManager().putFragment(outState, "homeTimelineFragment", homeTimelineFragment);
|
||||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountID);
|
// getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
|
||||||
Optional<Instance> instance = session.getInstance();
|
// getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
|
||||||
if (instance.isEmpty()) return; // avoiding incompatibility with akkoma
|
// getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
|
||||||
|
|
||||||
new GetNotifications(null, 1, EnumSet.allOf(Notification.Type.class), instance.get().isPleroma())
|
|
||||||
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
callFragmentToProvideAssistContent(fragmentForTab(currentTab), assistContent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import static org.joinmastodon.android.GlobalUserPreferences.reduceMotion;
|
import static org.joinmastodon.android.GlobalUserPreferences.showFederatedTimeline;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -10,7 +10,6 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -25,7 +24,6 @@ import android.view.ViewParent;
|
|||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -43,27 +41,21 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
|
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
||||||
|
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
||||||
import org.joinmastodon.android.model.Announcement;
|
import org.joinmastodon.android.model.Announcement;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -73,16 +65,18 @@ import me.grishka.appkit.fragments.OnBackPressedListener;
|
|||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener, HasFab, ProvidesAssistContent {
|
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener {
|
||||||
private static final int ANNOUNCEMENTS_RESULT = 654;
|
private static final int ANNOUNCEMENTS_RESULT = 654;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private MenuItem announcements, announcementsAction, settings, settingsAction;
|
private MenuItem announcements;
|
||||||
// private ImageView toolbarLogo;
|
// private ImageView toolbarLogo;
|
||||||
private Button toolbarShowNewPostsBtn;
|
private Button toolbarShowNewPostsBtn;
|
||||||
private boolean newPostsBtnShown;
|
private boolean newPostsBtnShown;
|
||||||
private AnimatorSet currentNewPostsAnim;
|
private AnimatorSet currentNewPostsAnim;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
|
private final List<Fragment> fragments = new ArrayList<>();
|
||||||
|
private final List<FrameLayout> tabViews = new ArrayList<>();
|
||||||
private View switcher;
|
private View switcher;
|
||||||
private FrameLayout toolbarFrame;
|
private FrameLayout toolbarFrame;
|
||||||
private ImageView timelineIcon;
|
private ImageView timelineIcon;
|
||||||
@@ -91,30 +85,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
private PopupMenu switcherPopup;
|
private PopupMenu switcherPopup;
|
||||||
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
private final Map<Integer, ListTimeline> listItems = new HashMap<>();
|
||||||
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
private final Map<Integer, Hashtag> hashtagsItems = new HashMap<>();
|
||||||
private List<TimelineDefinition> timelineDefinitions;
|
|
||||||
private int count;
|
|
||||||
private Fragment[] fragments;
|
|
||||||
private FrameLayout[] tabViews;
|
|
||||||
private TimelineDefinition[] timelines;
|
|
||||||
private final Map<Integer, TimelineDefinition> timelinesByMenuItem = new HashMap<>();
|
|
||||||
private SubMenu hashtagsMenu, listsMenu;
|
|
||||||
private PopupMenu overflowPopup;
|
|
||||||
private View overflowActionView = null;
|
|
||||||
private boolean announcementsBadged, settingsBadged;
|
|
||||||
private ImageButton fab;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
E.register(this);
|
|
||||||
accountID = getArguments().getString("account");
|
accountID = getArguments().getString("account");
|
||||||
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID));
|
|
||||||
assert timelineDefinitions != null;
|
|
||||||
if (timelineDefinitions.size() == 0) timelineDefinitions = List.of(TimelineDefinition.HOME_TIMELINE);
|
|
||||||
count = timelineDefinitions.size();
|
|
||||||
fragments = new Fragment[count];
|
|
||||||
tabViews = new FrameLayout[count];
|
|
||||||
timelines = new TimelineDefinition[count];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,48 +101,39 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||||
FrameLayout view = new FrameLayout(getContext());
|
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());
|
pager = new ViewPager2(getContext());
|
||||||
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
||||||
|
|
||||||
if (fragments[0] == null) {
|
if (fragments.size() == 0) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putBoolean("__is_tab", true);
|
args.putBoolean("__is_tab", true);
|
||||||
args.putBoolean("__disable_fab", true);
|
|
||||||
args.putBoolean("onlyPosts", true);
|
|
||||||
|
|
||||||
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
fragments.add(new HomeTimelineFragment());
|
||||||
TimelineDefinition tl = timelineDefinitions.get(i);
|
fragments.add(new LocalTimelineFragment());
|
||||||
fragments[i] = tl.getFragment();
|
if (showFederatedTimeline) fragments.add(new FederatedTimelineFragment());
|
||||||
timelines[i] = tl;
|
args=new Bundle(args);
|
||||||
}
|
args.putBoolean("onlyPosts", true);
|
||||||
|
NotificationsListFragment postsFragment=new NotificationsListFragment();
|
||||||
|
postsFragment.setArguments(args);
|
||||||
|
fragments.add(postsFragment);
|
||||||
|
|
||||||
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < fragments.size(); i++) {
|
||||||
fragments[i].setArguments(timelines[i].populateArguments(new Bundle(args)));
|
fragments.get(i).setArguments(args);
|
||||||
FrameLayout tabView = new FrameLayout(getActivity());
|
FrameLayout tabView = new FrameLayout(getActivity());
|
||||||
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
tabView.setVisibility(View.GONE);
|
tabView.setVisibility(View.GONE);
|
||||||
tabView.setId(i + 1);
|
tabView.setId(i + 1);
|
||||||
transaction.add(i + 1, fragments[i]);
|
transaction.add(i + 1, fragments.get(i));
|
||||||
view.addView(tabView);
|
view.addView(tabView);
|
||||||
tabViews[i] = tabView;
|
tabViews.add(tabView);
|
||||||
}
|
}
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.addView(pager, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
overflowActionView = UiUtils.makeOverflowActionView(getContext());
|
|
||||||
overflowPopup = new PopupMenu(getContext(), overflowActionView);
|
|
||||||
overflowPopup.setOnMenuItemClickListener(this::onOptionsItemSelected);
|
|
||||||
overflowActionView.setOnClickListener(l -> overflowPopup.show());
|
|
||||||
overflowActionView.setOnTouchListener(overflowPopup.getDragToOpenListener());
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,11 +147,18 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
|
||||||
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
|
||||||
switcherPopup = new PopupMenu(getContext(), switcher);
|
switcherPopup = new PopupMenu(getContext(), switcher);
|
||||||
|
switcherPopup.inflate(R.menu.home_switcher);
|
||||||
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
||||||
switcher.setOnClickListener(v->switcherPopup.show());
|
switcher.setOnClickListener(v->{
|
||||||
switcher.setOnTouchListener(switcherPopup.getDragToOpenListener());
|
|
||||||
updateSwitcherMenu();
|
updateSwitcherMenu();
|
||||||
|
switcherPopup.show();
|
||||||
|
});
|
||||||
|
View.OnTouchListener listener = switcherPopup.getDragToOpenListener();
|
||||||
|
switcher.setOnTouchListener((v, m)-> {
|
||||||
|
updateSwitcherMenu();
|
||||||
|
return listener.onTouch(v, m);
|
||||||
|
});
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
@@ -193,24 +166,18 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position){
|
public void onPageSelected(int position){
|
||||||
if (!reduceMotion) {
|
|
||||||
// setting this here because page transformer appears to fire too late so the
|
|
||||||
// animation can appear bumpy, especially when navigating to a further-away tab
|
|
||||||
switcher.setScaleY(0.85f);
|
|
||||||
switcher.setScaleX(0.85f);
|
|
||||||
switcher.setAlpha(0.65f);
|
|
||||||
}
|
|
||||||
updateSwitcherIcon(position);
|
updateSwitcherIcon(position);
|
||||||
if (!timelines[position].equals(TimelineDefinition.HOME_TIMELINE)) hideNewPostsButton();
|
if (position==0) return;
|
||||||
if (fragments[position] instanceof BaseRecyclerFragment<?> page){
|
hideNewPostsButton();
|
||||||
|
if (fragments.get(position) instanceof BaseRecyclerFragment<?> page){
|
||||||
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
if(!page.loaded && !page.isDataLoading()) page.loadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!reduceMotion) {
|
if (!GlobalUserPreferences.reduceMotion) {
|
||||||
pager.setPageTransformer((v, pos) -> {
|
pager.setPageTransformer((v, pos) -> {
|
||||||
if (reduceMotion || tabViews[pager.getCurrentItem()] != v) return;
|
if (tabViews.get(pager.getCurrentItem()) != v) return;
|
||||||
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
|
||||||
switcher.setScaleY(scaleFactor);
|
switcher.setScaleY(scaleFactor);
|
||||||
switcher.setScaleX(scaleFactor);
|
switcher.setScaleX(scaleFactor);
|
||||||
@@ -220,37 +187,15 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
updateToolbarLogo();
|
updateToolbarLogo();
|
||||||
|
|
||||||
ViewTreeObserver vto = getToolbar().getViewTreeObserver();
|
|
||||||
if (vto.isAlive()) {
|
|
||||||
vto.addOnGlobalLayoutListener(() -> {
|
|
||||||
Toolbar t = getToolbar();
|
|
||||||
if (t == null) return;
|
|
||||||
int toolbarWidth = t.getWidth();
|
|
||||||
if (toolbarWidth == 0) return;
|
|
||||||
|
|
||||||
int toolbarFrameWidth = toolbarFrame.getWidth();
|
|
||||||
int padding = toolbarWidth - toolbarFrameWidth;
|
|
||||||
FrameLayout parent = ((FrameLayout) toolbarShowNewPostsBtn.getParent());
|
|
||||||
if (padding == parent.getPaddingStart()) return;
|
|
||||||
|
|
||||||
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
|
||||||
// centering button by applying the same space on the left
|
|
||||||
parent.setPaddingRelative(padding, 0, 0, 0);
|
|
||||||
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
|
||||||
|
|
||||||
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
|
||||||
switcher.setPivotY(switcher.getHeight() / 2f);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
|
E.register(this);
|
||||||
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
updateUpdateState(GithubSelfUpdater.getInstance().getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetLists().setCallback(new Callback<>() {
|
new GetLists().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
updateList(lists, listItems);
|
addItemsToMap(lists, listItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -262,7 +207,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
new GetFollowedHashtags().setCallback(new Callback<>() {
|
new GetFollowedHashtags().setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
|
||||||
updateList(hashtags, hashtagsItems);
|
addItemsToMap(hashtags, hashtagsItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -270,61 +215,6 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|
||||||
new GetAnnouncements(false).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<Announcement> result) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
if (result.stream().anyMatch(a -> !a.read)) {
|
|
||||||
announcementsBadged = true;
|
|
||||||
announcements.setVisible(false);
|
|
||||||
announcementsAction.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).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();
|
|
||||||
listsMenu.getItem().setVisible(listItems.size() > 0);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(listsMenu));
|
|
||||||
listItems.forEach((id, list) -> {
|
|
||||||
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_people_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, item);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addHashtagsToOverflowMenu() {
|
|
||||||
Context ctx = getContext();
|
|
||||||
hashtagsMenu.clear();
|
|
||||||
hashtagsMenu.getItem().setVisible(hashtagsItems.size() > 0);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(hashtagsMenu));
|
|
||||||
hashtagsItems.forEach((id, hashtag) -> {
|
|
||||||
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
|
||||||
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(ctx, item);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateToolbarLogo(){
|
public void updateToolbarLogo(){
|
||||||
@@ -339,6 +229,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
updateSwitcherIcon(pager.getCurrentItem());
|
updateSwitcherIcon(pager.getCurrentItem());
|
||||||
|
|
||||||
|
// toolbarLogo=new ImageView(getActivity());
|
||||||
|
// toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
|
// toolbarLogo.setImageResource(R.drawable.logo);
|
||||||
|
// toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
|
||||||
|
|
||||||
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
|
||||||
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
|
||||||
@@ -359,90 +254,118 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
toolbarShowNewPostsBtn.setScaleY(.8f);
|
toolbarShowNewPostsBtn.setScaleY(.8f);
|
||||||
timelineTitle.setVisibility(View.VISIBLE);
|
timelineTitle.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewTreeObserver vto = toolbar.getViewTreeObserver();
|
||||||
|
if (vto.isAlive()) {
|
||||||
|
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
Toolbar t = getToolbar();
|
||||||
|
if (t == null) return;
|
||||||
|
int toolbarWidth = t.getWidth();
|
||||||
|
if (toolbarWidth == 0) return;
|
||||||
|
t.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
|
||||||
|
int toolbarFrameWidth = toolbarFrame.getWidth();
|
||||||
|
int padding = toolbarWidth - toolbarFrameWidth;
|
||||||
|
// toolbar frame goes from screen edge to beginning of right-aligned option buttons.
|
||||||
|
// centering button by applying the same space on the left
|
||||||
|
((FrameLayout) toolbarShowNewPostsBtn.getParent()).setPaddingRelative(padding, 0, 0, 0);
|
||||||
|
toolbarShowNewPostsBtn.setMaxWidth(toolbarWidth - padding * 2);
|
||||||
|
|
||||||
|
switcher.setPivotX(V.dp(28)); // padding + half of icon
|
||||||
|
switcher.setPivotY(switcher.getHeight() / 2f);
|
||||||
|
timelineTitle.setPivotX(timelineTitle.getWidth() - V.dp(8));
|
||||||
|
timelineTitle.setPivotY(timelineTitle.getHeight() / 2f);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
private void updateOverflowMenu() {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
Menu m = overflowPopup.getMenu();
|
|
||||||
m.clear();
|
|
||||||
overflowPopup.inflate(R.menu.home_overflow);
|
|
||||||
announcements = m.findItem(R.id.announcements);
|
|
||||||
settings = m.findItem(R.id.settings);
|
|
||||||
hashtagsMenu = m.findItem(R.id.hashtags).getSubMenu();
|
|
||||||
listsMenu = m.findItem(R.id.lists).getSubMenu();
|
|
||||||
|
|
||||||
announcements.setVisible(!announcementsBadged);
|
|
||||||
announcementsAction.setVisible(announcementsBadged);
|
|
||||||
settings.setVisible(!settingsBadged);
|
|
||||||
settingsAction.setVisible(settingsBadged);
|
|
||||||
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), overflowPopup);
|
|
||||||
|
|
||||||
addListsToOverflowMenu();
|
|
||||||
addHashtagsToOverflowMenu();
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !UiUtils.isEMUI()) {
|
|
||||||
m.setGroupDividerEnabled(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||||
inflater.inflate(R.menu.home, menu);
|
inflater.inflate(R.menu.home, menu);
|
||||||
|
announcements = menu.findItem(R.id.announcements);
|
||||||
|
|
||||||
menu.findItem(R.id.overflow).setActionView(overflowActionView);
|
new GetAnnouncements(false).setCallback(new Callback<>() {
|
||||||
announcementsAction = menu.findItem(R.id.announcements_action);
|
@Override
|
||||||
settingsAction = menu.findItem(R.id.settings_action);
|
public void onSuccess(List<Announcement> result) {
|
||||||
|
boolean hasUnread = result.stream().anyMatch(a -> !a.read);
|
||||||
updateOverflowMenu();
|
announcements.setIcon(hasUnread ? R.drawable.ic_announcements_24_badged : R.drawable.ic_fluent_megaphone_24_regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void updateList(List<T> addItems, Map<Integer, T> items) {
|
@Override
|
||||||
if (addItems.size() == 0 || getActivity() == null) return;
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void addItemsToMap(List<T> addItems, Map<Integer, T> items) {
|
||||||
|
if (addItems.size() == 0) return;
|
||||||
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
for (int i = 0; i < addItems.size(); i++) items.put(View.generateViewId(), addItems.get(i));
|
||||||
updateOverflowMenu();
|
updateSwitcherMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSwitcherMenu() {
|
private void updateSwitcherMenu() {
|
||||||
Menu switcherMenu = switcherPopup.getMenu();
|
Context context = getContext();
|
||||||
switcherMenu.clear();
|
switcherPopup.getMenu().findItem(R.id.federated).setVisible(showFederatedTimeline);
|
||||||
timelinesByMenuItem.clear();
|
|
||||||
|
|
||||||
for (TimelineDefinition tl : timelines) {
|
if (!listItems.isEmpty()) {
|
||||||
int menuItemId = View.generateViewId();
|
MenuItem listsItem = switcherPopup.getMenu().findItem(R.id.lists);
|
||||||
timelinesByMenuItem.put(menuItemId, tl);
|
listsItem.setVisible(true);
|
||||||
MenuItem item = switcherMenu.add(0, menuItemId, 0, tl.getTitle(getContext()));
|
SubMenu listsMenu = listsItem.getSubMenu();
|
||||||
item.setIcon(tl.getIcon().iconRes);
|
listsMenu.clear();
|
||||||
|
listItems.forEach((id, list) -> {
|
||||||
|
MenuItem item = listsMenu.add(Menu.NONE, id, Menu.NONE, list.title);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_people_list_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(context, item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
|
if (!hashtagsItems.isEmpty()) {
|
||||||
|
MenuItem hashtagsItem = switcherPopup.getMenu().findItem(R.id.followed_hashtags);
|
||||||
|
hashtagsItem.setVisible(true);
|
||||||
|
SubMenu hashtagsMenu = hashtagsItem.getSubMenu();
|
||||||
|
hashtagsMenu.clear();
|
||||||
|
hashtagsItems.forEach((id, hashtag) -> {
|
||||||
|
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
||||||
|
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(context, item);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean onSwitcherItemSelected(MenuItem item) {
|
private boolean onSwitcherItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
ListTimeline list;
|
||||||
|
Hashtag hashtag;
|
||||||
|
if (id == R.id.home) {
|
||||||
|
navigateTo(0);
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.local) {
|
||||||
|
navigateTo(1);
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.federated) {
|
||||||
|
navigateTo(2);
|
||||||
|
return true;
|
||||||
|
} else if (id == R.id.post_notifications) {
|
||||||
|
navigateTo(showFederatedTimeline ? 3 : 2);
|
||||||
|
} else if ((list = listItems.get(id)) != null) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
|
args.putString("listID", list.id);
|
||||||
if (id == R.id.menu_back) {
|
args.putString("listTitle", list.title);
|
||||||
switcher.post(() -> switcherPopup.show());
|
args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||||
return true;
|
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||||
|
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||||
|
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag.name, hashtag.following);
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineDefinition tl = timelinesByMenuItem.get(id);
|
|
||||||
if (tl != null) {
|
|
||||||
for (int i = 0; i < timelines.length; i++) {
|
|
||||||
if (timelines[i] == tl) {
|
|
||||||
navigateTo(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i) {
|
private void navigateTo(int i) {
|
||||||
navigateTo(i, !reduceMotion);
|
navigateTo(i, !GlobalUserPreferences.reduceMotion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void navigateTo(int i, boolean smooth) {
|
private void navigateTo(int i, boolean smooth) {
|
||||||
@@ -450,55 +373,39 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
updateSwitcherIcon(i);
|
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) {
|
private void updateSwitcherIcon(int i) {
|
||||||
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
// todo: refactor when implementing pinned tabs
|
||||||
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
if (i == (showFederatedTimeline ? 3 : 2)) {
|
||||||
showFab();
|
timelineIcon.setImageResource(R.drawable.ic_fluent_alert_24_regular);
|
||||||
|
timelineTitle.setText(R.string.sk_notify_posts);
|
||||||
|
} else {
|
||||||
|
timelineIcon.setImageResource(switch (i) {
|
||||||
|
default -> R.drawable.ic_fluent_home_24_regular;
|
||||||
|
case 1 -> R.drawable.ic_fluent_people_community_24_regular;
|
||||||
|
case 2 -> R.drawable.ic_fluent_earth_24_regular;
|
||||||
|
});
|
||||||
|
timelineTitle.setText(switch (i) {
|
||||||
|
default -> R.string.sk_timeline_home;
|
||||||
|
case 1 -> R.string.sk_timeline_local;
|
||||||
|
case 2 -> R.string.sk_timeline_federated;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
public boolean onOptionsItemSelected(MenuItem item){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
int id = item.getItemId();
|
if (item.getItemId() == R.id.settings) Nav.go(getActivity(), SettingsFragment.class, args);
|
||||||
ListTimeline list;
|
if (item.getItemId() == R.id.announcements) {
|
||||||
Hashtag hashtag;
|
|
||||||
|
|
||||||
if (item.getItemId() == R.id.menu_back) {
|
|
||||||
getToolbar().post(() -> overflowPopup.show());
|
|
||||||
return true;
|
|
||||||
} else if (id == R.id.settings || id == R.id.settings_action) {
|
|
||||||
Nav.go(getActivity(), SettingsFragment.class, args);
|
|
||||||
} else if (id == R.id.announcements || id == R.id.announcements_action) {
|
|
||||||
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
Nav.goForResult(getActivity(), AnnouncementsFragment.class, args, ANNOUNCEMENTS_RESULT, this);
|
||||||
} else if (id == R.id.edit_timelines) {
|
|
||||||
Nav.go(getActivity(), EditTimelinesFragment.class, args);
|
|
||||||
} else if ((list = listItems.get(id)) != null) {
|
|
||||||
args.putString("listID", list.id);
|
|
||||||
args.putString("listTitle", list.title);
|
|
||||||
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
|
||||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
|
||||||
args.putString("hashtag", hashtag.name);
|
|
||||||
args.putBoolean("following", hashtag.following);
|
|
||||||
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void scrollToTop(){
|
public void scrollToTop(){
|
||||||
((ScrollableToTop) fragments[pager.getCurrentItem()]).scrollToTop();
|
((ScrollableToTop) fragments.get(pager.getCurrentItem())).scrollToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideNewPostsButton(){
|
public void hideNewPostsButton(){
|
||||||
@@ -519,7 +426,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
|
||||||
);
|
);
|
||||||
set.setDuration(reduceMotion ? 0 : 300);
|
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
@@ -534,7 +441,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showNewPostsButton(){
|
public void showNewPostsButton(){
|
||||||
if(newPostsBtnShown || pager == null || !timelines[pager.getCurrentItem()].equals(TimelineDefinition.HOME_TIMELINE))
|
if(newPostsBtnShown || pager == null || pager.getCurrentItem() != 0)
|
||||||
return;
|
return;
|
||||||
newPostsBtnShown=true;
|
newPostsBtnShown=true;
|
||||||
if(currentNewPostsAnim!=null){
|
if(currentNewPostsAnim!=null){
|
||||||
@@ -552,7 +459,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
|
||||||
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
|
||||||
);
|
);
|
||||||
set.setDuration(reduceMotion ? 0 : 300);
|
set.setDuration(GlobalUserPreferences.reduceMotion ? 0 : 300);
|
||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
@@ -577,20 +484,15 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
public void onFragmentResult(int reqCode, boolean noMoreUnread, Bundle result){
|
||||||
if (reqCode == ANNOUNCEMENTS_RESULT && success) {
|
if (reqCode == ANNOUNCEMENTS_RESULT && noMoreUnread) {
|
||||||
announcementsBadged = false;
|
announcements.setIcon(R.drawable.ic_fluent_megaphone_24_regular);
|
||||||
announcements.setVisible(true);
|
|
||||||
announcementsAction.setVisible(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
|
||||||
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING) {
|
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING)
|
||||||
settingsBadged = true;
|
getToolbar().getMenu().findItem(R.id.settings).setIcon(R.drawable.ic_settings_24_badged);
|
||||||
settingsAction.setVisible(true);
|
|
||||||
settings.setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -610,26 +512,11 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView(){
|
public void onDestroyView(){
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
if (overflowPopup != null) {
|
|
||||||
overflowPopup.dismiss();
|
|
||||||
overflowPopup = null;
|
|
||||||
}
|
|
||||||
if (switcherPopup != null) {
|
|
||||||
switcherPopup.dismiss();
|
|
||||||
switcherPopup = null;
|
|
||||||
}
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
E.unregister(this);
|
E.unregister(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown() {
|
|
||||||
super.onShown();
|
|
||||||
Object pinnedTimelines = GlobalUserPreferences.pinnedTimelines.get(accountID);
|
|
||||||
if (pinnedTimelines != null && timelineDefinitions != pinnedTimelines) UiUtils.restartApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewStateRestored(Bundle savedInstanceState) {
|
public void onViewStateRestored(Bundle savedInstanceState) {
|
||||||
super.onViewStateRestored(savedInstanceState);
|
super.onViewStateRestored(savedInstanceState);
|
||||||
@@ -643,70 +530,12 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
outState.putInt("selectedTab", pager.getCurrentItem());
|
outState.putInt("selectedTab", pager.getCurrentItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onHashtagUpdatedEvent(HashtagUpdatedEvent event) {
|
|
||||||
handleListEvent(hashtagsItems, h -> h.name.equalsIgnoreCase(event.name), event.following, () -> {
|
|
||||||
Hashtag hashtag = new Hashtag();
|
|
||||||
hashtag.name = event.name;
|
|
||||||
hashtag.following = true;
|
|
||||||
return hashtag;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListDeletedEvent(ListDeletedEvent event) {
|
|
||||||
handleListEvent(listItems, l -> l.id.equals(event.id), false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
|
||||||
handleListEvent(listItems, l -> l.id.equals(event.id), true, () -> {
|
|
||||||
ListTimeline list = new ListTimeline();
|
|
||||||
list.id = event.id;
|
|
||||||
list.title = event.title;
|
|
||||||
list.repliesPolicy = event.repliesPolicy;
|
|
||||||
return list;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void handleListEvent(
|
|
||||||
Map<Integer, T> existingThings,
|
|
||||||
Predicate<T> matchExisting,
|
|
||||||
boolean shouldBeInList,
|
|
||||||
Supplier<T> makeNewThing
|
|
||||||
) {
|
|
||||||
Optional<Map.Entry<Integer, T>> existingThing = existingThings.entrySet().stream()
|
|
||||||
.filter(e -> matchExisting.test(e.getValue())).findFirst();
|
|
||||||
if (shouldBeInList) {
|
|
||||||
existingThings.put(existingThing.isPresent()
|
|
||||||
? existingThing.get().getKey() : View.generateViewId(), makeNewThing.get());
|
|
||||||
updateOverflowMenu();
|
|
||||||
} else if (existingThing.isPresent() && !shouldBeInList) {
|
|
||||||
existingThings.remove(existingThing.get().getKey());
|
|
||||||
updateOverflowMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<Hashtag> getHashtags() {
|
|
||||||
return hashtagsItems.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageButton getFab() {
|
|
||||||
return fab;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
callFragmentToProvideAssistContent(fragments[pager.getCurrentItem()], assistContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
FrameLayout tabView = tabViews[viewType % getItemCount()];
|
FrameLayout tabView = tabViews.get(viewType % getItemCount());
|
||||||
ViewGroup tabParent = (ViewGroup) tabView.getParent();
|
((ViewGroup)tabView.getParent()).removeView(tabView);
|
||||||
if (tabParent != null) tabParent.removeView(tabView);
|
|
||||||
tabView.setVisibility(View.VISIBLE);
|
tabView.setVisibility(View.VISIBLE);
|
||||||
return new SimpleViewHolder(tabView);
|
return new SimpleViewHolder(tabView);
|
||||||
}
|
}
|
||||||
@@ -716,7 +545,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
return count;
|
return fragments.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -9,7 +8,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
@@ -31,15 +29,9 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTimelineFragment extends StatusListFragment {
|
public class HomeTimelineFragment extends FabStatusListFragment {
|
||||||
private HomeTabFragment parent;
|
private HomeTabFragment parent;
|
||||||
private String maxID;
|
private String maxID;
|
||||||
private String lastSavedMarkerID;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean wantsComposeButton() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
@@ -62,7 +54,8 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
List<Status> filteredItems = filterPosts(result.items);
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
onDataLoaded(filteredItems, !result.items.isEmpty());
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
@@ -98,29 +91,6 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHidden(){
|
|
||||||
super.onHidden();
|
|
||||||
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){
|
public void onStatusCreated(StatusCreatedEvent ev){
|
||||||
prependItems(Collections.singletonList(ev.status), true);
|
prependItems(Collections.singletonList(ev.status), true);
|
||||||
}
|
}
|
||||||
@@ -149,11 +119,11 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
result.get(result.size()-1).hasGapAfter=true;
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
toAdd=result;
|
toAdd=result;
|
||||||
}
|
}
|
||||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||||
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
|
||||||
if(!toAdd.isEmpty()){
|
if(!toAdd.isEmpty()){
|
||||||
prependItems(toAdd, true);
|
prependItems(toAdd, true);
|
||||||
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
|
if (parent != null) parent.showNewPostsButton();
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,10 +135,6 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|
||||||
if (parent.getParentFragment() instanceof HomeFragment homeFragment) {
|
|
||||||
homeFragment.updateNotificationBadge();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -228,7 +194,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
||||||
targetList.clear();
|
targetList.clear();
|
||||||
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
||||||
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
|
||||||
for(Status s:result){
|
for(Status s:result){
|
||||||
if(idsBelowGap.contains(s.id))
|
if(idsBelowGap.contains(s.id))
|
||||||
break;
|
break;
|
||||||
@@ -281,14 +247,4 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
|
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.HOME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path("/").build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
public interface IsOnTop {
|
|
||||||
boolean isOnTop();
|
|
||||||
|
|
||||||
default boolean isRecyclerViewOnTop(@Nullable RecyclerView list) {
|
|
||||||
if (list == null) return true;
|
|
||||||
return !list.canScrollVertically(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,25 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetList;
|
import org.joinmastodon.android.api.requests.lists.CreateList;
|
||||||
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -37,15 +28,14 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
|
||||||
public class ListTimelineFragment extends PinnableStatusListFragment {
|
public class ListTimelineFragment extends StatusListFragment {
|
||||||
private String listID;
|
private String listID;
|
||||||
private String listTitle;
|
private String listTitle;
|
||||||
@Nullable
|
|
||||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
|
private ImageButton fab;
|
||||||
|
|
||||||
@Override
|
public ListTimelineFragment() {
|
||||||
protected boolean wantsComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,58 +48,39 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
|
|
||||||
setTitle(listTitle);
|
setTitle(listTitle);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
new GetList(listID).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(ListTimeline listTimeline) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
// TODO: save updated info
|
|
||||||
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
|
|
||||||
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
|
|
||||||
repliesPolicy = listTimeline.repliesPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.list, menu);
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), menu, R.id.pin);
|
inflater.inflate(R.menu.list, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (super.onOptionsItemSelected(item)) return true;
|
Bundle args = new Bundle();
|
||||||
|
args.putString("listID", listID);
|
||||||
if (item.getItemId() == R.id.edit) {
|
if (item.getItemId() == R.id.edit) {
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
editor.applyList(listTitle, repliesPolicy);
|
editor.applyList(listTitle, repliesPolicy);
|
||||||
new M3AlertDialogBuilder(getActivity())
|
new M3AlertDialogBuilder(getActivity())
|
||||||
.setTitle(R.string.sk_edit_list_title)
|
.setTitle(R.string.sk_edit_list_title)
|
||||||
.setIcon(R.drawable.ic_fluent_people_28_regular)
|
.setIcon(R.drawable.ic_fluent_people_list_28_regular)
|
||||||
.setView(editor)
|
.setView(editor)
|
||||||
.setPositiveButton(R.string.save, (d, which) -> {
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
String newTitle = editor.getTitle().trim();
|
new UpdateList(listID, editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
setTitle(newTitle);
|
|
||||||
new UpdateList(listID, newTitle, editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline list) {
|
public void onSuccess(ListTimeline list) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
setTitle(list.title);
|
setTitle(list.title);
|
||||||
listTitle = list.title;
|
listTitle = list.title;
|
||||||
repliesPolicy = list.repliesPolicy;
|
repliesPolicy = list.repliesPolicy;
|
||||||
E.post(new ListUpdatedCreatedEvent(listID, listTitle, repliesPolicy));
|
args.putString("listTitle", listTitle);
|
||||||
|
args.putInt("repliesPolicy", repliesPolicy.ordinal());
|
||||||
|
setResult(true, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {
|
||||||
setTitle(listTitle);
|
|
||||||
error.showToast(getContext());
|
error.showToast(getContext());
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
@@ -118,26 +89,20 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
.show();
|
.show();
|
||||||
} else if (item.getItemId() == R.id.delete) {
|
} else if (item.getItemId() == R.id.delete) {
|
||||||
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
|
||||||
E.post(new ListDeletedEvent(listID));
|
args.putBoolean("deleted", true);
|
||||||
|
setResult(true, args);
|
||||||
Nav.finish(this);
|
Nav.finish(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TimelineDefinition makeTimelineDefinition() {
|
|
||||||
return TimelineDefinition.ofList(listID, listTitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count) {
|
protected void doLoadData(int offset, int count) {
|
||||||
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null)
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result) {
|
public void onSuccess(List<Status> result) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
|
||||||
onDataLoaded(result, !result.isEmpty());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -152,7 +117,14 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFabClick(View v){
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
@@ -162,15 +134,4 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
protected void onSetFabBottomInset(int inset) {
|
protected void onSetFabBottomInset(int inset) {
|
||||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Filter.FilterContext getFilterContext() {
|
|
||||||
return Filter.FilterContext.HOME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path("/lists/" + listID).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
||||||
|
import org.joinmastodon.android.api.requests.lists.CreateList;
|
||||||
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
|
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
||||||
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
|
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
private static final int LIST_CHANGED_RESULT = 987;
|
||||||
|
|
||||||
|
private String accountId;
|
||||||
|
private String profileAccountId;
|
||||||
|
private String profileDisplayUsername;
|
||||||
|
private HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
||||||
|
private HashMap<String, Boolean> userInList = new HashMap<>();
|
||||||
|
private int inProgress = 0;
|
||||||
|
private ListsAdapter adapter;
|
||||||
|
|
||||||
|
public ListTimelinesFragment() {
|
||||||
|
super(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Bundle args=getArguments();
|
||||||
|
accountId=args.getString("account");
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
if(args.containsKey("profileAccount")){
|
||||||
|
profileAccountId=args.getString("profileAccount");
|
||||||
|
profileDisplayUsername=args.getString("profileDisplayUsername");
|
||||||
|
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
||||||
|
} else {
|
||||||
|
setTitle(R.string.sk_your_lists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.menu_list, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.create) {
|
||||||
|
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_create_list_title)
|
||||||
|
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
||||||
|
.setView(editor)
|
||||||
|
.setPositiveButton(R.string.sk_create, (d, which) -> {
|
||||||
|
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ListTimeline list) {
|
||||||
|
saveListMembership(list.id, true);
|
||||||
|
data.add(0, list);
|
||||||
|
adapter.notifyItemRangeInserted(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getContext());
|
||||||
|
}
|
||||||
|
}).exec(accountId);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveListMembership(String listId, boolean isMember) {
|
||||||
|
userInList.put(listId, isMember);
|
||||||
|
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
||||||
|
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
||||||
|
req.setCallback(new SimpleCallback<>(this) {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object o) {}
|
||||||
|
}).exec(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
userInListBefore.clear();
|
||||||
|
userInList.clear();
|
||||||
|
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
||||||
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
|
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
||||||
|
userInList.putAll(userInListBefore);
|
||||||
|
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
||||||
|
if (profileAccountId == null) return;
|
||||||
|
|
||||||
|
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.this) {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<ListTimeline> allLists) {
|
||||||
|
List<ListTimeline> newLists = new ArrayList<>();
|
||||||
|
for (ListTimeline l : allLists) {
|
||||||
|
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
||||||
|
if (!userInListBefore.containsKey(l.id)) {
|
||||||
|
userInListBefore.put(l.id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userInList.putAll(userInListBefore);
|
||||||
|
onDataLoaded(newLists, false);
|
||||||
|
}
|
||||||
|
}).exec(accountId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFragmentResult(int reqCode, boolean listChanged, Bundle result){
|
||||||
|
if (reqCode == LIST_CHANGED_RESULT && listChanged) {
|
||||||
|
String listID = result.getString("listID");
|
||||||
|
for (int i = 0; i < data.size(); i++) {
|
||||||
|
ListTimeline item = data.get(i);
|
||||||
|
if (item.id.equals(listID)) {
|
||||||
|
if (result.getBoolean("deleted")) {
|
||||||
|
data.remove(i);
|
||||||
|
adapter.notifyItemRemoved(i);
|
||||||
|
} else {
|
||||||
|
item.title = result.getString("listTitle", item.title);
|
||||||
|
item.repliesPolicy = ListTimeline.RepliesPolicy.values()[result.getInt("repliesPolicy")];
|
||||||
|
adapter.notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
||||||
|
return adapter = new ListsAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void scrollToTop() {
|
||||||
|
smoothScrollRecyclerViewToTop(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new ListViewHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
||||||
|
holder.bind(data.get(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
||||||
|
private final TextView title;
|
||||||
|
private final CheckBox listToggle;
|
||||||
|
|
||||||
|
public ListViewHolder(){
|
||||||
|
super(getActivity(), R.layout.item_text, list);
|
||||||
|
title=findViewById(R.id.title);
|
||||||
|
listToggle=findViewById(R.id.list_toggle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(ListTimeline item) {
|
||||||
|
title.setText(item.title);
|
||||||
|
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_list_24_regular), null, null, null);
|
||||||
|
if (profileAccountId != null) {
|
||||||
|
Boolean checked = userInList.get(item.id);
|
||||||
|
listToggle.setVisibility(View.VISIBLE);
|
||||||
|
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
||||||
|
listToggle.setOnClickListener(this::onClickToggle);
|
||||||
|
} else {
|
||||||
|
listToggle.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onClickToggle(View view) {
|
||||||
|
saveListMembership(item.id, listToggle.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountId);
|
||||||
|
args.putString("listID", item.id);
|
||||||
|
args.putString("listTitle", item.title);
|
||||||
|
args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
||||||
|
Nav.goForResult(getActivity(), ListTimelineFragment.class, args, LIST_CHANGED_RESULT, ListTimelinesFragment.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.AddAccountsToList;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.CreateList;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
|
||||||
import org.joinmastodon.android.api.requests.lists.RemoveAccountsFromList;
|
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|
||||||
import org.joinmastodon.android.ui.views.ListTimelineEditor;
|
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
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.BindableViewHolder;
|
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
|
||||||
|
|
||||||
public class ListsFragment extends RecyclerFragment<ListTimeline> implements ScrollableToTop, ProvidesAssistContent.ProvidesWebUri {
|
|
||||||
private String accountID;
|
|
||||||
private String profileAccountId;
|
|
||||||
private final HashMap<String, Boolean> userInListBefore = new HashMap<>();
|
|
||||||
private final HashMap<String, Boolean> userInList = new HashMap<>();
|
|
||||||
private ListsAdapter adapter;
|
|
||||||
|
|
||||||
public ListsFragment() {
|
|
||||||
super(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Bundle args = getArguments();
|
|
||||||
accountID = args.getString("account");
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
E.register(this);
|
|
||||||
|
|
||||||
if(args.containsKey("profileAccount")){
|
|
||||||
profileAccountId=args.getString("profileAccount");
|
|
||||||
String profileDisplayUsername = args.getString("profileDisplayUsername");
|
|
||||||
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.sk_your_lists);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onShown(){
|
|
||||||
super.onShown();
|
|
||||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
inflater.inflate(R.menu.menu_list, menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.create) {
|
|
||||||
ListTimelineEditor editor = new ListTimelineEditor(getContext());
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_create_list_title)
|
|
||||||
.setIcon(R.drawable.ic_fluent_people_add_28_regular)
|
|
||||||
.setView(editor)
|
|
||||||
.setPositiveButton(R.string.sk_create, (d, which) ->
|
|
||||||
new CreateList(editor.getTitle(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(ListTimeline list) {
|
|
||||||
data.add(0, list);
|
|
||||||
adapter.notifyItemRangeInserted(0, 1);
|
|
||||||
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID)
|
|
||||||
)
|
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveListMembership(String listId, boolean isMember) {
|
|
||||||
userInList.put(listId, isMember);
|
|
||||||
List<String> accountIdList = Collections.singletonList(profileAccountId);
|
|
||||||
MastodonAPIRequest<Object> req = isMember ? new AddAccountsToList(listId, accountIdList) : new RemoveAccountsFromList(listId, accountIdList);
|
|
||||||
req.setCallback(new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Object o) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getContext());
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){
|
|
||||||
userInListBefore.clear();
|
|
||||||
userInList.clear();
|
|
||||||
currentRequest=(profileAccountId != null ? new GetLists(profileAccountId) : new GetLists())
|
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
|
||||||
userInList.putAll(userInListBefore);
|
|
||||||
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
|
||||||
if (profileAccountId == null) return;
|
|
||||||
|
|
||||||
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListsFragment.this) {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(List<ListTimeline> allLists) {
|
|
||||||
if (getActivity() == null) return;
|
|
||||||
List<ListTimeline> newLists = new ArrayList<>();
|
|
||||||
for (ListTimeline l : allLists) {
|
|
||||||
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
|
||||||
if (!userInListBefore.containsKey(l.id)) {
|
|
||||||
userInListBefore.put(l.id, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
userInList.putAll(userInListBefore);
|
|
||||||
onDataLoaded(newLists, false);
|
|
||||||
}
|
|
||||||
}).exec(accountID);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListDeletedEvent(ListDeletedEvent event) {
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
|
||||||
ListTimeline item = data.get(i);
|
|
||||||
if (item.id.equals(event.id)) {
|
|
||||||
data.remove(i);
|
|
||||||
adapter.notifyItemRemoved(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void onListUpdatedCreatedEvent(ListUpdatedCreatedEvent event) {
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
|
||||||
ListTimeline item = data.get(i);
|
|
||||||
if (item.id.equals(event.id)) {
|
|
||||||
item.title = event.title;
|
|
||||||
item.repliesPolicy = event.repliesPolicy;
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
|
|
||||||
return adapter = new ListsAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void scrollToTop() {
|
|
||||||
smoothScrollRecyclerViewToTop(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path("/lists").build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return new ListViewHolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
|
|
||||||
holder.bind(data.get(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return data.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
|
|
||||||
private final TextView title;
|
|
||||||
private final CheckBox listToggle;
|
|
||||||
|
|
||||||
public ListViewHolder(){
|
|
||||||
super(getActivity(), R.layout.item_text, list);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
listToggle=findViewById(R.id.list_toggle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(ListTimeline item) {
|
|
||||||
title.setText(item.title);
|
|
||||||
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_24_regular), null, null, null);
|
|
||||||
if (profileAccountId != null) {
|
|
||||||
Boolean checked = userInList.get(item.id);
|
|
||||||
listToggle.setVisibility(View.VISIBLE);
|
|
||||||
listToggle.setChecked(userInList.containsKey(item.id) && checked != null && checked);
|
|
||||||
listToggle.setOnClickListener(this::onClickToggle);
|
|
||||||
} else {
|
|
||||||
listToggle.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onClickToggle(View view) {
|
|
||||||
saveListMembership(item.id, listToggle.isChecked());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick() {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putString("listID", item.id);
|
|
||||||
args.putString("listTitle", item.title);
|
|
||||||
if (item.repliesPolicy != null) args.putInt("repliesPolicy", item.repliesPolicy.ordinal());
|
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -14,12 +13,6 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
@@ -31,7 +24,12 @@ import org.joinmastodon.android.ui.SimpleViewHolder;
|
|||||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -39,16 +37,17 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop, ProvidesAssistContent {
|
public class NotificationsFragment extends MastodonToolbarFragment implements ScrollableToTop{
|
||||||
|
|
||||||
private TabLayout tabLayout;
|
private TabLayout tabLayout;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
private TabLayoutMediator tabLayoutMediator;
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
|
|
||||||
private NotificationsListFragment allNotificationsFragment, mentionsFragment;
|
private NotificationsListFragment allNotificationsFragment, mentionsFragment, conversationsFragment;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -105,12 +104,13 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
pager=view.findViewById(R.id.pager);
|
pager=view.findViewById(R.id.pager);
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
|
|
||||||
tabViews=new FrameLayout[2];
|
tabViews=new FrameLayout[3];
|
||||||
for(int i=0;i<tabViews.length;i++){
|
for(int i=0;i<tabViews.length;i++){
|
||||||
FrameLayout tabView=new FrameLayout(getActivity());
|
FrameLayout tabView=new FrameLayout(getActivity());
|
||||||
tabView.setId(switch(i){
|
tabView.setId(switch(i){
|
||||||
case 0 -> R.id.notifications_all;
|
case 0 -> R.id.notifications_all;
|
||||||
case 1 -> R.id.notifications_mentions;
|
case 1 -> R.id.notifications_mentions;
|
||||||
|
case 2 -> R.id.notifications_posts;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||||
});
|
});
|
||||||
tabView.setVisibility(View.GONE);
|
tabView.setVisibility(View.GONE);
|
||||||
@@ -120,18 +120,6 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
|
|
||||||
tabLayout.setTabTextSize(V.dp(16));
|
tabLayout.setTabTextSize(V.dp(16));
|
||||||
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
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.setOffscreenPageLimit(4);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
@@ -162,9 +150,15 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
mentionsFragment=new NotificationsListFragment();
|
mentionsFragment=new NotificationsListFragment();
|
||||||
mentionsFragment.setArguments(args);
|
mentionsFragment.setArguments(args);
|
||||||
|
|
||||||
|
args=new Bundle(args);
|
||||||
|
args.putBoolean("onlyConversations", true);
|
||||||
|
conversationsFragment =new NotificationsListFragment();
|
||||||
|
conversationsFragment.setArguments(args);
|
||||||
|
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.add(R.id.notifications_all, allNotificationsFragment)
|
.add(R.id.notifications_all, allNotificationsFragment)
|
||||||
.add(R.id.notifications_mentions, mentionsFragment)
|
.add(R.id.notifications_mentions, mentionsFragment)
|
||||||
|
.add(R.id.notifications_posts, conversationsFragment)
|
||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +168,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
tab.setText(switch(position){
|
tab.setText(switch(position){
|
||||||
case 0 -> R.string.all_notifications;
|
case 0 -> R.string.all_notifications;
|
||||||
case 1 -> R.string.mentions;
|
case 1 -> R.string.mentions;
|
||||||
|
case 2 -> R.string.sk_conversations;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||||
});
|
});
|
||||||
tab.view.textView.setAllCaps(true);
|
tab.view.textView.setAllCaps(true);
|
||||||
@@ -188,7 +183,6 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
||||||
if (getActivity() == null) return;
|
|
||||||
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,28 +211,23 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
protected void updateToolbar(){
|
protected void updateToolbar(){
|
||||||
super.updateToolbar();
|
super.updateToolbar();
|
||||||
getToolbar().setOutlineProvider(null);
|
getToolbar().setOutlineProvider(null);
|
||||||
getToolbar().setOnClickListener(v->scrollToTop());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationsListFragment getFragmentForPage(int page){
|
private NotificationsListFragment getFragmentForPage(int page){
|
||||||
return switch(page){
|
return switch(page){
|
||||||
case 0 -> allNotificationsFragment;
|
case 0 -> allNotificationsFragment;
|
||||||
case 1 -> mentionsFragment;
|
case 1 -> mentionsFragment;
|
||||||
|
case 2 -> conversationsFragment;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
callFragmentToProvideAssistContent(getFragmentForPage(pager.getCurrentItem()), assistContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DiscoverPagerAdapter extends RecyclerView.Adapter<SimpleViewHolder>{
|
private class DiscoverPagerAdapter extends RecyclerView.Adapter<SimpleViewHolder>{
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=tabViews[viewType];
|
FrameLayout view=tabViews[viewType];
|
||||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
view.setVisibility(View.VISIBLE);
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
@@ -249,7 +238,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
return 2;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
@@ -11,27 +11,21 @@ import com.squareup.otto.Subscribe;
|
|||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
import org.joinmastodon.android.api.requests.notifications.PleromaMarkNotificationsRead;
|
import org.joinmastodon.android.api.requests.timelines.GetConversationsTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
|
||||||
import org.joinmastodon.android.model.Filter;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Markers;
|
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
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.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.InsetStatusItemDecoration;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -44,18 +38,17 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
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.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
public class NotificationsListFragment extends BaseStatusListFragment<Notification>{
|
||||||
private boolean onlyMentions;
|
private boolean onlyMentions;
|
||||||
private boolean onlyPosts;
|
private boolean onlyPosts;
|
||||||
private String maxID;
|
|
||||||
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
|
||||||
|
|
||||||
@Override
|
private boolean onlyConversations;
|
||||||
protected boolean wantsComposeButton() {
|
private String maxID;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -74,6 +67,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
onlyMentions=getArguments().getBoolean("onlyMentions", false);
|
onlyMentions=getArguments().getBoolean("onlyMentions", false);
|
||||||
onlyPosts=getArguments().getBoolean("onlyPosts", false);
|
onlyPosts=getArguments().getBoolean("onlyPosts", false);
|
||||||
|
onlyConversations=getArguments().getBoolean("onlyConversations", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,15 +80,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
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){
|
String extraText=switch(n.type){
|
||||||
case FOLLOW -> getString(R.string.user_followed_you);
|
case FOLLOW -> getString(R.string.user_followed_you);
|
||||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||||
@@ -102,26 +87,23 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
case REBLOG -> getString(R.string.notification_boosted);
|
case REBLOG -> getString(R.string.notification_boosted);
|
||||||
case FAVORITE -> getString(R.string.user_favorited);
|
case FAVORITE -> getString(R.string.user_favorited);
|
||||||
case POLL -> getString(R.string.poll_ended);
|
case POLL -> getString(R.string.poll_ended);
|
||||||
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, n.emojiUrl!=null ? HtmlParser.parseCustomEmoji(extraText, Collections.singletonList(emoji)) : extraText, n, null) : null;
|
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText, n, null) : null;
|
||||||
if(n.status!=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);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n);
|
||||||
|
if(titleItem!=null){
|
||||||
|
for(StatusDisplayItem item:items){
|
||||||
|
if(item instanceof ImageStatusDisplayItem imgItem){
|
||||||
|
imgItem.horizontalInset=V.dp(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(titleItem!=null)
|
if(titleItem!=null)
|
||||||
items.add(0, titleItem);
|
items.add(0, titleItem);
|
||||||
return items;
|
return items;
|
||||||
}else if(titleItem!=null){
|
}else if(titleItem!=null){
|
||||||
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this,
|
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this, n.account, n);
|
||||||
reportTarget != null ? reportTarget : n.account, n);
|
return Arrays.asList(titleItem, card);
|
||||||
TextStatusDisplayItem text = n.report != null && !TextUtils.isEmpty(n.report.comment) ?
|
|
||||||
new TextStatusDisplayItem(n.id, n.report.comment, this,
|
|
||||||
Status.ofFake(n.id, n.report.comment, n.createdAt), true) :
|
|
||||||
null;
|
|
||||||
return text == null ? Arrays.asList(titleItem, card) : Arrays.asList(titleItem, text, card);
|
|
||||||
}else{
|
}else{
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
@@ -133,21 +115,20 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
knownAccounts.put(s.account.id, s.account);
|
knownAccounts.put(s.account.id, s.account);
|
||||||
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
|
if(s.status!=null && !knownAccounts.containsKey(s.status.account.id))
|
||||||
knownAccounts.put(s.status.account.id, s.status.account);
|
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
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
AccountSessionManager.getInstance()
|
AccountSessionManager.getInstance()
|
||||||
.getAccount(accountID).getCacheController()
|
.getAccount(accountID).getCacheController()
|
||||||
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
|
.getNotifications(offset > 0 ? maxID : null, count, onlyMentions, onlyPosts || onlyConversations, refreshing, new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Notification>> result){
|
public void onSuccess(PaginatedResponse<List<Notification>> result) {
|
||||||
if (getActivity() == null) return;
|
if (getActivity() == null)
|
||||||
|
return;
|
||||||
if (refreshing)
|
if (refreshing)
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
onDataLoaded(result.items.stream().filter(n -> n.type != null || (onlyConversations &&)).collect(Collectors.toList()), !result.items.isEmpty());
|
||||||
Set<String> needRelationships = result.items.stream()
|
Set<String> needRelationships = result.items.stream()
|
||||||
.filter(ntf -> ntf.status == null && !relationships.containsKey(ntf.account.id))
|
.filter(ntf -> ntf.status == null && !relationships.containsKey(ntf.account.id))
|
||||||
.map(ntf -> ntf.account.id)
|
.map(ntf -> ntf.account.id)
|
||||||
@@ -155,19 +136,12 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
loadRelationships(needRelationships);
|
loadRelationships(needRelationships);
|
||||||
maxID = result.maxID;
|
maxID = result.maxID;
|
||||||
|
|
||||||
Markers markers = AccountSessionManager.getInstance().getAccount(accountID).markers;
|
if (offset == 0 && !result.items.isEmpty()) {
|
||||||
if(offset==0 && !result.items.isEmpty() && !result.isFromCache() && markers != null && markers.notifications != null){
|
|
||||||
E.post(new AllNotificationsSeenEvent());
|
|
||||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).markers
|
|
||||||
.notifications.lastReadId = result.items.get(0).id;
|
|
||||||
AccountSessionManager.getInstance().writeAccountsFile();
|
|
||||||
|
|
||||||
if (AccountSessionManager.getInstance().getAccount(accountID).getInstance().map(Instance::isPleroma).orElse(false))
|
|
||||||
new PleromaMarkNotificationsRead(result.items.get(0).id).exec(accountID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -199,9 +173,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
||||||
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
||||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||||
}else if(n.report != null){
|
|
||||||
String domain = AccountSessionManager.getInstance().getAccount(accountID).domain;
|
|
||||||
UiUtils.launchWebBrowser(getActivity(), "https://"+domain+"/admin/reports/"+n.report.id);
|
|
||||||
}else{
|
}else{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -214,7 +185,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||||
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification getNotificationByID(String id){
|
private Notification getNotificationByID(String id){
|
||||||
@@ -271,11 +241,4 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
displayItems.subList(index, lastIndex).clear();
|
displayItems.subList(index, lastIndex).clear();
|
||||||
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
adapter.notifyItemRangeRemoved(index, lastIndex-index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return base.path(isInstanceAkkoma()
|
|
||||||
? "/users/" + getSession().self.username + "/interactions"
|
|
||||||
: "/notifications").build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public abstract class PinnableStatusListFragment extends StatusListFragment {
|
|
||||||
protected List<TimelineDefinition> pinnedTimelines;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.getDefaultTimelines(accountID)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
updatePinButton(menu.findItem(R.id.pin));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isPinned() {
|
|
||||||
return pinnedTimelines.contains(makeTimelineDefinition());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updatePinButton(MenuItem pin) {
|
|
||||||
boolean pinned = isPinned();
|
|
||||||
pin.setIcon(pinned ?
|
|
||||||
R.drawable.ic_fluent_pin_24_filled :
|
|
||||||
R.drawable.ic_fluent_pin_24_regular);
|
|
||||||
pin.setTitle(pinned ? R.string.sk_unpin_timeline : R.string.sk_pin_timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract TimelineDefinition makeTimelineDefinition();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
if (item.getItemId() == R.id.pin) {
|
|
||||||
togglePin(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void togglePin(MenuItem pin) {
|
|
||||||
onPinnedUpdated(true);
|
|
||||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
TimelineDefinition def = makeTimelineDefinition();
|
|
||||||
boolean pinned = isPinned();
|
|
||||||
if (pinned) pinnedTimelines.remove(def);
|
|
||||||
else pinnedTimelines.add(def);
|
|
||||||
Toast.makeText(getContext(), pinned ? R.string.sk_unpinned_timeline : R.string.sk_pinned_timeline, Toast.LENGTH_SHORT).show();
|
|
||||||
GlobalUserPreferences.pinnedTimelines.put(accountID, pinnedTimelines);
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
updatePinButton(pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPinnedUpdated(boolean pinned) {}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
@@ -12,11 +13,23 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
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.R;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||||
@@ -26,11 +39,8 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import me.grishka.appkit.api.Callback;
|
||||||
import androidx.annotation.Nullable;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
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.fragments.WindowInsetsAwareFragment;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
@@ -46,6 +56,11 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
|||||||
private static final int MAX_FIELDS=4;
|
private static final int MAX_FIELDS=4;
|
||||||
|
|
||||||
public UsableRecyclerView list;
|
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 List<AccountField> fields=Collections.emptyList();
|
||||||
private AboutAdapter adapter;
|
private AboutAdapter adapter;
|
||||||
private Paint dividerPaint=new Paint();
|
private Paint dividerPaint=new Paint();
|
||||||
@@ -64,11 +79,49 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
|||||||
adapter.notifyDataSetChanged();
|
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
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
|
||||||
list=new UsableRecyclerView(getActivity());
|
View view = inflater.inflate(R.layout.fragment_profile_about, null);
|
||||||
list.setId(R.id.list);
|
|
||||||
|
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.setItemAnimator(new BetterItemAnimator());
|
list.setItemAnimator(new BetterItemAnimator());
|
||||||
list.setDrawSelectorOnTop(true);
|
list.setDrawSelectorOnTop(true);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
@@ -95,8 +148,20 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return list;
|
return view;
|
||||||
}
|
}
|
||||||
|
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.sk_personal_note_update_failed), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void enterEditMode(List<AccountField> editableFields){
|
public void enterEditMode(List<AccountField> editableFields){
|
||||||
isInEditMode=true;
|
isInEditMode=true;
|
||||||
@@ -337,7 +402,7 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
|||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||||
super.onSelectedChanged(viewHolder, actionState);
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||||
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.setTag(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();
|
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
draggedViewHolder=viewHolder;
|
draggedViewHolder=viewHolder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,32 +6,31 @@ import android.animation.AnimatorSet;
|
|||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.GradientDrawable;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.SubMenu;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.view.animation.TranslateAnimation;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
@@ -43,6 +42,11 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
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.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
|
||||||
@@ -50,6 +54,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
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.requests.accounts.UpdateAccountCredentials;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||||
@@ -58,9 +63,7 @@ import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
|||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||||
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
||||||
@@ -69,13 +72,10 @@ import org.joinmastodon.android.ui.tabs.TabLayout;
|
|||||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.CoverImageView;
|
import org.joinmastodon.android.ui.views.CoverImageView;
|
||||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
|
||||||
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
import org.joinmastodon.android.ui.views.NestedRecyclerScrollView;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -86,14 +86,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -101,46 +93,40 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.fragments.LoaderFragment;
|
import me.grishka.appkit.fragments.LoaderFragment;
|
||||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
|
||||||
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
|
|
||||||
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
|
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
|
||||||
|
|
||||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop, HasFab, ProvidesAssistContent.ProvidesWebUri {
|
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
||||||
private static final int AVATAR_RESULT=722;
|
private static final int AVATAR_RESULT=722;
|
||||||
private static final int COVER_RESULT=343;
|
private static final int COVER_RESULT=343;
|
||||||
|
|
||||||
private ImageView avatar;
|
private ImageView avatar;
|
||||||
private CoverImageView cover;
|
private CoverImageView cover;
|
||||||
private View avatarBorder, nameWrap;
|
private View avatarBorder;
|
||||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel, postsCount, postsLabel;
|
||||||
private ProgressBarButton actionButton, notifyButton;
|
private ProgressBarButton actionButton, notifyButton;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private NestedRecyclerScrollView scrollView;
|
private NestedRecyclerScrollView scrollView;
|
||||||
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, pinnedPostsFragment, mediaFragment;
|
||||||
// private ProfileAboutFragment aboutFragment;
|
private ProfileAboutFragment aboutFragment;
|
||||||
private TabLayout tabbar;
|
private TabLayout tabbar;
|
||||||
private SwipeRefreshLayout refreshLayout;
|
private SwipeRefreshLayout refreshLayout;
|
||||||
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
private CoverOverlayGradientDrawable coverGradient=new CoverOverlayGradientDrawable();
|
||||||
private float titleTransY;
|
private float titleTransY;
|
||||||
private View postsBtn, followersBtn, followingBtn, profileCounters;
|
private View postsBtn, followersBtn, followingBtn;
|
||||||
private EditText nameEdit, bioEdit;
|
private EditText nameEdit, bioEdit;
|
||||||
private ProgressBar actionProgress, notifyProgress;
|
private ProgressBar actionProgress, notifyProgress;
|
||||||
private FrameLayout[] tabViews;
|
private FrameLayout[] tabViews;
|
||||||
private TabLayoutMediator tabLayoutMediator;
|
private TabLayoutMediator tabLayoutMediator;
|
||||||
private TextView followsYouView;
|
private TextView followsYouView;
|
||||||
private ViewGroup rolesView;
|
|
||||||
|
|
||||||
|
public FrameLayout noteWrap;
|
||||||
|
public EditText noteEdit;
|
||||||
|
private String note;
|
||||||
private Account account;
|
private Account account;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private String domain;
|
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private int statusBarHeight;
|
private int statusBarHeight;
|
||||||
private boolean isOwnProfile;
|
private boolean isOwnProfile;
|
||||||
@@ -154,16 +140,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private WindowInsets childInsets;
|
private WindowInsets childInsets;
|
||||||
private PhotoViewer currentPhotoViewer;
|
private PhotoViewer currentPhotoViewer;
|
||||||
private boolean editModeLoading;
|
private boolean editModeLoading;
|
||||||
|
private boolean isScrollingUp = false;
|
||||||
private int maxFields = 4;
|
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
public UsableRecyclerView list;
|
|
||||||
private List<AccountField> metadataListData=Collections.emptyList();
|
|
||||||
private MetadataAdapter adapter;
|
|
||||||
private ItemTouchHelper dragHelper=new ItemTouchHelper(new ReorderCallback());
|
|
||||||
private RecyclerView.ViewHolder draggedViewHolder;
|
|
||||||
private ListImageLoaderWrapper imgLoader;
|
|
||||||
|
|
||||||
public ProfileFragment(){
|
public ProfileFragment(){
|
||||||
super(R.layout.loader_fragment_overlay_toolbar);
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
@@ -176,7 +153,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
domain=AccountSessionManager.getInstance().getAccount(accountID).domain;
|
|
||||||
if(getArguments().containsKey("profileAccount")){
|
if(getArguments().containsKey("profileAccount")){
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||||
profileAccountID=account.id;
|
profileAccountID=account.id;
|
||||||
@@ -184,12 +160,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
loaded=true;
|
loaded=true;
|
||||||
if(!isOwnProfile)
|
if(!isOwnProfile)
|
||||||
loadRelationship();
|
loadRelationship();
|
||||||
else {
|
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(domain);
|
|
||||||
if (instance != null && instance.isPleroma()) {
|
|
||||||
maxFields = instance.pleroma.metadata.fieldsLimits.maxFields;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
profileAccountID=getArguments().getString("profileAccountID");
|
profileAccountID=getArguments().getString("profileAccountID");
|
||||||
if(!getArguments().getBoolean("noAutoLoad", false))
|
if(!getArguments().getBoolean("noAutoLoad", false))
|
||||||
@@ -216,16 +186,15 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
cover=content.findViewById(R.id.cover);
|
cover=content.findViewById(R.id.cover);
|
||||||
avatarBorder=content.findViewById(R.id.avatar_border);
|
avatarBorder=content.findViewById(R.id.avatar_border);
|
||||||
name=content.findViewById(R.id.name);
|
name=content.findViewById(R.id.name);
|
||||||
nameWrap=content.findViewById(R.id.name_wrap);
|
|
||||||
username=content.findViewById(R.id.username);
|
username=content.findViewById(R.id.username);
|
||||||
bio=content.findViewById(R.id.bio);
|
bio=content.findViewById(R.id.bio);
|
||||||
profileCounters=content.findViewById(R.id.profile_counters);
|
|
||||||
followersCount=content.findViewById(R.id.followers_count);
|
followersCount=content.findViewById(R.id.followers_count);
|
||||||
followersLabel=content.findViewById(R.id.followers_label);
|
followersLabel=content.findViewById(R.id.followers_label);
|
||||||
followersBtn=content.findViewById(R.id.followers_btn);
|
followersBtn=content.findViewById(R.id.followers_btn);
|
||||||
followingCount=content.findViewById(R.id.following_count);
|
followingCount=content.findViewById(R.id.following_count);
|
||||||
followingLabel=content.findViewById(R.id.following_label);
|
followingLabel=content.findViewById(R.id.following_label);
|
||||||
followingBtn=content.findViewById(R.id.following_btn);
|
followingBtn=content.findViewById(R.id.following_btn);
|
||||||
|
|
||||||
postsCount=content.findViewById(R.id.posts_count);
|
postsCount=content.findViewById(R.id.posts_count);
|
||||||
postsLabel=content.findViewById(R.id.posts_label);
|
postsLabel=content.findViewById(R.id.posts_label);
|
||||||
postsBtn=content.findViewById(R.id.posts_btn);
|
postsBtn=content.findViewById(R.id.posts_btn);
|
||||||
@@ -241,8 +210,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
notifyProgress=content.findViewById(R.id.notify_progress);
|
notifyProgress=content.findViewById(R.id.notify_progress);
|
||||||
fab=content.findViewById(R.id.fab);
|
fab=content.findViewById(R.id.fab);
|
||||||
followsYouView=content.findViewById(R.id.follows_you);
|
followsYouView=content.findViewById(R.id.follows_you);
|
||||||
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(){
|
avatar.setOutlineProvider(new ViewOutlineProvider(){
|
||||||
@Override
|
@Override
|
||||||
@@ -252,6 +223,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
});
|
});
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
|
|
||||||
|
noteEdit.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
if (hasFocus) {
|
||||||
|
fab.setVisibility(View.GONE);
|
||||||
|
noteEditConfirm.setVisibility(View.VISIBLE);
|
||||||
|
noteEditConfirm.animate()
|
||||||
|
.alpha(1.0f)
|
||||||
|
.setDuration(700);
|
||||||
|
} else {
|
||||||
|
fab.setVisibility(View.VISIBLE);
|
||||||
|
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()){
|
FrameLayout sizeWrapper=new FrameLayout(getActivity()){
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
|
||||||
@@ -262,7 +258,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tabViews=new FrameLayout[4];
|
tabViews=new FrameLayout[5];
|
||||||
for(int i=0;i<tabViews.length;i++){
|
for(int i=0;i<tabViews.length;i++){
|
||||||
FrameLayout tabView=new FrameLayout(getActivity());
|
FrameLayout tabView=new FrameLayout(getActivity());
|
||||||
tabView.setId(switch(i){
|
tabView.setId(switch(i){
|
||||||
@@ -279,7 +275,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
UiUtils.reduceSwipeSensitivity(pager);
|
UiUtils.reduceSwipeSensitivity(pager);
|
||||||
pager.setOffscreenPageLimit(4);
|
pager.setOffscreenPageLimit(5);
|
||||||
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
|
||||||
pager.setAdapter(new ProfilePagerAdapter());
|
pager.setAdapter(new ProfilePagerAdapter());
|
||||||
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
pager.getLayoutParams().height=getResources().getDisplayMetrics().heightPixels;
|
||||||
@@ -313,7 +309,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
});
|
});
|
||||||
|
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
|
||||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||||
avatar.setOnClickListener(this::onAvatarClick);
|
avatar.setOnClickListener(this::onAvatarClick);
|
||||||
cover.setOnClickListener(this::onCoverClick);
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
@@ -335,30 +330,40 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
username.setOnLongClickListener(v->{
|
username.setOnLongClickListener(v->{
|
||||||
String usernameString=account.acct;
|
String usernameString=account.acct;
|
||||||
if(!usernameString.contains("@")){
|
if(!usernameString.contains("@")){
|
||||||
usernameString+="@"+domain;
|
usernameString+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
|
||||||
}
|
}
|
||||||
UiUtils.copyText(username, '@'+usernameString);
|
UiUtils.copyText(username, '@'+usernameString);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
list.setItemAnimator(new BetterItemAnimator());
|
|
||||||
list.setDrawSelectorOnTop(true);
|
|
||||||
list.setLayoutManager(new LinearLayoutManager(getActivity()));
|
|
||||||
imgLoader=new ListImageLoaderWrapper(getActivity(), list, new RecyclerViewDelegate(list), null);
|
|
||||||
list.setAdapter(adapter=new MetadataAdapter());
|
|
||||||
list.setClipToPadding(false);
|
|
||||||
|
|
||||||
return sizeWrapper;
|
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.sk_personal_note_update_failed), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(){
|
protected void doLoadData(){
|
||||||
currentRequest=new GetAccountByID(profileAccountID)
|
currentRequest=new GetAccountByID(profileAccountID)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
if (getActivity() == null) return;
|
|
||||||
account=result;
|
account=result;
|
||||||
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
@@ -381,7 +386,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(mediaFragment.loaded)
|
if(mediaFragment.loaded)
|
||||||
mediaFragment.onRefresh();
|
mediaFragment.onRefresh();
|
||||||
}
|
}
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
// if(noteEdit.hasFocus()){
|
||||||
|
// V.setVisibilityAnimated(fab, View.GONE);
|
||||||
|
// }else{
|
||||||
|
// V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
@@ -404,8 +413,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
||||||
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
pinnedPostsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.PINNED, false);
|
||||||
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
||||||
// aboutFragment=new ProfileAboutFragment();
|
aboutFragment=new ProfileAboutFragment();
|
||||||
setFields(fields);
|
aboutFragment.setFields(fields);
|
||||||
}
|
}
|
||||||
pager.getAdapter().notifyDataSetChanged();
|
pager.getAdapter().notifyDataSetChanged();
|
||||||
super.dataLoaded();
|
super.dataLoaded();
|
||||||
@@ -447,7 +456,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
toolbarSubtitleView.setTranslationY(titleTransY);
|
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||||
}
|
}
|
||||||
RecyclerFragment.setRefreshLayoutColors(refreshLayout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -499,29 +507,15 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
name.setText(ssb);
|
name.setText(ssb);
|
||||||
setTitle(ssb);
|
setTitle(ssb);
|
||||||
|
|
||||||
if (account.roles != null && !account.roles.isEmpty()) {
|
|
||||||
rolesView.setVisibility(View.VISIBLE);
|
|
||||||
rolesView.removeAllViews();
|
|
||||||
name.setPadding(0, 0, V.dp(12), 0);
|
|
||||||
for (Account.Role role : account.roles) {
|
|
||||||
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
|
||||||
roleText.setText(role.name);
|
|
||||||
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);
|
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
|
||||||
|
|
||||||
if(account.locked){
|
if(account.locked){
|
||||||
ssb=new SpannableStringBuilder("@");
|
ssb=new SpannableStringBuilder("@");
|
||||||
ssb.append(account.acct);
|
ssb.append(account.acct);
|
||||||
if(isSelf){
|
if(isSelf){
|
||||||
ssb.append('@');
|
ssb.append('@');
|
||||||
ssb.append(domain);
|
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
|
||||||
}
|
}
|
||||||
ssb.append(" ");
|
ssb.append(" ");
|
||||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
||||||
@@ -529,9 +523,22 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
lock.setTint(username.getCurrentTextColor());
|
lock.setTint(username.getCurrentTextColor());
|
||||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
|
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
|
||||||
username.setText(ssb);
|
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{
|
}else{
|
||||||
// noinspection SetTextI18n
|
// noinspection SetTextI18n
|
||||||
username.setText('@'+account.acct+(isSelf ? ('@'+domain) : ""));
|
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
|
||||||
}
|
}
|
||||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
if(TextUtils.isEmpty(parsedBio)){
|
if(TextUtils.isEmpty(parsedBio)){
|
||||||
@@ -540,6 +547,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
bio.setVisibility(View.VISIBLE);
|
bio.setVisibility(View.VISIBLE);
|
||||||
bio.setText(parsedBio);
|
bio.setText(parsedBio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
|
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
|
||||||
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
|
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
|
||||||
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
|
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
|
||||||
@@ -559,12 +568,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
fields.clear();
|
fields.clear();
|
||||||
|
|
||||||
if (account.createdAt != null) {
|
|
||||||
AccountField joined=new AccountField();
|
AccountField joined=new AccountField();
|
||||||
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
joined.parsedName=joined.name=getString(R.string.profile_joined);
|
||||||
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
joined.parsedValue=joined.value=DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).format(LocalDateTime.ofInstant(account.createdAt, ZoneId.systemDefault()));
|
||||||
fields.add(joined);
|
fields.add(joined);
|
||||||
}
|
|
||||||
|
|
||||||
for(AccountField field:account.fields){
|
for(AccountField field:account.fields){
|
||||||
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
@@ -583,7 +590,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
fields.add(field);
|
fields.add(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
setFields(fields);
|
if(aboutFragment!=null){
|
||||||
|
aboutFragment.setFields(fields);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToolbar(){
|
private void updateToolbar(){
|
||||||
@@ -619,16 +628,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(relationship==null && !isOwnProfile)
|
if(relationship==null && !isOwnProfile)
|
||||||
return;
|
return;
|
||||||
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
|
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
|
||||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags);
|
if(isOwnProfile){
|
||||||
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.manage_user_lists, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled, R.id.share);
|
||||||
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
|
}else{
|
||||||
openWithAccounts.setVisible(hasMultipleAccounts);
|
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
||||||
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()));
|
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||||
if(isOwnProfile)
|
if(isOwnProfile)
|
||||||
@@ -714,7 +717,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
args.putString("profileAccount", profileAccountID);
|
args.putString("profileAccount", profileAccountID);
|
||||||
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||||
}
|
}
|
||||||
Nav.go(getActivity(), ListsFragment.class, args);
|
Nav.go(getActivity(), ListTimelinesFragment.class, args);
|
||||||
}else if(id==R.id.followed_hashtags){
|
}else if(id==R.id.followed_hashtags){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -752,7 +755,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelationship(){
|
private void updateRelationship(){
|
||||||
if (getActivity() == null) return;
|
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
actionButton.setVisibility(View.VISIBLE);
|
actionButton.setVisibility(View.VISIBLE);
|
||||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||||
@@ -762,23 +764,17 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||||
notifyButton.setSelected(relationship.notifying);
|
notifyButton.setSelected(relationship.notifying);
|
||||||
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
if (!isOwnProfile) {
|
||||||
|
setNote(relationship.note);
|
||||||
|
aboutFragment.setNote(relationship.note, accountID, profileAccountID);
|
||||||
|
}
|
||||||
|
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageButton getFab() {
|
public ImageButton getFab() {
|
||||||
return fab;
|
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){
|
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||||
int topBarsH=getToolbar().getHeight()+statusBarHeight;
|
int topBarsH=getToolbar().getHeight()+statusBarHeight;
|
||||||
if(scrollY>avatarBorder.getTop()-topBarsH){
|
if(scrollY>avatarBorder.getTop()-topBarsH){
|
||||||
@@ -799,8 +795,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
coverGradient.setTopOffset(scrollY);
|
coverGradient.setTopOffset(scrollY);
|
||||||
cover.invalidate();
|
cover.invalidate();
|
||||||
titleTransY=getToolbar().getHeight();
|
titleTransY=getToolbar().getHeight();
|
||||||
if(scrollY>nameWrap.getTop()-topBarsH){
|
if(scrollY>name.getTop()-topBarsH){
|
||||||
titleTransY=Math.max(0f, titleTransY-(scrollY-(nameWrap.getTop()-topBarsH)));
|
titleTransY=Math.max(0f, titleTransY-(scrollY-(name.getTop()-topBarsH)));
|
||||||
}
|
}
|
||||||
if(toolbarTitleView!=null){
|
if(toolbarTitleView!=null){
|
||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
@@ -817,7 +813,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
case 1 -> postsWithRepliesFragment;
|
case 1 -> postsWithRepliesFragment;
|
||||||
case 2 -> pinnedPostsFragment;
|
case 2 -> pinnedPostsFragment;
|
||||||
case 3 -> mediaFragment;
|
case 3 -> mediaFragment;
|
||||||
// case 4 -> aboutFragment;
|
case 4 -> aboutFragment;
|
||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -837,31 +833,6 @@ 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){
|
private void setActionProgressVisible(boolean visible){
|
||||||
actionButton.setTextVisible(!visible);
|
actionButton.setTextVisible(!visible);
|
||||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
@@ -884,7 +855,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
enterEditMode(result);
|
enterEditMode(result);
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -892,7 +864,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
editModeLoading=false;
|
editModeLoading=false;
|
||||||
if (getActivity() == null) return;
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
error.showToast(getActivity());
|
error.showToast(getActivity());
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -907,12 +880,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
pager.setUserInputEnabled(false);
|
pager.setUserInputEnabled(false);
|
||||||
actionButton.setText(R.string.done);
|
actionButton.setText(R.string.done);
|
||||||
|
pager.setCurrentItem(4);
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay, getActivity().getTheme()).mutate();
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, .3f));
|
||||||
|
tabbar.getTabAt(i).view.setEnabled(false);
|
||||||
|
}
|
||||||
|
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
||||||
avatar.setForeground(overlay);
|
avatar.setForeground(overlay);
|
||||||
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
animators.add(ObjectAnimator.ofInt(overlay, "alpha", 0, 255));
|
||||||
|
|
||||||
nameWrap.setVisibility(View.GONE);
|
|
||||||
nameEdit.setVisibility(View.VISIBLE);
|
nameEdit.setVisibility(View.VISIBLE);
|
||||||
nameEdit.setText(account.displayName);
|
nameEdit.setText(account.displayName);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
@@ -924,9 +901,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
bioEdit.setText(account.source.note);
|
bioEdit.setText(account.source.note);
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f, 1f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 0f));
|
||||||
profileCounters.setVisibility(View.GONE);
|
|
||||||
pager.setVisibility(View.GONE);
|
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, .3f));
|
||||||
tabbar.setVisibility(View.GONE);
|
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, .3f));
|
||||||
|
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, .3f));
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
@@ -934,12 +912,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
// aboutFragment.enterEditMode(account.source.fields);
|
aboutFragment.enterEditMode(account.source.fields);
|
||||||
|
|
||||||
V.setVisibilityAnimated(fab, View.GONE);
|
|
||||||
metadataListData=account.source.fields;
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
dragHelper.attachToRecyclerView(list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exitEditMode(){
|
private void exitEditMode(){
|
||||||
@@ -950,14 +923,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
ArrayList<Animator> animators=new ArrayList<>();
|
ArrayList<Animator> animators=new ArrayList<>();
|
||||||
actionButton.setText(R.string.edit_profile);
|
actionButton.setText(R.string.edit_profile);
|
||||||
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
animators.add(ObjectAnimator.ofFloat(tabbar.getTabAt(i).view, View.ALPHA, 1f));
|
||||||
|
}
|
||||||
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
animators.add(ObjectAnimator.ofInt(avatar.getForeground(), "alpha", 0));
|
||||||
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(nameEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
animators.add(ObjectAnimator.ofFloat(bioEdit, View.ALPHA, 0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
animators.add(ObjectAnimator.ofFloat(bio, View.ALPHA, 1f));
|
||||||
profileCounters.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(postsBtn, View.ALPHA, 1f));
|
||||||
pager.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(followersBtn, View.ALPHA, 1f));
|
||||||
tabbar.setVisibility(View.VISIBLE);
|
animators.add(ObjectAnimator.ofFloat(followingBtn, View.ALPHA, 1f));
|
||||||
V.setVisibilityAnimated(nameWrap, View.VISIBLE);
|
|
||||||
|
|
||||||
AnimatorSet set=new AnimatorSet();
|
AnimatorSet set=new AnimatorSet();
|
||||||
set.playTogether(animators);
|
set.playTogether(animators);
|
||||||
@@ -966,21 +941,20 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
set.addListener(new AnimatorListenerAdapter(){
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public void onAnimationEnd(Animator animation){
|
||||||
|
for(int i=0;i<tabViews.length-1;i++){
|
||||||
|
tabbar.getTabAt(i).view.setEnabled(true);
|
||||||
|
}
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
nameEdit.setVisibility(View.GONE);
|
nameEdit.setVisibility(View.GONE);
|
||||||
bioEdit.setVisibility(View.GONE);
|
bioEdit.setVisibility(View.GONE);
|
||||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) username.getLayoutParams();
|
||||||
lp.addRule(RelativeLayout.BELOW, R.id.name_wrap);
|
lp.addRule(RelativeLayout.BELOW, R.id.name);
|
||||||
username.getParent().requestLayout();
|
username.getParent().requestLayout();
|
||||||
avatar.setForeground(null);
|
avatar.setForeground(null);
|
||||||
scrollToTop();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
set.start();
|
set.start();
|
||||||
|
|
||||||
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
|
|
||||||
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
|
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,13 +962,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(!isInEditMode)
|
if(!isInEditMode)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
setActionProgressVisible(true);
|
setActionProgressVisible(true);
|
||||||
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, metadataListData)
|
new UpdateAccountCredentials(nameEdit.getText().toString(), bioEdit.getText().toString(), editNewAvatar, editNewCover, aboutFragment.getFields())
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
account=result;
|
account=result;
|
||||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
||||||
if (getActivity() == null) return;
|
|
||||||
exitEditMode();
|
exitEditMode();
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1027,6 +1000,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBackPressed(){
|
public boolean onBackPressed(){
|
||||||
|
if(noteEdit.hasFocus()) {
|
||||||
|
savePrivateNote();
|
||||||
|
}
|
||||||
if(isInEditMode){
|
if(isInEditMode){
|
||||||
exitEditMode();
|
exitEditMode();
|
||||||
return true;
|
return true;
|
||||||
@@ -1125,7 +1101,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=tabViews[viewType];
|
FrameLayout view=tabViews[viewType];
|
||||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
view.setVisibility(View.VISIBLE);
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
@@ -1159,242 +1135,4 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
|
||||||
public void setFields(ArrayList<AccountField> fields){
|
|
||||||
metadataListData=fields;
|
|
||||||
if (isInEditMode) {
|
|
||||||
isInEditMode=false;
|
|
||||||
dragHelper.attachToRecyclerView(null);
|
|
||||||
}
|
|
||||||
if (adapter != null) adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProvideAssistContent(AssistContent assistContent) {
|
|
||||||
callFragmentToProvideAssistContent(getFragmentForPage(pager.getCurrentItem()), assistContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return Uri.parse(account.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MetadataAdapter extends UsableRecyclerView.Adapter<BaseViewHolder> implements ImageLoaderRecyclerAdapter {
|
|
||||||
public MetadataAdapter(){
|
|
||||||
super(imgLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return switch(viewType){
|
|
||||||
case 0 -> new AboutViewHolder();
|
|
||||||
case 1 -> new EditableAboutViewHolder();
|
|
||||||
case 2 -> new AddRowViewHolder();
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+viewType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(BaseViewHolder holder, int position){
|
|
||||||
if(position<metadataListData.size()){
|
|
||||||
holder.bind(metadataListData.get(position));
|
|
||||||
}else{
|
|
||||||
holder.bind(null);
|
|
||||||
}
|
|
||||||
super.onBindViewHolder(holder, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount(){
|
|
||||||
if(isInEditMode){
|
|
||||||
int size=metadataListData.size();
|
|
||||||
if(size<maxFields)
|
|
||||||
size++;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return metadataListData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position){
|
|
||||||
if(isInEditMode){
|
|
||||||
return position==metadataListData.size() ? 2 : 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getImageCountForItem(int position){
|
|
||||||
return isInEditMode || metadataListData.get(position).emojiRequests==null
|
|
||||||
? 0 : metadataListData.get(position).emojiRequests.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageLoaderRequest getImageRequest(int position, int image){
|
|
||||||
return metadataListData.get(position).emojiRequests.get(image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class BaseViewHolder extends BindableViewHolder<AccountField> {
|
|
||||||
public BaseViewHolder(int layout){
|
|
||||||
super(getActivity(), layout, list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
|
|
||||||
private TextView title;
|
|
||||||
private LinkedTextView value;
|
|
||||||
|
|
||||||
public AboutViewHolder(){
|
|
||||||
super(R.layout.item_profile_about);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
value=findViewById(R.id.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item){
|
|
||||||
title.setText(item.parsedName);
|
|
||||||
value.setText(item.parsedValue);
|
|
||||||
if(item.verifiedAt!=null){
|
|
||||||
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
|
|
||||||
value.setTextColor(textColor);
|
|
||||||
value.setLinkTextColor(textColor);
|
|
||||||
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_24_regular, getActivity().getTheme()).mutate();
|
|
||||||
check.setTint(textColor);
|
|
||||||
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
|
|
||||||
}else{
|
|
||||||
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
|
||||||
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
|
|
||||||
value.setCompoundDrawables(null, null, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setImage(int index, Drawable image){
|
|
||||||
CustomEmojiSpan span=index>=item.nameEmojis.length ? item.valueEmojis[index-item.nameEmojis.length] : item.nameEmojis[index];
|
|
||||||
span.setDrawable(image);
|
|
||||||
title.invalidate();
|
|
||||||
value.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearImage(int index){
|
|
||||||
setImage(index, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EditableAboutViewHolder extends BaseViewHolder {
|
|
||||||
private EditText title;
|
|
||||||
private EditText value;
|
|
||||||
|
|
||||||
public EditableAboutViewHolder(){
|
|
||||||
super(R.layout.item_profile_about_editable);
|
|
||||||
title=findViewById(R.id.title);
|
|
||||||
value=findViewById(R.id.value);
|
|
||||||
findViewById(R.id.dragger_thingy).setOnLongClickListener(v->{
|
|
||||||
dragHelper.startDrag(this);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
title.addTextChangedListener(new SimpleTextWatcher(e->item.name=e.toString()));
|
|
||||||
value.addTextChangedListener(new SimpleTextWatcher(e->item.value=e.toString()));
|
|
||||||
findViewById(R.id.remove_row_btn).setOnClickListener(this::onRemoveRowClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item){
|
|
||||||
title.setText(item.name);
|
|
||||||
value.setText(item.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onRemoveRowClick(View v){
|
|
||||||
int pos=getAbsoluteAdapterPosition();
|
|
||||||
metadataListData.remove(pos);
|
|
||||||
adapter.notifyItemRemoved(pos);
|
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
|
||||||
BaseViewHolder vh=(BaseViewHolder) list.getChildViewHolder(list.getChildAt(i));
|
|
||||||
vh.rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AddRowViewHolder extends BaseViewHolder implements UsableRecyclerView.Clickable{
|
|
||||||
public AddRowViewHolder(){
|
|
||||||
super(R.layout.item_profile_about_add_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(){
|
|
||||||
metadataListData.add(new AccountField());
|
|
||||||
if(metadataListData.size()==maxFields){ // replace this row with new row
|
|
||||||
adapter.notifyItemChanged(metadataListData.size()-1);
|
|
||||||
}else{
|
|
||||||
adapter.notifyItemInserted(metadataListData.size()-1);
|
|
||||||
rebind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(AccountField item) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReorderCallback extends ItemTouchHelper.SimpleCallback{
|
|
||||||
public ReorderCallback(){
|
|
||||||
super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target){
|
|
||||||
if(target instanceof AddRowViewHolder)
|
|
||||||
return false;
|
|
||||||
int fromPosition=viewHolder.getAbsoluteAdapterPosition();
|
|
||||||
int toPosition=target.getAbsoluteAdapterPosition();
|
|
||||||
if (fromPosition<toPosition) {
|
|
||||||
for (int i=fromPosition;i<toPosition;i++) {
|
|
||||||
Collections.swap(metadataListData, i, i+1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i=fromPosition;i>toPosition;i--) {
|
|
||||||
Collections.swap(metadataListData, i, i-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.notifyItemMoved(fromPosition, toPosition);
|
|
||||||
((BindableViewHolder)viewHolder).rebind();
|
|
||||||
((BindableViewHolder)target).rebind();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
|
||||||
super.onSelectedChanged(viewHolder, actionState);
|
|
||||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder){
|
|
||||||
super.clearView(recyclerView, viewHolder);
|
|
||||||
viewHolder.itemView.animate().translationZ(0).setDuration(100).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
|
||||||
draggedViewHolder=null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLongPressDragEnabled(){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
@@ -29,11 +28,11 @@ import me.grishka.appkit.api.SimpleCallback;
|
|||||||
|
|
||||||
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
|
private ImageButton fab;
|
||||||
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
||||||
|
|
||||||
@Override
|
public ScheduledStatusListFragment() {
|
||||||
protected boolean wantsComposeButton() {
|
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,31 +55,21 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFabClick(View v) {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onFabLongClick(View v) {
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
fab=view.findViewById(R.id.fab);
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
|
fab.setOnClickListener(v -> Nav.go(getActivity(), ComposeFragment.class, args));
|
||||||
|
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, args));
|
||||||
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
if (getArguments().getBoolean("hide_fab", false)) fab.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
||||||
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, null);
|
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,8 +86,6 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
args.putString("sourceText", status.text);
|
args.putString("sourceText", status.text);
|
||||||
args.putString("sourceSpoiler", status.spoilerText);
|
args.putString("sourceSpoiler", status.spoilerText);
|
||||||
args.putBoolean("redraftStatus", true);
|
args.putBoolean("redraftStatus", true);
|
||||||
args.putString("sourceContentType", scheduledStatus.params.contentType != null ?
|
|
||||||
scheduledStatus.params.contentType.name() : null);
|
|
||||||
setResult(true, null);
|
setResult(true, null);
|
||||||
|
|
||||||
// closing this scheduled status list if another status list is opened from compose fragment
|
// closing this scheduled status list if another status list is opened from compose fragment
|
||||||
@@ -122,7 +109,6 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
if (getActivity() == null) return;
|
|
||||||
onDataLoaded(result, nextMaxID!=null);
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -182,10 +168,4 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
// TODO: adapt when frontends finally implement a scheduled posts list
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,11 @@ import android.content.Intent;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.LruCache;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -22,6 +19,8 @@ import android.view.WindowInsets;
|
|||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.animation.LinearInterpolator;
|
import android.view.animation.LinearInterpolator;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@@ -44,14 +43,11 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||||
import org.joinmastodon.android.api.session.AccountActivationInfo;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
import org.joinmastodon.android.model.ContentType;
|
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
@@ -59,19 +55,17 @@ import org.joinmastodon.android.ui.OutlineProviders;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -80,13 +74,11 @@ import me.grishka.appkit.utils.BindableViewHolder;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class SettingsFragment extends MastodonToolbarFragment implements ProvidesAssistContent.ProvidesWebUri {
|
public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
private UsableRecyclerView list;
|
private UsableRecyclerView list;
|
||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private ThemeItem themeItem;
|
private ThemeItem themeItem;
|
||||||
private NotificationPolicyItem notificationPolicyItem;
|
private NotificationPolicyItem notificationPolicyItem;
|
||||||
private SwitchItem showNewPostsItem, glitchModeItem, compactReblogReplyLineItem;
|
|
||||||
private ButtonItem defaultContentTypeButtonItem;
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean needUpdateNotificationSettings;
|
private boolean needUpdateNotificationSettings;
|
||||||
private boolean needAppRestart;
|
private boolean needAppRestart;
|
||||||
@@ -95,9 +87,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
private ImageView themeTransitionWindowView;
|
private ImageView themeTransitionWindowView;
|
||||||
private TextItem checkForUpdateItem, clearImageCacheItem;
|
private TextItem checkForUpdateItem, clearImageCacheItem;
|
||||||
private ImageCache imageCache;
|
private ImageCache imageCache;
|
||||||
private Menu contentTypeMenu;
|
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -107,7 +97,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
imageCache = ImageCache.getInstance(getActivity());
|
imageCache = ImageCache.getInstance(getActivity());
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
Optional<Instance> instance = session.getInstance();
|
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||||
String instanceName = UiUtils.getInstanceName(accountID);
|
String instanceName = UiUtils.getInstanceName(accountID);
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
@@ -121,6 +111,19 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
items.add(new HeaderItem(R.string.settings_theme));
|
items.add(new HeaderItem(R.string.settings_theme));
|
||||||
items.add(themeItem=new ThemeItem());
|
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.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->{
|
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 popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||||
popupMenu.inflate(R.menu.color_palettes);
|
popupMenu.inflate(R.menu.color_palettes);
|
||||||
@@ -137,20 +140,29 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
case BROWN -> R.string.sk_color_palette_brown;
|
case BROWN -> R.string.sk_color_palette_brown;
|
||||||
case RED -> R.string.sk_color_palette_red;
|
case RED -> R.string.sk_color_palette_red;
|
||||||
case YELLOW -> R.string.sk_color_palette_yellow;
|
case YELLOW -> R.string.sk_color_palette_yellow;
|
||||||
|
case NORD -> R.string.sk_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);
|
updatePublishText(b);
|
||||||
|
if (GlobalUserPreferences.relocatePublishButton) {
|
||||||
b.setOnClickListener(l -> {
|
b.setOnClickListener(l -> {
|
||||||
TextInputFrameLayout input = new TextInputFrameLayout(
|
Toast.makeText(getActivity(), R.string.sk_disable_relocate_publish_button_to_enable_customization,
|
||||||
getContext(),
|
Toast.LENGTH_LONG).show();
|
||||||
getString(R.string.publish),
|
});
|
||||||
GlobalUserPreferences.publishButtonText.trim()
|
} else {
|
||||||
);
|
b.setOnClickListener(l -> {
|
||||||
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_publish_button_text_title).setView(input)
|
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) -> {
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
GlobalUserPreferences.publishButtonText = input.getEditText().getText().toString().trim();
|
GlobalUserPreferences.publishButtonText = input.getText().toString().trim();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
updatePublishText(b);
|
updatePublishText(b);
|
||||||
})
|
})
|
||||||
@@ -159,24 +171,19 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
updatePublishText(b);
|
updatePublishText(b);
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.cancel, (d, which) -> {})
|
.setNegativeButton(R.string.cancel, (d, which) -> {
|
||||||
|
})
|
||||||
.show();
|
.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));
|
items.add(new HeaderItem(R.string.settings_behavior));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
|
||||||
|
GlobalUserPreferences.showFederatedTimeline=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
||||||
GlobalUserPreferences.playGifs=i.checked;
|
GlobalUserPreferences.playGifs=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
@@ -198,128 +205,53 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
|
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
||||||
GlobalUserPreferences.enableDeleteNotifications=i.checked;
|
items.add(new SwitchItem(R.string.sk_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
|
||||||
|
GlobalUserPreferences.disableDividers=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_disable_alt_text_reminder, R.drawable.ic_fluent_image_alt_text_24_regular, GlobalUserPreferences.disableAltTextReminder, i->{
|
// items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_delete_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
|
||||||
GlobalUserPreferences.disableAltTextReminder=i.checked;
|
// GlobalUserPreferences.enableDeleteNotifications=i.checked;
|
||||||
GlobalUserPreferences.save();
|
// GlobalUserPreferences.save();
|
||||||
}));
|
// needAppRestart=true;
|
||||||
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
// }));
|
||||||
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
items.add(new SwitchItem(R.string.sk_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.relocatePublishButton=i.checked;
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_prefix_reply_cw_with_re, R.drawable.ic_fluent_arrow_reply_24_regular, GlobalUserPreferences.prefixRepliesWithRe, i->{
|
|
||||||
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();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
// items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
|
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
|
// GlobalUserPreferences.save();
|
||||||
|
// needAppRestart=true;
|
||||||
|
// }));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_timelines));
|
items.add(new HeaderItem(R.string.home_timeline));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
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.showReplies=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
if (instance.map(Instance::isPleroma).orElse(false)) {
|
|
||||||
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->{
|
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.showBoosts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||||
showNewPostsItem.enabled = i.checked;
|
|
||||||
if (!i.checked) {
|
|
||||||
GlobalUserPreferences.showNewPostsButton = false;
|
|
||||||
showNewPostsItem.checked = false;
|
|
||||||
}
|
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
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();
|
|
||||||
}));
|
|
||||||
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_regular, GlobalUserPreferences.collapseLongPosts, i->{
|
|
||||||
GlobalUserPreferences.collapseLongPosts=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
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_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
|
|
||||||
.map(i -> i.v2 != null && i.v2.configuration.translation != null && i.v2.configuration.translation.enabled)
|
|
||||||
.orElse(false);
|
|
||||||
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(new HeaderItem(R.string.settings_notifications));
|
||||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||||
PushSubscription pushSubscription=getPushSubscription();
|
PushSubscription pushSubscription=getPushSubscription();
|
||||||
boolean switchEnabled=pushSubscription.policy!=PushSubscription.Policy.NONE;
|
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked)));
|
||||||
|
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked)));
|
||||||
items.add(new SwitchItem(R.string.notify_favorites, R.drawable.ic_fluent_star_24_regular, pushSubscription.alerts.favourite, i->onNotificationsChanged(PushNotification.Type.FAVORITE, i.checked), switchEnabled));
|
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked)));
|
||||||
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked), switchEnabled));
|
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
|
||||||
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked), switchEnabled));
|
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_alert_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked)));
|
||||||
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_fluent_mention_24_regular, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked), switchEnabled));
|
items.add(new SwitchItem(R.string.sk_settings_single_notification, R.drawable.ic_fluent_convert_range_24_regular, GlobalUserPreferences.keepOnlyLatestNotification, i->{
|
||||||
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_chat_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked), switchEnabled));
|
GlobalUserPreferences.keepOnlyLatestNotification=i.checked;
|
||||||
items.add(new SwitchItem(R.string.sk_notify_update, R.drawable.ic_fluent_history_24_regular, pushSubscription.alerts.update, i->onNotificationsChanged(PushNotification.Type.UPDATE, i.checked), switchEnabled));
|
GlobalUserPreferences.save();
|
||||||
items.add(new SwitchItem(R.string.sk_notify_poll_results, R.drawable.ic_fluent_poll_24_regular, pushSubscription.alerts.poll, i->onNotificationsChanged(PushNotification.Type.POLL, i.checked), switchEnabled));
|
}));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.settings_account));
|
items.add(new HeaderItem(R.string.settings_account));
|
||||||
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.sk_settings_profile, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/profile"), R.drawable.ic_fluent_open_24_regular));
|
||||||
@@ -328,106 +260,42 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
items.add(new TextItem(R.string.sk_settings_auth, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.sk_settings_auth, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit"), R.drawable.ic_fluent_open_24_regular));
|
||||||
|
|
||||||
items.add(new HeaderItem(instanceName));
|
items.add(new HeaderItem(instanceName));
|
||||||
items.add(new TextItem(R.string.sk_settings_rules, instance.<Runnable>map(i -> () -> {
|
items.add(new TextItem(R.string.sk_settings_rules, ()->{
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putParcelable("instance", Parcels.wrap(i));
|
args.putParcelable("instance", Parcels.wrap(instance));
|
||||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||||
}).orElse(null), R.drawable.ic_fluent_task_list_ltr_24_regular));
|
}, R.drawable.ic_fluent_task_list_ltr_24_regular));
|
||||||
items.add(new TextItem(R.string.sk_settings_about_instance , ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/about"), R.drawable.ic_fluent_info_24_regular));
|
items.add(new TextItem(R.string.sk_settings_about_instance , ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/about"), R.drawable.ic_fluent_info_24_regular));
|
||||||
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
|
||||||
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
|
||||||
items.add(new SmallTextItem(instance
|
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
|
||||||
.map(i -> getString(R.string.sk_settings_server_version, i.version))
|
items.add(new SmallTextItem(getString(translationAvailable ?
|
||||||
.orElse(getString(R.string.sk_instance_info_unavailable))));
|
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_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);
|
|
||||||
instance.ifPresent(i -> ContentType.adaptMenuToInstance(contentTypeMenu, i));
|
|
||||||
}));
|
|
||||||
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) {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
|
|
||||||
if (!instance.map(Instance::isPleroma).orElse(false)) {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GlobalUserPreferences.accountsWithLocalOnlySupport.remove(accountID);
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
|
||||||
}
|
|
||||||
glitchModeItem.checked = GlobalUserPreferences.accountsInGlitchMode.contains(accountID);
|
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(glitchModeItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
items.add(new SmallTextItem(getString(R.string.sk_settings_local_only_explanation)));
|
|
||||||
items.add(glitchModeItem = new SwitchItem(R.string.sk_settings_glitch_instance, 0, GlobalUserPreferences.accountsInGlitchMode.contains(accountID), i->{
|
|
||||||
if (i.checked) {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.add(accountID);
|
|
||||||
} else {
|
|
||||||
GlobalUserPreferences.accountsInGlitchMode.remove(accountID);
|
|
||||||
}
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}));
|
|
||||||
glitchModeItem.enabled = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID);
|
|
||||||
items.add(new SmallTextItem(getString(R.string.sk_settings_glitch_mode_explanation)));
|
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_settings_about));
|
items.add(new HeaderItem(R.string.sk_settings_about));
|
||||||
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_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));
|
// 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();
|
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||||
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), cache != null ? cache.size() : 0, true), this::clearImageCache, 0);
|
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||||
|
items.add(checkForUpdateItem);
|
||||||
|
}
|
||||||
|
items.add(new TextItem(R.string.sk_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.settings_clear_cache, this::clearImageCache));
|
||||||
|
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), imageCache.getDiskCache().size(), true), this::clearImageCache, 0);
|
||||||
items.add(clearImageCacheItem);
|
items.add(clearImageCacheItem);
|
||||||
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
||||||
GlobalUserPreferences.recentLanguages.remove(accountID);
|
GlobalUserPreferences.recentLanguages.remove(accountID);
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
})));
|
})));
|
||||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
items.add(new TextItem(R.string.sk_clear_recent_emoji, ()-> {
|
||||||
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
|
GlobalUserPreferences.recentEmojis.clear();
|
||||||
GlobalUserPreferences.enablePreReleases=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||||
items.add(checkForUpdateItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(BuildConfig.DEBUG){
|
|
||||||
items.add(new RedHeaderItem("Debug options"));
|
|
||||||
items.add(new TextItem("Test e-mail confirmation flow", ()->{
|
|
||||||
AccountSession sess=AccountSessionManager.getInstance().getAccount(accountID);
|
|
||||||
sess.activated=false;
|
|
||||||
sess.activationInfo=new AccountActivationInfo("test@email", System.currentTimeMillis());
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putBoolean("debug", true);
|
|
||||||
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
items.add(new FooterItem(getString(R.string.sk_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)));
|
||||||
}
|
}
|
||||||
@@ -482,7 +350,11 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
||||||
}
|
}
|
||||||
if(needAppRestart) UiUtils.restartApp();
|
if(needAppRestart){
|
||||||
|
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
|
||||||
|
MastodonApp.context.startActivity(intent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -517,6 +389,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
else if (id == R.id.brown_color) pref = ColorPreference.BROWN;
|
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.red_color) pref = ColorPreference.RED;
|
||||||
else if (id == R.id.yellow_color) pref = ColorPreference.YELLOW;
|
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;
|
if (pref == null) return false;
|
||||||
|
|
||||||
@@ -526,6 +399,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void onTrueBlackThemeChanged(SwitchItem item){
|
private void onTrueBlackThemeChanged(SwitchItem item){
|
||||||
GlobalUserPreferences.trueBlackTheme=item.checked;
|
GlobalUserPreferences.trueBlackTheme=item.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
@@ -542,53 +416,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(){
|
private void restartActivityToApplyNewTheme(){
|
||||||
// Calling activity.recreate() causes a black screen for like half a second.
|
// 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.
|
// So, let's take a screenshot and overlay it on top to create the illusion of a smoother transition.
|
||||||
@@ -631,14 +458,18 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
case FAVORITE -> subscription.alerts.favourite=enabled;
|
case FAVORITE -> subscription.alerts.favourite=enabled;
|
||||||
case FOLLOW -> subscription.alerts.follow=enabled;
|
case FOLLOW -> subscription.alerts.follow=enabled;
|
||||||
case REBLOG -> subscription.alerts.reblog=enabled;
|
case REBLOG -> subscription.alerts.reblog=enabled;
|
||||||
case MENTION -> subscription.alerts.mention=enabled;
|
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
||||||
case POLL -> subscription.alerts.poll=enabled;
|
|
||||||
case STATUS -> subscription.alerts.status=enabled;
|
case STATUS -> subscription.alerts.status=enabled;
|
||||||
case UPDATE -> subscription.alerts.update=enabled;
|
|
||||||
}
|
}
|
||||||
needUpdateNotificationSettings=true;
|
needUpdateNotificationSettings=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onNotificationStyleChanged(SwitchItem item){
|
||||||
|
GlobalUserPreferences.uniformNotificationIcon=item.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void onNotificationsPolicyChanged(PushSubscription.Policy policy){
|
private void onNotificationsPolicyChanged(PushSubscription.Policy policy){
|
||||||
PushSubscription subscription=getPushSubscription();
|
PushSubscription subscription=getPushSubscription();
|
||||||
PushSubscription.Policy prevPolicy=subscription.policy;
|
PushSubscription.Policy prevPolicy=subscription.policy;
|
||||||
@@ -653,13 +484,9 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
list.getAdapter().notifyItemChanged(index);
|
list.getAdapter().notifyItemChanged(index);
|
||||||
}
|
}
|
||||||
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
if((prevPolicy==PushSubscription.Policy.NONE)!=(policy==PushSubscription.Policy.NONE)){
|
||||||
boolean newState=policy!=PushSubscription.Policy.NONE;
|
|
||||||
for(PushNotification.Type value : PushNotification.Type.values()){
|
|
||||||
onNotificationsChanged(value, newState);
|
|
||||||
}
|
|
||||||
index++;
|
index++;
|
||||||
while(items.get(index) instanceof SwitchItem si){
|
while(items.get(index) instanceof SwitchItem si){
|
||||||
si.enabled=si.checked=newState;
|
si.enabled=si.checked=policy!=PushSubscription.Policy.NONE;
|
||||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
((BindableViewHolder<?>)holder).rebind();
|
((BindableViewHolder<?>)holder).rebind();
|
||||||
@@ -699,7 +526,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onLoggedOut(){
|
private void onLoggedOut(){
|
||||||
if (getActivity() == null) return;
|
|
||||||
AccountSessionManager.getInstance().removeAccount(accountID);
|
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
@@ -742,16 +568,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return isInstanceAkkoma() ? null : base.path("/settings").build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccountID() {
|
|
||||||
return accountID;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static abstract class Item{
|
private static abstract class Item{
|
||||||
public abstract int getViewType();
|
public abstract int getViewType();
|
||||||
}
|
}
|
||||||
@@ -787,7 +603,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
this.onChanged=onChanged;
|
this.onChanged=onChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchItem(@StringRes int text, @DrawableRes int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
public SwitchItem(@StringRes int text, int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
|
||||||
this.text=getString(text);
|
this.text=getString(text);
|
||||||
this.icon=icon;
|
this.icon=icon;
|
||||||
this.checked=checked;
|
this.checked=checked;
|
||||||
@@ -818,6 +634,13 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ColorPicker extends Item{
|
||||||
|
@Override
|
||||||
|
public int getViewType(){
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ThemeItem extends Item{
|
private static class ThemeItem extends Item{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -874,11 +697,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
this.secondaryText = secondaryText;
|
this.secondaryText = secondaryText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextItem(String text, Runnable onClick){
|
|
||||||
this.text=text;
|
|
||||||
this.onClick=onClick;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 4;
|
return 4;
|
||||||
@@ -891,10 +709,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
super(text);
|
super(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedHeaderItem(String text){
|
|
||||||
super(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 5;
|
return 5;
|
||||||
@@ -988,12 +802,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(SwitchItem item){
|
public void onBind(SwitchItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
if (item.icon == 0) {
|
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
icon.setVisibility(View.VISIBLE);
|
|
||||||
icon.setImageResource(item.icon);
|
icon.setImageResource(item.icon);
|
||||||
}
|
|
||||||
checkbox.setChecked(item.checked && item.enabled);
|
checkbox.setChecked(item.checked && item.enabled);
|
||||||
checkbox.setEnabled(item.enabled);
|
checkbox.setEnabled(item.enabled);
|
||||||
}
|
}
|
||||||
@@ -1071,6 +880,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ButtonViewHolder extends BindableViewHolder<ButtonItem>{
|
private class ButtonViewHolder extends BindableViewHolder<ButtonItem>{
|
||||||
private final Button button;
|
private final Button button;
|
||||||
private final ImageView icon;
|
private final ImageView icon;
|
||||||
@@ -1087,11 +897,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(ButtonItem item){
|
public void onBind(ButtonItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
if (item.icon == 0) {
|
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
icon.setImageResource(item.icon);
|
icon.setImageResource(item.icon);
|
||||||
}
|
|
||||||
item.buttonConsumer.accept(button);
|
item.buttonConsumer.accept(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1171,19 +977,21 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
|
|
||||||
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
|
;
|
||||||
|
|
||||||
public SmallTextViewHolder(){
|
public SmallTextViewHolder(){
|
||||||
super(getActivity(), R.layout.item_settings_text, list);
|
super(getActivity(), R.layout.item_settings_text, list);
|
||||||
text = itemView.findViewById(R.id.text);
|
text = itemView.findViewById(R.id.text);
|
||||||
text.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary));
|
|
||||||
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
|
||||||
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
||||||
text.setPaddingRelative(text.getPaddingStart(), 0, text.getPaddingEnd(), text.getPaddingBottom());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(SmallTextItem item){
|
public void onBind(SmallTextItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
|
TypedValue val = new TypedValue();
|
||||||
|
getContext().getTheme().resolveAttribute(android.R.attr.textColorSecondary, val, true);
|
||||||
|
text.setTextColor(getResources().getColor(val.resourceId, getContext().getTheme()));
|
||||||
|
text.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
|
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1258,6 +1066,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide
|
|||||||
progress.removeCallbacks(progressUpdater);
|
progress.removeCallbacks(progressUpdater);
|
||||||
}
|
}
|
||||||
changelog.setText(info.changelog);
|
changelog.setText(info.changelog);
|
||||||
|
// changelog.setText(getString(R.string.sk_changelog, info.changelog));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateProgress(){
|
private void updateProgress(){
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user