Compare commits

..

789 Commits

Author SHA1 Message Date
LucasGGamerM
b814c7eab9 91 changelog 2023-02-02 16:08:56 -03:00
LucasGGamerM
ecfa255d75 Bump version number 2023-02-02 16:07:27 -03:00
LucasGGamerM
979977aec6 Removing som unused functions 2023-02-02 16:07:15 -03:00
LucasGGamerM
a487a45828 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (23 of 23 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pt_BR/
(cherry picked from commit 43c82ed99b)
2023-02-02 16:05:38 -03:00
Andrewblasco
0afcf10c74 Translated using Weblate (Spanish)
Currently translated at 100.0% (16 of 16 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/es/
(cherry picked from commit 28302f3a4b)
2023-02-02 16:05:38 -03:00
LucasGGamerM
d6ad1cbfbf Maybe adding filter contexts. Needs testing 2023-02-02 15:51:00 -03:00
LucasGGamerM
fd08a1f845 The auto updater is not brokey anymore :) 2023-02-02 15:40:32 -03:00
LucasGGamerM
67b2a82058 Maybe works? 2023-02-02 15:36:14 -03:00
LucasGGamerM
f7ab8bd27e Pulling better button from megalodon, thanks @sk22! 2023-02-02 15:20:34 -03:00
LucasGGamerM
5df8318200 Unbreak weird switch items thing 2023-02-02 15:13:20 -03:00
LucasGGamerM
0fcaa08ed0 Readding the pre-releases toggle, not in the self updater yet though 2023-02-02 15:11:25 -03:00
LucasGGamerM
2ce3167db0 Revert "implement pre-release toggle"
This reverts commit 430dd616
2023-02-02 15:05:18 -03:00
LucasGGamerM
2c8ecbd996 Reverting broken images 2023-02-02 15:01:39 -03:00
LucasGGamerM
90aa8c5762 Clicking on filtered items will show them like expected (It previously showed another warning when you clicked it) 2023-02-02 13:35:44 -03:00
LucasGGamerM
bb0ca63a03 Revert "Its now a much cleaner transition"
This reverts commit 562a5aae7d.
2023-02-02 13:18:14 -03:00
LucasGGamerM
e8a8691b03 Adding 90 changelog 2023-02-01 22:02:29 -03:00
LucasGGamerM
7fe3d97347 Bump version number 2023-02-01 22:01:25 -03:00
poesty
a2e932934c Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (16 of 16 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/zh_Hans/
(cherry picked from commit c34b08ffb20b1e58307b13775165728a57aaa2f8)
2023-02-01 22:00:13 -03:00
Oliebol
e82e51ca88 Translated using Weblate (Dutch)
Currently translated at 12.5% (2 of 16 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/nl/
(cherry picked from commit 10c50b8a78aba6f275e3ce01260f585c27535e46)
2023-02-01 22:00:12 -03:00
tygyh
ef561b6724 Translated using Weblate (Swedish)
Currently translated at 100.0% (16 of 16 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/sv/
(cherry picked from commit 467bde77b94623a9aff2bf1e0a5f735819f65edb)
2023-02-01 22:00:12 -03:00
poesty
2bdff65c13 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/zh_Hans/
(cherry picked from commit 1937864d90f67d1a1df40f15e1b6e900aa55fa8a)
2023-02-01 22:00:12 -03:00
LucasGGamerM
286b642101 Merge branch 'feature/filters_again' 2023-02-01 21:46:50 -03:00
LucasGGamerM
27ed78c293 Merge branch 'feature/filters' 2023-02-01 21:44:35 -03:00
LucasGGamerM
562a5aae7d Its now a much cleaner transition 2023-02-01 21:42:47 -03:00
LucasGGamerM
4940eff7f9 Its pretty much done i think 2023-02-01 21:39:54 -03:00
LucasGGamerM
cfb9854a8e Adding a little bit of polish, and also adding a title to the filters 2023-02-01 21:16:47 -03:00
LucasGGamerM
594e49cf64 Fixing a weird bug I found 2023-02-01 20:21:19 -03:00
LucasGGamerM
ad2895e297 Doing a bit of cleanup. Man, I still dont believe what i have done 2023-01-31 20:36:01 -03:00
LucasGGamerM
dae2632c18 IT WORKS, now i need to polish it :) 2023-01-31 20:30:05 -03:00
LucasGGamerM
97f09d4569 Adding missing drawables? 2023-01-30 19:13:20 -03:00
LucasGGamerM
ac1dbc0f90 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	mastodon/src/main/res/values-de-rDE/strings_mo.xml
#	metadata/es-ES/changelogs/80.txt
#	metadata/es-ES/changelogs/81.txt
#	metadata/es-ES/full_description.txt
2023-01-30 18:55:16 -03:00
LucasGGamerM
cf67175826 Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.4% (21 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pt_BR/
2023-01-30 21:51:47 +00:00
MKCOOL142
dc8f81f447 Translated using Weblate (German)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/de/
2023-01-30 21:51:46 +00:00
MKCOOL142
b11b4c95d0 Translated using Weblate (German)
Currently translated at 100.0% (16 of 16 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/de/
2023-01-30 21:51:46 +00:00
Espasant3
20dc664242 Translated using Weblate (Galician)
Currently translated at 40.0% (6 of 15 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/gl/
2023-01-30 19:04:45 +00:00
ewm
5cabab368f Translated using Weblate (Polish)
Currently translated at 13.3% (2 of 15 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/pl/
2023-01-30 19:04:45 +00:00
Oliebol
e512a0b327 Translated using Weblate (Dutch)
Currently translated at 6.6% (1 of 15 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/nl/
2023-01-30 19:04:45 +00:00
Espasant3
4a96392f11 Translated using Weblate (Spanish)
Currently translated at 86.6% (13 of 15 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/es/
2023-01-30 19:04:45 +00:00
ewm
74a28cb881 Translated using Weblate (Polish)
Currently translated at 90.9% (20 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pl/
2023-01-30 19:04:45 +00:00
dontobi
cd712f5109 Translated using Weblate (German)
Currently translated at 100.0% (15 of 15 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/de/
2023-01-30 19:04:45 +00:00
Oliebol
11d5a65f04 Translated using Weblate (Dutch)
Currently translated at 81.8% (18 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/nl/
2023-01-30 19:04:45 +00:00
ghose
efc1a0b4e3 Translated using Weblate (Galician)
Currently translated at 15.3% (2 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/gl/
2023-01-30 19:04:45 +00:00
ghose
492d9e90b8 Translated using Weblate (Galician)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/gl/
2023-01-30 19:04:45 +00:00
LucasGGamerM
0928b5808f Adding 89 changelog 2023-01-30 16:03:44 -03:00
LucasGGamerM
99c2555a88 Bumping version number 2023-01-30 15:59:03 -03:00
LucasGGamerM
ede4137935 It now hides everything! It still doesnt pop up though :D 2023-01-29 18:46:39 -03:00
LucasGGamerM
b625ed7aec Now it acctually shows a missing posts warning below every filtered post. Still a long way to go 2023-01-29 18:24:34 -03:00
LucasGGamerM
03722868b1 Fixing a crash again 2023-01-29 14:19:38 -03:00
LucasGGamerM
a2a0c9801b Fixing #61 2023-01-29 10:40:39 -03:00
LucasGGamerM
9a55f847b9 Its once again almost working 2023-01-28 20:19:41 -03:00
LucasGGamerM
0473062bc2 Its almost working! 2023-01-28 19:30:21 -03:00
LucasGGamerM
9393e845b7 Removing megalodon things i accidentally merged 2023-01-28 13:56:50 -03:00
LucasGGamerM
3dc56286cc 88 changelog 2023-01-27 21:43:51 -03:00
LucasGGamerM
e22ea06d28 Bump version number 2023-01-27 21:40:42 -03:00
LucasGGamerM
6fd5b77e51 Forgot to add some stuff 2023-01-27 21:33:08 -03:00
LucasGGamerM
372de91be9 Updating the readme because its too old 2023-01-27 21:31:18 -03:00
LucasGGamerM
ce3f8ce1b5 Fixing thy icons 2023-01-27 16:08:46 -03:00
LucasGGamerM
7c01809eb3 Changing notif colors back to normal 2023-01-27 16:02:24 -03:00
LucasGGamerM
29cc73e84e Fixing npe in compose fragment, and also rearranging settings items 2023-01-27 16:01:17 -03:00
sk22
c3567bcbff It finally compiles! Though I need to fix some stuff before releasing 2023-01-27 15:51:00 -03:00
sk22
bd4ade0852 Translated using Weblate (German)
Currently translated at 100.0% (251 of 251 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 15:40:48 -03:00
sk
62649ffe60 move some settings around 2023-01-27 15:40:41 -03:00
Oliebol
b0a405337a Translated using Weblate (Dutch)
Currently translated at 93.9% (234 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-01-27 15:40:19 -03:00
gallegonovato
18b6e33862 Translated using Weblate (Spanish)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-27 15:40:18 -03:00
gallegonovato
acb5b01cbd Translated using Weblate (Spanish)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2023-01-27 15:40:18 -03:00
gallegonovato
754c41389e Translated using Weblate (Spanish)
Currently translated at 95.5% (238 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-27 15:40:18 -03:00
McKris
d26ba6df4a Translated using Weblate (Polish)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/
2023-01-27 15:40:18 -03:00
ihor_ck
cc9b97338d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2023-01-27 15:40:18 -03:00
tygyh
f714a00fc4 Translated using Weblate (Swedish)
Currently translated at 42.8% (6 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/sv/
2023-01-27 15:40:18 -03:00
Choukajohn
7097b5371e Translated using Weblate (French)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2023-01-27 15:40:10 -03:00
ihor_ck
ad6ebf9e23 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 15:40:10 -03:00
tygyh
5436a45c5f Translated using Weblate (Swedish)
Currently translated at 16.4% (41 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sv/
2023-01-27 15:40:10 -03:00
McKris
4ba3baf354 Translated using Weblate (Polish)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-27 15:40:10 -03:00
rex07
9160a5b0e2 Translated using Weblate (Arabic)
Currently translated at 2.4% (6 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ar/
2023-01-27 15:40:10 -03:00
rex07
fdac91363e Added translation using Weblate (Arabic) 2023-01-27 15:40:10 -03:00
Linerly
70a9c91f8a Translated using Weblate (Indonesian)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2023-01-27 15:40:10 -03:00
Linerly
45ca3d1b48 Translated using Weblate (Indonesian)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 15:40:09 -03:00
Espasant3
9506d55149 Translated using Weblate (Galician)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/gl/
2023-01-27 15:40:09 -03:00
Espasant3
16808b404b Translated using Weblate (Galician)
Currently translated at 98.3% (245 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-27 15:40:09 -03:00
sk
97a4cade1f separate notification toggle for polls 2023-01-27 15:40:01 -03:00
sk
55f3d80f3a unsubscribe from notifications when policy is none
re: mastodon#520
2023-01-27 15:39:36 -03:00
sk
10d66b732c set disabled state if policy is none
re: mastodon#520
2023-01-27 15:39:31 -03:00
sk
e62c70268a add server version to settings
closes sk22#376
2023-01-27 15:38:33 -03:00
sk
cbb10ac480 use regular lock in boost menu
closes sk22#375
2023-01-27 15:38:27 -03:00
Choukajohn
33de46130c Translated using Weblate (French)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 15:38:18 -03:00
sk22
d1bd3a423c Translated using Weblate (German)
Currently translated at 92.8% (13 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2023-01-27 15:38:18 -03:00
sk22
2e24e443c5 Translated using Weblate (German)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 15:38:18 -03:00
sk22
040b613d68 Translated using Weblate (English)
Currently translated at 100.0% (14 of 14 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/en/
2023-01-27 15:38:18 -03:00
ihor_ck
6659ca5f60 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 15:37:56 -03:00
edxkl
adaa09790a Translated using Weblate (Portuguese (Brazil))
Currently translated at 90.7% (226 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-27 15:37:56 -03:00
McKris
699bcc0377 Translated using Weblate (Polish)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-27 15:37:56 -03:00
Linerly
b359a2bfa1 Translated using Weblate (Indonesian)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 15:37:56 -03:00
Choukajohn
f821709afc Translated using Weblate (French)
Currently translated at 100.0% (249 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 15:37:56 -03:00
irure
a9899e8f9a Translated using Weblate (Basque)
Currently translated at 93.5% (233 of 249 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-27 15:37:56 -03:00
sk
e1b1ae717f adapt upstream changes 2023-01-27 15:37:51 -03:00
sk
0fd2894a8b update comment 2023-01-27 15:37:48 -03:00
sk
05ddd2f8d9 change local-only string 2023-01-27 15:37:43 -03:00
sk
6d9639b042 fix visibility radio button 2023-01-27 15:37:40 -03:00
sk
f3ee87a815 change missing icons 2023-01-27 15:37:36 -03:00
sk
59c5ac0dd1 enable boosting local posts 2023-01-27 15:37:34 -03:00
sk
e647b37143 replace visibility icons 2023-01-27 15:37:31 -03:00
sk
d94c3e3d9b change list icon 2023-01-27 15:37:25 -03:00
Grishka
9b508bd9ca More onboarding updates 2023-01-27 15:36:27 -03:00
sk
fc49c20fff hopefully prevent some crashes 2023-01-27 15:35:55 -03:00
sk
7cc791ccd9 dividers and alignments
nobody knows the trouble i've seen
2023-01-27 15:35:01 -03:00
HudobniVolk
b527ab6e84 Translated using Weblate (Slovenian)
Currently translated at 30.7% (4 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/sl/
2023-01-27 15:34:51 -03:00
HudobniVolk
a5fb358998 Translated using Weblate (Slovenian)
Currently translated at 80.4% (189 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-01-27 15:34:51 -03:00
edxkl
daa0497ce8 Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.7% (225 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-27 15:34:51 -03:00
ghose
2d3f852fd6 Translated using Weblate (Galician)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/gl/
2023-01-27 15:34:44 -03:00
ihor_ck
cf288c9ba9 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 15:34:44 -03:00
HudobniVolk
4847eff116 Translated using Weblate (Slovenian)
Currently translated at 79.5% (187 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-01-27 15:34:44 -03:00
edxkl
36430c6557 Translated using Weblate (Portuguese (Brazil))
Currently translated at 86.3% (203 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-27 15:34:44 -03:00
gicorada
3fb8470899 Translated using Weblate (Italian)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-01-27 15:34:44 -03:00
irure
e1f7feb875 Translated using Weblate (Basque)
Currently translated at 99.1% (233 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-27 15:34:44 -03:00
gallegonovato
0ec005d595 Translated using Weblate (Spanish)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-27 15:34:44 -03:00
AiOO
b2bed8e6b5 Translated using Weblate (Korean)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-27 15:34:44 -03:00
LucasGGamerM
30c6a56f25 Added translation using Weblate (Portuguese) 2023-01-27 15:34:27 -03:00
Espasant3
2ba575ecb8 Translated using Weblate (Galician)
Currently translated at 92.3% (12 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/gl/
2023-01-27 15:34:19 -03:00
McKris
d67cfe1d28 Translated using Weblate (Polish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/
2023-01-27 15:34:19 -03:00
edxkl
cab36e8f79 Translated using Weblate (Portuguese (Brazil))
Currently translated at 81.7% (192 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-27 15:34:19 -03:00
McKris
d30eb0bd36 Translated using Weblate (Polish)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-27 15:34:19 -03:00
Oliebol
31443d59ca Translated using Weblate (Dutch)
Currently translated at 98.2% (231 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-01-27 15:34:19 -03:00
gicorada
79ccc4b14a Translated using Weblate (Italian)
Currently translated at 78.2% (184 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-01-27 15:34:19 -03:00
Linerly
6ce1dec467 Translated using Weblate (Indonesian)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 15:34:19 -03:00
ghose
81cac94fac Translated using Weblate (Galician)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-27 15:34:19 -03:00
Choukajohn
4301c82856 Translated using Weblate (French)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 15:34:19 -03:00
gallegonovato
2856a5e6d4 Translated using Weblate (Spanish)
Currently translated at 98.2% (231 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-27 15:34:19 -03:00
Espasant3
a06414e3a1 Translated using Weblate (Spanish)
Currently translated at 98.2% (231 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-27 15:34:18 -03:00
ling0412
747edce53c Translated using Weblate (Chinese (Simplified))
Currently translated at 99.1% (233 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-27 15:34:14 -03:00
sk22
c407b5d504 Translated using Weblate (German)
Currently translated at 100.0% (235 of 235 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 15:34:14 -03:00
edxkl
997c277beb Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2023-01-27 15:34:14 -03:00
ihor_ck
2e392f3bad Translated using Weblate (Ukrainian)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 15:34:14 -03:00
HudobniVolk
db133ad8d2 Translated using Weblate (Slovenian)
Currently translated at 35.6% (81 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-01-27 15:34:14 -03:00
edxkl
015479e6e0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 82.3% (187 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-27 15:34:13 -03:00
Linerly
8b26103049 Translated using Weblate (Indonesian)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 15:34:07 -03:00
Choukajohn
8d095e7901 Translated using Weblate (French)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 15:34:07 -03:00
ling0412
945b57681e Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-27 15:34:07 -03:00
Oliebol
1dcaa51f2c Translated using Weblate (Dutch)
Currently translated at 15.3% (2 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/nl/
2023-01-27 15:34:06 -03:00
sheepnik
c7b377829e Translated using Weblate (Welsh)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/cy/
2023-01-27 15:34:05 -03:00
sheepnik
63ffd53b09 Translated using Weblate (Welsh)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cy/
2023-01-27 15:34:05 -03:00
McKris
631f5f8691 Translated using Weblate (Polish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/
2023-01-27 15:34:05 -03:00
ling0412
89976d1894 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2023-01-27 15:34:05 -03:00
ihor_ck
ff7305da8b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 15:34:05 -03:00
HudobniVolk
6593c43c1c Translated using Weblate (Slovenian)
Currently translated at 23.7% (54 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-01-27 15:34:04 -03:00
McKris
cf36c6ca83 Translated using Weblate (Polish)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-27 15:34:04 -03:00
Oliebol
92fb0272f0 Translated using Weblate (Dutch)
Currently translated at 93.3% (212 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-01-27 15:34:04 -03:00
Linerly
aad7b5d703 Translated using Weblate (Indonesian)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 15:33:59 -03:00
ghose
eb5f82af2a Translated using Weblate (Galician)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-27 15:33:59 -03:00
Choukajohn
cf8cfd8093 Translated using Weblate (French)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 15:33:59 -03:00
ling0412
2538c8b689 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-27 15:33:58 -03:00
AiOO
36d78abd1e Translated using Weblate (Korean)
Currently translated at 66.9% (152 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-27 15:33:53 -03:00
sk22
072e78a89a Translated using Weblate (German)
Currently translated at 100.0% (227 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 15:33:53 -03:00
sk
58dee464e5 support admin notifications 2023-01-27 15:33:40 -03:00
sk
21f816860f improve local visibility compatibility 2023-01-27 15:33:10 -03:00
sk
84e5983c4c fix null pointer exception 2023-01-27 15:33:07 -03:00
sk
1a6ad6c029 re-arrange settings 2023-01-27 15:32:10 -03:00
sk
f8ecbb8936 tweak no alt indicator 2023-01-27 15:31:07 -03:00
sk
1c98bba7d2 update string 2023-01-27 15:29:52 -03:00
LucasGGamerM
b94e927439 support akkoma local visibility / but it still doesnt compile 2023-01-27 15:29:44 -03:00
sk
eb0fcda3cc support akkoma local visibility 2023-01-27 15:28:17 -03:00
sk
d7feb78197 fix non-local-only posting (which i broke) 2023-01-27 15:24:56 -03:00
Grishka
acf1988827 Update onboarding 2023-01-27 15:24:48 -03:00
LucasGGamerM
3822263b1c Making it compile again 2023-01-27 15:24:17 -03:00
sk
1af82fde61 implement local-only posting 2023-01-27 15:20:47 -03:00
sk
6062510844 use notification icon 2023-01-27 15:19:24 -03:00
sk
27c34461bf add indicator for direct and local-only posts 2023-01-27 15:19:19 -03:00
sk
abf801742f don't apply title for hashtags/lists
closes sk22#343
2023-01-27 15:18:58 -03:00
sk
186604931d no hashtag header in list timeline
closes sk22#366
2023-01-27 15:18:56 -03:00
sk
a726bf6101 add option to disable show new posts button 2023-01-27 15:18:52 -03:00
sk
0008b07072 tweak no alt indicator background 2023-01-27 15:18:13 -03:00
sk
527267b315 Revert "bigger hitbox for alt indicator"
This reverts commit 1b04440546.

this commit just wasn't working properly. animation would have a
frame of stuttering and i don't know where that came from or how i
could fix that. also, the code was a mess anyway
2023-01-27 15:18:07 -03:00
sk
396836760a fix loading default visibility 2023-01-27 15:18:03 -03:00
sk
a5a293be78 add null check 2023-01-27 15:17:58 -03:00
LucasGGamerM
ea8107d33e implement pre-release toggle 2023-01-27 15:17:18 -03:00
sk
430dd6164c implement pre-release toggle 2023-01-27 15:13:38 -03:00
sk
7588143ec6 fix navigation bumpiness
closes sk22#347
2023-01-27 15:10:56 -03:00
sk
1f97fa9447 don't override visibility when replying to self
closes sk22#348
2023-01-27 15:10:53 -03:00
sk
fff7949e51 bigger hitbox for alt indicator
closes sk22#353
2023-01-27 15:10:47 -03:00
LucasGGamerM
ce6b928964 Fixing the settings page again 2023-01-27 15:10:42 -03:00
sk
0604326f3f add indicator for missing alt texts
closes sk22#355
2023-01-27 15:06:52 -03:00
sk
338de2699f hide scheduling options when editing
closes sk22#364
2023-01-27 15:05:17 -03:00
sk
0b0fe98e2d fix current language getting overwritten 2023-01-27 15:05:13 -03:00
LucasGGamerM
84d412645f decrease margin between items in the compose bottom bar 2023-01-27 15:05:09 -03:00
LucasGGamerM
c565f6779d Changing stuff from the alt text reminder 2023-01-27 14:59:32 -03:00
sk
1733b1b065 prompt when saving edited draft
closes sk22#319
2023-01-27 14:53:25 -03:00
sk
49cff20785 bigger hitbox for items in compose toolbar 2023-01-27 14:51:36 -03:00
sk
65232b3b1e change add media icon
closes sk22#351
2023-01-27 14:50:39 -03:00
sk
ac6177f3de display header for followed hashtags
closes sk22#323
2023-01-27 14:50:34 -03:00
sk
48e6a6f624 update hashtags/lists in home
closes sk22#312
2023-01-27 14:50:32 -03:00
sk
73db5907fe display header for followed hashtags
closes sk22#323
2023-01-27 14:50:18 -03:00
sk
69114b9f57 update hashtags/lists in home
closes sk22#312
2023-01-27 14:49:53 -03:00
sk
114283846c enable scrolling to top via toolbar 2023-01-27 14:49:50 -03:00
sk
e0dd2e3a08 improve search empty text 2023-01-27 14:49:45 -03:00
sk
648c3a0c0d fix crash when recycler view is null 2023-01-27 14:49:14 -03:00
sk
dd3bf1d1e0 improve header icons
* align more button to action overflow button
* use different background to better reflect hitbox
2023-01-27 14:49:01 -03:00
sk
5280ba1930 use fluent more icon, correct padding
closes sk22#350
2023-01-27 14:48:58 -03:00
sk
5c8c888024 fix akkoma crash on list edit
closes sk22#352
2023-01-27 14:48:54 -03:00
sk
b9a997730a move edit timelines option 2023-01-27 14:48:18 -03:00
sk
446a2f8206 clean up code 2023-01-27 14:48:15 -03:00
sk
ea4c7dc51d probably fix options menu issue
closes sk22#360
2023-01-27 14:48:12 -03:00
sk
16c0ff2f0b fix wrong status bar color
closes sk22#363
2023-01-27 14:48:09 -03:00
sk
8941c2bc1d fix double-click icon button 2023-01-27 14:48:03 -03:00
sk22
aa4a1648af Update blocks.tsv 2023-01-27 14:48:01 -03:00
sk22
5e27a43471 Adding a 12 hour option for polls (#346)
Co-authored-by: Pleclown <pleclown+github@gmail.com>

closes #346
2023-01-27 14:47:57 -03:00
sk22
12a51eb51f Translated using Weblate (German)
Currently translated at 99.1% (225 of 227 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 14:47:54 -03:00
sk
5fd17778f4 remove add media string 2023-01-27 14:47:50 -03:00
sk
a6028e06ea add a few icons 2023-01-27 14:47:45 -03:00
ihor_ck
8a0f95affe Translated using Weblate (Ukrainian)
Currently translated at 100.0% (174 of 174 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-27 14:47:23 -03:00
Linerly
1221a5622f Translated using Weblate (Indonesian)
Currently translated at 100.0% (174 of 174 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-27 14:47:18 -03:00
Choukajohn
b776b0bc1b Translated using Weblate (French)
Currently translated at 100.0% (174 of 174 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-27 14:47:13 -03:00
florian-obernberger
09bf0f2c3c Translated using Weblate (German)
Currently translated at 91.9% (160 of 174 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-27 14:47:08 -03:00
sheepnik
42dc6b7425 Translated using Weblate (Welsh)
Currently translated at 15.3% (2 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/cy/
2023-01-27 14:47:04 -03:00
sheepnik
1b31284a95 Translated using Weblate (Welsh)
Currently translated at 100.0% (149 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cy/
2023-01-27 14:47:04 -03:00
gicorada
672ed86630 Translated using Weblate (Italian)
Currently translated at 100.0% (149 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-01-27 14:46:59 -03:00
sk
862a23af5d add a few icons 2023-01-27 14:46:53 -03:00
sk
d49733695d add back file opener
closes sk22#328
2023-01-27 14:46:49 -03:00
sk
d79c4ad650 rearrange home menus 2023-01-27 14:46:16 -03:00
Grishka
6e314f0ea5 Fix #509 2023-01-27 14:44:46 -03:00
Grishka
e5a6dc33b4 Fix #510 2023-01-27 14:44:43 -03:00
sk
8c6ac9e22b Revert "work around crash onHidden"
This reverts commit 763c5fe2a7.
2023-01-27 14:43:23 -03:00
Grishka
8d827054bf Fix #512 2023-01-27 14:42:57 -03:00
sk
55a2ae8748 fix wrong "hasSpoiler" value on restore
closes sk22#324
2023-01-27 14:42:37 -03:00
sk
55640dddb9 fix typo in string 2023-01-27 14:42:33 -03:00
sk
9a2df814c5 scroll image in alt text editor
closes sk22#315
2023-01-27 14:42:30 -03:00
sk
0fcbf02a2c notifications for edited posts
closes sk22#331
2023-01-27 14:41:54 -03:00
sk
bc9edf1f69 fix redrafting empty posts
closes sk22#325
2023-01-27 14:37:46 -03:00
sk
50baffefbe add lists to status header 2023-01-27 14:37:41 -03:00
sk
3622eba057 remove pivot for timeline title 2023-01-27 14:37:32 -03:00
sk
9df738831f work around crash onHidden
re: mastodon#512
2023-01-27 14:37:28 -03:00
sk
7939380172 different icon for post notifs 2023-01-27 14:34:04 -03:00
sk
b36b1b2a28 tweak alt button 2023-01-27 14:32:54 -03:00
sk22
b63c0010a9 Pinnable timelines (#338)
* implement draggable list

* implement pinning timelines

* fix TimelineDefinition equals not working

* implement removing timelines

* implement pinned lists/hashtags

* per-account pinned timelines

* implement pin button

* fix issues with pinning

* improve pin button

* improve pinning timelines

* implement custom icons

* fix home switcher menu

* make hashtags pinnable

* edit timelines in options menu
2023-01-27 14:32:04 -03:00
Espasant3
901f1763ab Translated using Weblate (Galician)
Currently translated at 61.5% (8 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/gl/
2023-01-27 14:29:12 -03:00
McKris
cf22096e35 Translated using Weblate (Polish)
Currently translated at 96.6% (144 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-27 14:29:12 -03:00
ghose
c96e5cde7b Translated using Weblate (Galician)
Currently translated at 100.0% (149 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-27 14:29:12 -03:00
Jippang
5538d5af6c Translated using Weblate (Korean)
Currently translated at 100.0% (149 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-27 14:29:07 -03:00
Grishka
662fbbf8b6 Fix #69 (nice) 2023-01-27 14:28:17 -03:00
LucasGGamerM
247dea3af0 Chaging string in title 2023-01-27 14:24:44 -03:00
LucasGGamerM
efff075bbc Fixing weird fab behavior on quickly jittering the main screen 2023-01-27 14:23:09 -03:00
LucasGGamerM
45832355a3 Refactoring the enableHideFab option, and increasing the threshold for the fab popping up from 400 to 800 2023-01-26 20:15:50 -03:00
FineFindus
51d4fd63db refactor(compose-fab): code cleanup 2023-01-26 20:11:08 -03:00
FineFindus
ececa7aa2f refactor(compose-fab): show fab after small scroll distance 2023-01-26 20:11:05 -03:00
tygyh
6cf8793efe Translated using Weblate (Swedish)
Currently translated at 38.4% (5 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/sv/
2023-01-26 19:53:02 -03:00
HudobniVolk
57251d58cb Translated using Weblate (Slovenian)
Currently translated at 46.1% (6 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/sl/
2023-01-26 19:53:02 -03:00
LamaEpik
cf1f8e8d1a Translated using Weblate (French)
Currently translated at 15.3% (2 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/fr/
2023-01-26 19:53:02 -03:00
Andrewblasco
c77cb14602 Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/es/
2023-01-26 19:53:02 -03:00
tygyh
de864edb33 Translated using Weblate (Swedish)
Currently translated at 31.8% (7 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/sv/
2023-01-26 19:53:02 -03:00
Andrewblasco
699925ac9b Translated using Weblate (Spanish)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/es/
2023-01-26 19:53:02 -03:00
Kevin
e367b7711f Translated using Weblate (German)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/de/
2023-01-26 19:53:02 -03:00
Kevin
b0e4f707aa Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/de/
2023-01-26 19:53:01 -03:00
LucasGGamerM
2749ffb6f2 Reverting the other irrelevant thing. 2023-01-26 16:23:54 -03:00
LucasGGamerM
47f57bab17 Testing the hooks for weblate 2023-01-26 14:50:52 -03:00
sk
f1b0f828ac Fixing up name of the setting, and making it disabled by default 2023-01-26 14:42:10 -03:00
tygyh
afbf13ef95 Translated using Weblate (Swedish)
Currently translated at 38.4% (5 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/sv/
2023-01-26 17:12:42 +00:00
HudobniVolk
8e4cff17a5 Translated using Weblate (Slovenian)
Currently translated at 46.1% (6 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/sl/
2023-01-26 17:12:42 +00:00
LamaEpik
da954ed3fd Translated using Weblate (French)
Currently translated at 15.3% (2 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/fr/
2023-01-26 17:12:42 +00:00
Andrewblasco
9d48beaebb Translated using Weblate (Spanish)
Currently translated at 100.0% (13 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/es/
2023-01-26 17:12:42 +00:00
tygyh
e607118347 Translated using Weblate (Swedish)
Currently translated at 31.8% (7 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/sv/
2023-01-26 17:12:42 +00:00
Andrewblasco
5f6dafb763 Translated using Weblate (Spanish)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/es/
2023-01-26 17:12:42 +00:00
Kevin
052a000d3c Translated using Weblate (German)
Currently translated at 100.0% (22 of 22 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/de/
2023-01-26 17:12:42 +00:00
Kevin
59315f81ec Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/de/
2023-01-26 17:12:42 +00:00
LucasGGamerM
5b1188ce97 Merge pull request #55 from FineFindus/feat/profile-menu
feat(profile): move options to overflow menu
2023-01-24 18:16:50 -03:00
LucasGGamerM
c11863388d Putting stuff back to sk_ instead of mo_ on sk strings.
Relevant command: find values*/strings_sk.xml -type f -exec sed -i 's/mo_/sk_/g' {} \;
2023-01-24 17:47:12 -03:00
LucasGGamerM
476b462e86 Fixing a weird crash on the onHidden method added in a commit from upstream 2023-01-24 17:36:15 -03:00
LucasGGamerM
f69b308936 Merge pull request #54
Updated german translation
2023-01-24 17:29:59 -03:00
FineFindus
bdcafd2564 feat(profile): move options to overflow menu 2023-01-24 20:42:41 +01:00
dontobi
3455aab3ba Updated german translation 2023-01-24 11:13:02 +01:00
LucasGGamerM
aac4f412bd Finishing moving all the relevant strings to strings_mo.xml 2023-01-23 18:56:50 -03:00
LucasGGamerM
26831c3375 Updating somethings 2023-01-23 18:49:33 -03:00
LucasGGamerM
d7004824fb Decoupling moshidon strings from megalodon 2023-01-23 18:47:07 -03:00
LucasGGamerM
53d93764b0 Adding an icon and text to the hide compose button while scrolling setting 2023-01-23 17:50:07 -03:00
LucasGGamerM
6a84462b79 Adding an option for enabling/disabling the fab. Still missing an icon and text 2023-01-23 17:39:59 -03:00
Grishka
739d30c887 Save last seen home timeline post via markers API 2023-01-23 17:26:06 -03:00
Grishka
aa3aa8a5f9 Save last seen home timeline post via markers API 2023-01-23 17:20:47 -03:00
Grishka
be48719f52 remove log 2023-01-23 17:19:50 -03:00
Grishka
8e60d107fe Allow viewing alt text on images
closes #100
2023-01-23 17:19:46 -03:00
Grishka
c618feabe9 Workaround to fix #497 2023-01-23 17:19:34 -03:00
Grishka
87164dc469 Minor fixes from our boy grishka himself 2023-01-23 17:18:05 -03:00
sk
4ab1c61262 work around black screen opening notifs
closes sk22#342
2023-01-23 17:08:15 -03:00
LucasGGamerM
fe519f10a1 Failed try to fix the fabled fab problem 2023-01-22 19:41:17 -03:00
LucasGGamerM
901c7c3806 Revert "Fixing the bug of the fab reloading every time the user profile loads"
This reverts commit 211e6cdee2.
2023-01-22 19:08:24 -03:00
LucasGGamerM
211e6cdee2 Fixing the bug of the fab reloading every time the user profile loads 2023-01-22 18:36:31 -03:00
LucasGGamerM
394699c072 Fixing fab behavior on profile page when notes are selected 2023-01-22 18:26:53 -03:00
sk
c9766defff Removing the banners from the local and federated timelines 2023-01-22 18:18:14 -03:00
LucasGGamerM
737aa95bf5 Revert "Making it so the fab doesnt reappear when the notes is in edit mode"
This reverts commit 870ac2b946.
2023-01-22 15:19:52 -03:00
LucasGGamerM
38e035d792 Fixing #53 2023-01-22 13:33:45 -03:00
LucasGGamerM
4aa750c05e Update README.md
Adding a link to the official matrix chatrooom
2023-01-21 17:37:57 -03:00
LucasGGamerM
c3e5f4d254 Changelog for 87 2023-01-21 16:11:14 -03:00
LucasGGamerM
2e5ff452fd Bumping version number 2023-01-21 16:03:52 -03:00
LucasGGamerM
c397c08e40 In the process of trying to make them stack I just couldnt. So I guess this is the new release then 2023-01-21 16:02:11 -03:00
LucasGGamerM
a4d2101f54 a little less broken 2023-01-20 22:12:24 -03:00
sk
f956a17797 Experimental notifications improvement, still a long while to go 2023-01-20 22:06:42 -03:00
sk
1c1d1772a3 Adding needAppRestart to reduceMotion setting 2023-01-20 17:30:12 -03:00
sk
4db87feec4 Making sure it compiles! 2023-01-20 17:30:12 -03:00
sk
bef3c72513 fix "0" reply to ID 2023-01-20 17:30:12 -03:00
sk
4fa641b482 fix null-pointer when switching themes 2023-01-20 17:30:12 -03:00
sk
885b5d781a tweak timeline title animation 2023-01-20 17:30:12 -03:00
sk
2f3bfb3e74 fix clearing notifications
closes sk22#292
2023-01-20 17:30:12 -03:00
sk
2be625fd76 add missing draft params
closes sk22#302
2023-01-20 17:30:12 -03:00
sk
134a371263 only show new posts button at home 2023-01-20 17:30:12 -03:00
sk
8b0eddb8e1 set pivot of timeline title
closes sk22#296
2023-01-20 17:30:12 -03:00
sk
bd2d56b953 remove debug statements 2023-01-20 17:30:12 -03:00
sk
38e429f738 improve multi-line style 2023-01-20 17:30:12 -03:00
sk
de8b15d447 don't cut off multi-line strings 2023-01-20 17:30:12 -03:00
sk
0df1bcce31 fix new posts centered layout 2023-01-20 17:30:12 -03:00
sk
4e17256cfa fix centering button in rtl 2023-01-20 17:30:12 -03:00
sk
e12c3e2d68 fix rtl direction 2023-01-20 17:30:12 -03:00
sk
aec2704f15 restore current tab 2023-01-20 17:30:12 -03:00
sk
31f9173126 don't use old fragments 2023-01-20 17:30:12 -03:00
sk
90196df65d simplify method 2023-01-20 17:30:12 -03:00
sk
6b9fa71806 change crash workaround 2023-01-20 17:30:12 -03:00
sk
130085f804 Revert "work around crash theme switch"
This reverts commit 58fd0c444f30aa5352486b97cab34b2aca6ce8ab.
2023-01-20 17:30:12 -03:00
sk
f4356e74a4 add pager title transition 2023-01-20 17:30:12 -03:00
sk
9c8a4b7a8e Fixing some compile problems 2023-01-20 17:30:12 -03:00
sk22
b7ccf1144c New home layout with public timelines (#288)
* add dummy popup menu
* add pager to home fragment
* reduce pager sensitivity
* remove timelines from discover fragment
* add fabs to timelines
* change info banner color
* add back toolbar functionality
* update icons on navigate
* handle back press
* add lists and hashtags
* use tabs
* improve timeline title
* tweak switcher behavior
* fix show new posts button appearance
* hide show new posts button on reload
* tweak show new posts animations
* work around crash theme switch
* enable disabling federated timeline
Thanks @sk22!
2023-01-20 17:30:11 -03:00
sk
87d5b92a99 change crash workaround 2023-01-20 17:29:23 -03:00
LucasGGamerM
29f8260852 Merge remote-tracking branch 'origin/master' 2023-01-20 11:26:10 -03:00
LucasGGamerM
060745869b Revert "New home layout with public timelines (#288)"
This reverts commit 78d0add808.
2023-01-20 11:19:20 -03:00
LucasGGamerM
1aff3eacd8 Revert "New home layout with public timelines (#288)"
This reverts commit 0207ddb774.
2023-01-20 11:19:16 -03:00
LucasGGamerM
0207ddb774 New home layout with public timelines (#288)
* add dummy popup menu
* add pager to home fragment
* reduce pager sensitivity
* remove timelines from discover fragment
* add fabs to timelines
* change info banner color
* add back toolbar functionality
* update icons on navigate
* handle back press
* add lists and hashtags
* use tabs
* improve timeline title
* tweak switcher behavior
* fix show new posts button appearance
* hide show new posts button on reload
* tweak show new posts animations
* work around crash theme switch
* enable disabling federated timeline
2023-01-20 11:19:09 -03:00
sk22
78d0add808 New home layout with public timelines (#288)
* add dummy popup menu
* add pager to home fragment
* reduce pager sensitivity
* remove timelines from discover fragment
* add fabs to timelines
* change info banner color
* add back toolbar functionality
* update icons on navigate
* handle back press
* add lists and hashtags
* use tabs
* improve timeline title
* tweak switcher behavior
* fix show new posts button appearance
* hide show new posts button on reload
* tweak show new posts animations
* work around crash theme switch
* enable disabling federated timeline
2023-01-20 11:17:26 -03:00
LucasGGamerM
2fa042490a Revert "feat(status/footer): add tooltips to icons"
This reverts commit 707c51e4d6.
2023-01-20 11:09:16 -03:00
LucasGGamerM
885f559092 Revert "Revert "click "replying to" to scroll up""
This reverts commit 77af7ceae3.
2023-01-20 11:01:39 -03:00
sk
77af7ceae3 Revert "click "replying to" to scroll up"
This reverts commit cd0cfba7
2023-01-20 11:00:53 -03:00
sk
09d4188d54 click "replying to" to scroll up
closes #241
2023-01-20 10:59:02 -03:00
LucasGGamerM
1ad03828e3 Merge pull request #49 from dontobi/master
Update german translation
2023-01-19 20:36:22 -03:00
LucasGGamerM
870ac2b946 Making it so the fab doesnt reappear when the notes is in edit mode 2023-01-19 20:19:33 -03:00
FineFindus
394a3eebb1 feat(composeButton): hide when scrolling in profile fragment 2023-01-19 20:01:39 -03:00
FineFindus
95c10a9fea feat(composeButton): hide fab on scroll 2023-01-19 20:00:54 -03:00
dontobi
f0e14c5a13 Update german translation 2023-01-18 21:26:47 +01:00
LucasGGamerM
b79b69d961 Adding a todo 2023-01-18 15:46:15 -03:00
LucasGGamerM
5118a1fb1e Reverting TextStatusDisplayItem to master's, as I have figured out a better way to do filters 2023-01-18 15:36:22 -03:00
LucasGGamerM
18275183d0 Merge branch 'master' into feature/filters 2023-01-18 15:28:07 -03:00
LucasGGamerM
616049bff2 Fixing weird icon in the profile 2023-01-18 14:15:07 -03:00
sk
1a79bc0b61 Changing Megalodon to Moshidon in the strings.
The command I use for automating this is "find values* -type f -exec sed -i 's/Megalodon/Moshidon/g' {} \;". Run this in the resources folder and it should all be changed
2023-01-18 14:10:38 -03:00
sk
d43cbe642f disable translating scheduled posts
closes sk22#318
2023-01-18 14:07:12 -03:00
sk
77cee4c46a fix "0" reply to ID 2023-01-18 14:07:07 -03:00
sk22
0949ad1ce6 Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2023-01-18 14:07:00 -03:00
sk22
1e411c0c23 Translated using Weblate (German)
Currently translated at 100.0% (13 of 13 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2023-01-18 14:07:00 -03:00
ling0412
76d306aef7 Translated using Weblate (Chinese (Simplified))
Currently translated at 97.9% (146 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-18 14:07:00 -03:00
sk22
7ff19ef481 Translated using Weblate (German)
Currently translated at 100.0% (149 of 149 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-18 14:07:00 -03:00
ihor_ck
6650bb946f Translated using Weblate (Ukrainian)
Currently translated at 100.0% (144 of 144 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-18 14:06:50 -03:00
Linerly
bbbf1683aa Translated using Weblate (Indonesian)
Currently translated at 100.0% (144 of 144 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:06:50 -03:00
Choukajohn
5cdea99eb0 Translated using Weblate (French)
Currently translated at 100.0% (144 of 144 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:06:50 -03:00
ling0412
356426b5fc Translated using Weblate (Chinese (Simplified))
Currently translated at 98.6% (142 of 144 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-18 14:06:49 -03:00
gicorada
7577d60f42 Translated using Weblate (Italian)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/it/
2023-01-18 14:06:49 -03:00
ihor_ck
8c7364d57d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-18 14:06:49 -03:00
Oliebol
c0a2945378 Translated using Weblate (Dutch)
Currently translated at 93.7% (134 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-01-18 14:06:49 -03:00
gicorada
1af9a71210 Translated using Weblate (Italian)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-01-18 14:06:48 -03:00
Choukajohn
dc859fe91c Translated using Weblate (French)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:06:42 -03:00
Nicolas_Horvath
d50c37af23 Translated using Weblate (Czech)
Currently translated at 13.9% (20 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cs/
2023-01-18 14:06:42 -03:00
ca
00c8a03b80 Translated using Weblate (Catalan)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2023-01-18 14:06:42 -03:00
ca
d15d222b72 Translated using Weblate (Catalan)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ca/
2023-01-18 14:06:41 -03:00
ca
34d134cb57 Translated using Weblate (Catalan)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2023-01-18 14:06:34 -03:00
EifionLlwyd
40eb3e2400 Translated using Weblate (Welsh)
Currently translated at 48.2% (69 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cy/
2023-01-18 14:06:34 -03:00
McKris
0af45e5f56 Translated using Weblate (Polish)
Currently translated at 58.3% (7 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/
2023-01-18 14:06:34 -03:00
ihor_ck
82e3250623 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-18 14:06:31 -03:00
HudobniVolk
533a51fc77 Translated using Weblate (Slovenian)
Currently translated at 15.3% (22 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-01-18 14:06:31 -03:00
edxkl
6fc82cf26b Translated using Weblate (Portuguese (Brazil))
Currently translated at 95.1% (136 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-18 14:06:31 -03:00
McKris
6dcfdb9735 Translated using Weblate (Polish)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-01-18 14:06:30 -03:00
Linerly
217d8348d4 Translated using Weblate (Indonesian)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:06:22 -03:00
ghose
9bce934944 Translated using Weblate (Galician)
Currently translated at 25.1% (36 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-18 14:06:21 -03:00
Choukajohn
a1ef3e1cae Translated using Weblate (French)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:06:21 -03:00
gallegonovato
978c1cfdc4 Translated using Weblate (Spanish)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-18 14:06:20 -03:00
ling0412
9fb9ffc269 Translated using Weblate (Chinese (Simplified))
Currently translated at 98.6% (141 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-18 14:06:20 -03:00
AiOO
4f357637de Translated using Weblate (Korean)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:06:20 -03:00
maxocito
1acd177e81 Translated using Weblate (German)
Currently translated at 100.0% (143 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-18 14:06:20 -03:00
ca
e4b1bf452f Translated using Weblate (Catalan)
Currently translated at 98.6% (141 of 143 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2023-01-18 14:06:20 -03:00
Anonymous
ce22cb4678 Translated using Weblate (Burmese)
Currently translated at 71.4% (100 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/my/
2023-01-18 14:06:20 -03:00
Linerly
3c45215fca Translated using Weblate (Indonesian)
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:06:20 -03:00
ghose
b17e63acae Translated using Weblate (Galician)
Currently translated at 10.7% (15 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/
2023-01-18 14:06:20 -03:00
mondstern
859ba5ebb9 Translated using Weblate (Czech)
Currently translated at 13.5% (19 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cs/
2023-01-18 14:06:20 -03:00
NovaQ64
164579cbb5 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.1% (136 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-18 14:06:20 -03:00
EifionLlwyd
990f8189e4 Translated using Weblate (Welsh)
Currently translated at 23.5% (33 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/cy/
2023-01-18 14:06:20 -03:00
ihor_ck
4d0a642fd9 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-18 14:06:16 -03:00
brenno
84b2994b99 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.4% (135 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-18 14:06:15 -03:00
Linerly
efbca327c1 Translated using Weblate (Indonesian)
Currently translated at 88.5% (124 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:06:06 -03:00
Choukajohn
aa4b007d25 Translated using Weblate (French)
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:06:06 -03:00
gallegonovato
23dccef4b4 Translated using Weblate (Spanish)
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-18 14:06:06 -03:00
ling0412
1ef96ed5e6 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-18 14:06:06 -03:00
sk22
31e2a32233 Translated using Weblate (German)
Currently translated at 100.0% (140 of 140 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-18 14:06:06 -03:00
irure
168ae80743 Translated using Weblate (Basque)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-18 14:06:06 -03:00
Hiajen
8acf23ddac Translated using Weblate (German)
Currently translated at 97.6% (122 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-01-18 14:06:05 -03:00
AiOO
eb40211582 Translated using Weblate (Korean)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:06:00 -03:00
irure
bf35161c9f Translated using Weblate (Basque)
Currently translated at 93.6% (117 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-18 14:05:59 -03:00
Linerly
456c50f69e Translated using Weblate (Indonesian)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:05:44 -03:00
Choukajohn
587212cf46 Translated using Weblate (French)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:05:44 -03:00
AiOO
fe800a259d Translated using Weblate (Korean)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:05:44 -03:00
Linerly
c193741013 Translated using Weblate (Indonesian)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:05:39 -03:00
Choukajohn
a7b752264f Translated using Weblate (French)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:05:31 -03:00
AiOO
9075027f69 Translated using Weblate (Korean)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:05:25 -03:00
EifionLlwyd
28faf4277a Added translation using Weblate (Welsh) 2023-01-18 14:04:53 -03:00
irure
120ab8ca54 Translated using Weblate (Basque)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-18 14:04:53 -03:00
AiOO
a3fc1a6a74 Translated using Weblate (Korean)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:04:51 -03:00
irure
ec1fe07fea Translated using Weblate (Basque)
Currently translated at 93.6% (117 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2023-01-18 14:04:51 -03:00
mondstern
e3d054ae3e Translated using Weblate (Hungarian)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/hu/
2023-01-18 14:04:51 -03:00
edxkl
795d4b0801 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2023-01-18 14:04:51 -03:00
khant
0cf0f07f2d Translated using Weblate (Burmese)
Currently translated at 16.6% (2 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/my/
2023-01-18 14:04:51 -03:00
AiOO
2cc5872ec7 Translated using Weblate (Korean)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2023-01-18 14:04:51 -03:00
HitaloM
6a151e00ac Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2023-01-18 14:04:47 -03:00
ling0412
1e99862d40 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2023-01-18 14:04:41 -03:00
khant
3a1b12306b Translated using Weblate (Burmese)
Currently translated at 80.0% (100 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/my/
2023-01-18 14:04:31 -03:00
ihor_ck
e31db6d506 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-01-18 14:04:31 -03:00
edxkl
b22c0a5d3d Translated using Weblate (Portuguese (Brazil))
Currently translated at 84.0% (105 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-01-18 14:04:31 -03:00
Linerly
e7d856acf4 Translated using Weblate (Indonesian)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:04:31 -03:00
Choukajohn
ee8b087b61 Translated using Weblate (French)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:04:31 -03:00
gallegonovato
4e86314df5 Translated using Weblate (Spanish)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-01-18 14:04:31 -03:00
ling0412
cf5fbe3b55 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-01-18 14:04:31 -03:00
AiOO
952416fefc Translated using Weblate (Korean)
Currently translated at 100.0% (125 of 125 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:04:31 -03:00
AiOO
1645ce4486 Translated using Weblate (Korean)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2023-01-18 14:04:30 -03:00
Linerly
2bde95b4d2 Translated using Weblate (Indonesian)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-01-18 14:04:21 -03:00
Choukajohn
2de003c5bb Translated using Weblate (French)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-01-18 14:04:21 -03:00
AiOO
a04e16c572 Translated using Weblate (Korean)
Currently translated at 100.0% (124 of 124 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2023-01-18 14:04:21 -03:00
sk
eb41d77d54 implement soft-blocking
sk22#297
2023-01-18 14:03:36 -03:00
sk
9325590319 change search placeholder string 2023-01-18 14:03:15 -03:00
sk
d32a57a18d fix akkoma link previews not showing
closes sk22#183
2023-01-18 14:02:53 -03:00
sk
13b5462f63 fix images not loading on akkoma
closes sk22#25
2023-01-18 14:02:50 -03:00
sk
480d4ad904 make announcements selectable
closes sk22#275
2023-01-18 14:02:23 -03:00
sk
7fa52247e8 display unread announcements first
closes sk22#274
2023-01-18 14:02:20 -03:00
sk
5a547015e6 include list title in delete prompt
closes sk22#304
2023-01-18 14:02:11 -03:00
LucasGGamerM
513736f765 Fixing duplicate settings item 2023-01-18 14:02:04 -03:00
sk
c6164b1bcd display image cache size
closes sk22#301
2023-01-18 14:00:15 -03:00
sk
87342782d7 fix clearing notifications
closes sk22#292
2023-01-18 13:58:03 -03:00
sk
a4b18de72c add missing draft params
closes sk22#302
2023-01-18 13:57:59 -03:00
sk
3064b549cd fix cut off text
closes sk22#295
2023-01-18 13:57:50 -03:00
sk
6666f82329 don't require unused fields 2023-01-18 13:55:49 -03:00
Grishka
ad87efa7e2 Fix #484 2023-01-18 13:55:34 -03:00
Grishka
d06cf1bb1e Paginate trending posts 2023-01-18 13:55:29 -03:00
sk
096aa23f69 don't use person icon for unblock
close #245
2023-01-18 13:54:25 -03:00
Grishka
2464042329 Fix #499 2023-01-18 13:54:06 -03:00
sk
928b04eda6 tweak list decoration
closes sk22#273
2023-01-18 13:54:03 -03:00
sk
a31e33415e fix edit user lists always visible
closes sk22#272
2023-01-18 13:53:59 -03:00
Grishka
87ce6b8bb1 Fix #498 2023-01-18 13:53:45 -03:00
sk
bb08d6585c disable more button when read
closes #262
2023-01-18 13:53:33 -03:00
sk
94fa1133fd fix editing sensitive option
closes sk22#260
2023-01-18 13:52:58 -03:00
sk
83822b8f69 add more domains 2023-01-18 13:52:50 -03:00
sk
9f3bd186ba improve domain check logic 2023-01-18 13:52:46 -03:00
sk
58cb338cb2 add missing icons to popup 2023-01-18 13:52:16 -03:00
sk
6f447909eb add dividers to lists 2023-01-18 13:52:11 -03:00
sk
af953e294d move lists and change icons 2023-01-18 13:51:39 -03:00
LucasGGamerM
7504a1b9cb Revert "move lists and change icons"
This reverts commit 3842ecb0d1.
2023-01-18 13:47:29 -03:00
LucasGGamerM
60ee781004 Revert "Revert "move lists and change icons""
This reverts commit 4b88ce5115.
2023-01-18 13:47:29 -03:00
LucasGGamerM
4b88ce5115 Revert "move lists and change icons"
This reverts commit d0ad5561
2023-01-18 13:45:41 -03:00
sk
3842ecb0d1 move lists and change icons 2023-01-18 13:44:30 -03:00
sk
3713063ce3 implement editing lists
re: sk22#30
2023-01-18 13:35:26 -03:00
sk
83b089457e implement deleting lists
re: sk22#30
2023-01-18 13:34:54 -03:00
sk
ed9813f093 implement creating lists
re: sk22#30
2023-01-18 13:34:34 -03:00
sk
e45f3f30f3 change lists string 2023-01-18 13:33:02 -03:00
sk
df44d4cc4f don't overwrite notifications
re: sk22#219
2023-01-18 13:32:58 -03:00
sk
b666048603 add missing margin 2023-01-18 13:29:48 -03:00
FineFindus
153542e1b4 feat(settings): add about instance link (#263) 2023-01-18 13:29:45 -03:00
Tyler Saunders
57e0b96f36 Update ComposeFragment to use the photopicker.
The android platform has a great photopicker, and this change checks
for the current device's sdk version, and uses the photopicker if it's
available on the device.

For pre Android R sdkrev2 devices, the experience remains unchanged.
2023-01-18 13:27:08 -03:00
mishnz
ff65d150e3 serverKey assignment was missing, corrected. 2023-01-18 13:23:23 -03:00
mishnz
88474ba826 Second fix for MIME64 inconsistency in serverKey.
The previous fix https://github.com/mastodon/mastodon-android/pull/486 would break any connections to any instances using WEB_SAFE MIME64 encoding on the serverKey, which actually appears to be the usual case.
This update reverts to the previous logic, but also converts standard MIME64 characters ('/' and '+') to their WEB_SAFE equivalents.
This ensures the standard case of WEB_SAFE BASE64 serverKeys and the anomolous case of DEFAULT BASE64 keys both work.
2023-01-18 13:23:17 -03:00
mishnz
dd92f1b66f The Mastodon server does not currently use URL_SAFE encoding on its serverKey. Using URL_SAFE in this client means the client will crash for any server that uses a key that generates a Mime64 string containing a "+" or "/". This change removes the URL_SAFE logic. See: https://github.com/mastodon/mastodon-android/issues/483 2023-01-18 13:23:10 -03:00
LucasGGamerM
7c7f3cc42a Properly Fixing the bot icon at the end of the username on the header of posts. 2023-01-17 22:26:54 -03:00
LucasGGamerM
734a8049a5 Taking the notes out of the about section until further notice 2023-01-17 21:38:01 -03:00
LucasGGamerM
417faa66f9 Deleting some old stuff 2023-01-17 21:35:58 -03:00
LucasGGamerM
7223a13d08 Editing the padding so that text doesnt go below the button to save 2023-01-17 21:29:51 -03:00
LucasGGamerM
a55002da0c Messing a bit with the colors, I think I like this one 2023-01-17 21:26:44 -03:00
LucasGGamerM
ce89733f2d Making the notes thing more beautiful, and also saves when back button is pressed. i love this. Try it later @FineFindus! 2023-01-17 21:12:04 -03:00
LucasGGamerM
18811ec32a Adding a bigger margin for consistency 2023-01-17 20:42:02 -03:00
LucasGGamerM
e65e6163ba Successfully putting it back where it was. Still need to add a toggle for it 2023-01-16 21:32:54 -03:00
LucasGGamerM
9c3db24d2f Little improvement on the note field 2023-01-16 21:10:06 -03:00
LucasGGamerM
19abbe199b Fixing the Bot icon. Look at how gorgeous this is @FineFindus! 2023-01-16 20:57:05 -03:00
LucasGGamerM
b33003f7b0 Merge pull request #46
Feat/private notes
2023-01-16 20:24:53 -03:00
LucasGGamerM
9a5747efc8 Revert "Fixing it"
This reverts commit 1a075e32de.
2023-01-16 15:26:27 -03:00
LucasGGamerM
980503ed57 Merge remote-tracking branch 'origin/master' 2023-01-16 14:49:59 -03:00
LucasGGamerM
c2dd858de8 Merge pull request #47
refactor(settings): reorder settings
2023-01-16 14:49:53 -03:00
LucasGGamerM
d2ef6fb567 Merge remote-tracking branch 'origin/master' 2023-01-16 14:48:34 -03:00
LucasGGamerM
9c996b3568 Merge pull request #45 from FineFindus/feat/tooltips
feat(status/footer): add tooltips to icons
2023-01-16 14:48:26 -03:00
FineFindus
2387d84bc0 fix(settings): reorder settings 2023-01-14 22:48:03 +01:00
FineFindus
3bd69b5447 fix(note/confirm): animation not displaying 2023-01-14 14:58:31 +01:00
FineFindus
71f6311598 merge: update to latest master 2023-01-14 12:51:29 +01:00
FineFindus
e808977717 refactor(profile/note): rephrase confirm tooltip 2023-01-14 12:50:06 +01:00
FineFindus
8594e34bb5 refactor(profile/note): move note in about tab 2023-01-14 12:46:40 +01:00
FineFindus
4591f06d63 refactor(profile/note): rework edit text hint 2023-01-14 12:44:57 +01:00
FineFindus
b9c3143c6f feat(profile/note): increase animation duration 2023-01-14 11:01:29 +01:00
FineFindus
adefb0e567 feat(profile/note): add fade animation to confirm icon 2023-01-13 23:46:58 +01:00
FineFindus
5a8fed3c06 feat(profile/note): add toast on save failure 2023-01-13 23:41:57 +01:00
FineFindus
2c1b8da475 feat(profile): add private notes textfield 2023-01-13 23:38:40 +01:00
FineFindus
707c51e4d6 feat(status/footer): add tooltips to icons 2023-01-12 19:32:30 +01:00
LucasGGamerM
1a075e32de Fixing it 2023-01-11 20:10:14 -03:00
LucasGGamerM
73869b6ea2 86 changelog 2023-01-11 11:28:29 -03:00
LucasGGamerM
3980329112 Merge remote-tracking branch 'origin/master' 2023-01-11 11:26:21 -03:00
LucasGGamerM
6107b21d3b Merge pull request #43
Update german translation
2023-01-11 11:26:14 -03:00
LucasGGamerM
42a0b881af Bumping version number 2023-01-11 11:23:00 -03:00
LucasGGamerM
2076818220 Now its fixed for good. That was easier than I thought 2023-01-11 11:19:30 -03:00
Tobias S
513bea7959 Merge branch 'LucasGGamerM:master' into master 2023-01-11 05:52:06 +01:00
LucasGGamerM
a48dc18df5 Possibly fixing #29 for good, but the mastodon servers of the accounts I own are slow, so there is basically no way to test it 2023-01-10 21:45:35 -03:00
dontobi
c55639e966 Update german translation 2023-01-10 17:28:26 +01:00
Tobias S
1fc89d7448 Merge branch 'LucasGGamerM:master' into master 2023-01-10 17:20:37 +01:00
LucasGGamerM
4fcc07fcd6 Adding a margin to the char counter when its on the top 2023-01-10 12:24:01 -03:00
LucasGGamerM
b50b1c33da Renaming custom login fragment to custom welcome fragment 2023-01-10 12:11:11 -03:00
LucasGGamerM
25460191b0 Merge pull request #44
feat(compose): move charCounter to action bar if relocatePublishButto…
2023-01-10 12:09:44 -03:00
FineFindus
1206ce6efc feat(compose): move charCounter to action bar if relocatePublishButton is enabled 2023-01-10 15:43:26 +01:00
sk
3cdcffe03d implement announcements
closes #127
2023-01-10 11:16:14 -03:00
sk
b09aab4a1d don't connect to fascists 2023-01-10 11:13:30 -03:00
sk
d27fe4ebd1 tweak header text margin 2023-01-10 11:13:27 -03:00
sk
ab2cee08fe hide keyboard when navigating
closes #227
2023-01-10 11:13:14 -03:00
sk
1fa42fd20f scale text according to system
closes #244
2023-01-10 11:13:04 -03:00
sk
e343131670 fix wrong pin icon
closes #230
2023-01-10 11:11:32 -03:00
sk
f157313d9a click "replying to" to scroll up
closes #241
2023-01-10 11:11:28 -03:00
sk
9bd4433942 tweak reply animation
closes #237
2023-01-10 11:08:56 -03:00
sk
c9cc8e23c1 don't add subject if blank
closes #240
2023-01-10 11:08:53 -03:00
Thiago 'Jedi' Abreu
786dbaf92c Filter all status, even if filters are empty (#255) 2023-01-10 11:08:40 -03:00
mishnz
8dd22e1853 serverKey assignment was missing, corrected. 2023-01-10 11:08:33 -03:00
mishnz
34a4dd6d1f Second fix for MIME64 inconsistency in serverKey.
The previous fix https://github.com/mastodon/mastodon-android/pull/486 would break any connections to any instances using WEB_SAFE MIME64 encoding on the serverKey, which actually appears to be the usual case.
This update reverts to the previous logic, but also converts standard MIME64 characters ('/' and '+') to their WEB_SAFE equivalents.
This ensures the standard case of WEB_SAFE BASE64 serverKeys and the anomolous case of DEFAULT BASE64 keys both work.
2023-01-10 11:08:27 -03:00
mishnz
6e13e592d0 The Mastodon server does not currently use URL_SAFE encoding on its serverKey. Using URL_SAFE in this client means the client will crash for any server that uses a key that generates a Mime64 string containing a "+" or "/". This change removes the URL_SAFE logic. See: https://github.com/mastodon/mastodon-android/issues/483 2023-01-10 11:08:20 -03:00
Tobias S
485ab4ee22 Update german translation 2023-01-10 10:09:11 +01:00
dontobi
59a8d1d462 Update german translation 2023-01-10 06:24:10 +01:00
LucasGGamerM
f4e5baf94d Merge pull request #41
feat(accessibility) add missing toolTipTexts and contentDescriptions
2023-01-09 18:32:21 -03:00
FineFindus
a782160dd3 refactor: use getContentDescription instead of duplicate code 2023-01-09 21:30:52 +01:00
FineFindus
871c17cbe2 merge: update to latest master 2023-01-09 21:18:43 +01:00
FineFindus
047e72ce9c refactor(compose): use UiUtils.getVisibiltyText 2023-01-09 21:05:58 +01:00
FineFindus
6c1424055f feat(StatusDisplay): add toolTip/ContentDescription to visibilty icon 2023-01-09 21:02:00 +01:00
FineFindus
369902ffe5 feat(compose): add toolTip/ContentDescription to error icon 2023-01-09 20:19:29 +01:00
LucasGGamerM
6c778d05ea Its half working++ 2023-01-09 16:16:38 -03:00
FineFindus
747439999d feat(profile): add toolTip/ContentDescription to fab 2023-01-09 19:23:38 +01:00
FineFindus
4d836f8032 feat(instanceCategory): set (unused) emoji importantForAccessibility to no 2023-01-09 19:21:54 +01:00
FineFindus
f97ab73c5d feat(reportChoice): set radioIcon importantForAccessibility to no 2023-01-09 19:21:20 +01:00
FineFindus
147fb94442 feat(settingsItem): set icon to not importantForAccessibility 2023-01-09 19:20:07 +01:00
FineFindus
975dc94d41 feat(settingsThemeItem): hide image for accessibility 2023-01-09 19:12:19 +01:00
FineFindus
a5c1053c58 feat(recycler_fragment_with_fab): add toolTip/ContentDescription 2023-01-09 17:46:27 +01:00
FineFindus
1bfbb4bf38 fix(composeMedia): change sk_remove_attachment to delete 2023-01-09 17:42:01 +01:00
FineFindus
c2950ace90 feat(tabBar): accessibilty hide profile avatar
Sets the `importantForAccessibility` to no on the profile avatar, since the parent already has a contentDescription.
2023-01-09 17:36:04 +01:00
FineFindus
f0846465c2 feat(accessibility): add contentDesc/toolTip to remove attachment 2023-01-09 17:22:54 +01:00
FineFindus
ddc4512116 feat(accessibility): add contentDesc/toolTip to poll options 2023-01-09 17:22:33 +01:00
FineFindus
1c9e4fe561 feat(accessibility): add contentDesc/toolTip to poll options 2023-01-09 17:21:38 +01:00
FineFindus
35299a7b3f feat(accessibility): add contentDesc and toolTip to audioItem 2023-01-09 17:19:20 +01:00
LucasGGamerM
2d9938e8b2 Its half working 2023-01-08 21:31:01 -03:00
LucasGGamerM
1e99940c1d I have figured out something, I dont know what it is though 2023-01-08 21:01:59 -03:00
LucasGGamerM
2827bcffe3 Merge branch 'master' into feature/filters 2023-01-08 20:56:22 -03:00
LucasGGamerM
3a1b71e95c Adding a small little different icon for when the toot has replies. Thanks @sk22 2023-01-08 20:55:41 -03:00
LucasGGamerM
ce4e762cd5 Trying my best to make this work 2023-01-07 21:29:03 -03:00
LucasGGamerM
54ec1a6cf7 Merge pull request #38
Update german translation
2023-01-07 20:46:23 -03:00
FineFindus
bed3e987b7 feat(compose/poll): add contentDescription to addPoll 2023-01-07 11:38:00 +01:00
Tobias S
639ddb3f80 Update german translation 2023-01-06 19:43:28 +01:00
LucasGGamerM
e645abb771 Merge pull request #35
Update german translation
2023-01-06 14:51:00 -03:00
LucasGGamerM
762adce054 Merge branch 'bugfix/#29' 2023-01-06 14:47:37 -03:00
LucasGGamerM
263bde658e Fixing #29 for good 2023-01-06 14:47:06 -03:00
Florian Obernberger
3951acf12e Add new and custom icon
(cherry picked from commit 13a80fb536)
2023-01-06 14:43:17 -03:00
LucasGGamerM
5cb640a387 Merge pull request #36
Add new and custom icon
2023-01-06 14:42:46 -03:00
FineFindus
c65b9ff873 fix: remove change 2023-01-06 18:40:28 +01:00
Florian Obernberger
13a80fb536 Add new and custom icon 2023-01-06 18:12:42 +01:00
LucasGGamerM
23e49c52e5 Its working, but it still needs a better icon 2023-01-06 12:26:52 -03:00
Tobias S
9fcc73984b Merge branch 'LucasGGamerM:master' into master 2023-01-06 15:51:29 +01:00
LucasGGamerM
67338b6c85 Allowing multiple notifications by default 2023-01-06 11:31:25 -03:00
LucasGGamerM
101e7efd74 Fixing #34 2023-01-06 11:18:51 -03:00
Tobias S
a996a24b7f Update german translation 2023-01-06 06:22:25 +01:00
Tobias S
44dcc9fe2b Merge branch 'LucasGGamerM:master' into master 2023-01-06 06:20:35 +01:00
LucasGGamerM
58f79e06ef Idk what these are, but testing commits 2023-01-05 21:16:03 -03:00
FineFindus
f197c8201d merge: update to newest master 2023-01-05 19:45:03 +01:00
dontobi
2fc5669203 Update german translation 2023-01-05 18:53:12 +01:00
LucasGGamerM
0065b93060 Adding 85 changelog 2023-01-05 13:44:24 -03:00
LucasGGamerM
3f9dbd6fe2 Bump version number 2023-01-05 13:39:53 -03:00
LucasGGamerM
7ceff3eaa4 Fixing #28 2023-01-05 13:24:09 -03:00
FineFindus
7cdddf06bc feat(compose/add-poll): add contentLabel for more option 2023-01-05 16:24:44 +01:00
LucasGGamerM
77e7b136ff Changing megalodon to moshidon 2023-01-05 12:23:20 -03:00
LucasGGamerM
c8d160fc35 Changing too to post. Fixing #26 2023-01-05 12:19:13 -03:00
LucasGGamerM
ee0737c9c7 Adding the ability to long click or to double click the search icon to bring up the keyboard 2023-01-04 21:54:03 -03:00
LucasGGamerM
0f85be7114 Merge branch 'feature/long_press_discover' 2023-01-04 21:33:19 -03:00
LucasGGamerM
34ba4ceb16 Fixing #31 2023-01-04 21:28:06 -03:00
LucasGGamerM
f2a536d0ea Fixing #31 2023-01-04 18:30:12 -03:00
LucasGGamerM
815c4d4cc9 Merge pull request #32
feat: share links
2023-01-04 18:20:29 -03:00
LucasGGamerM
969b91bba9 Merging stuff and adding more icons 2023-01-04 18:18:58 -03:00
LucasGGamerM
f24abde76e Merge pull request #30
feat(profile/bot-icon): prepend icon to username
2023-01-04 18:04:41 -03:00
LucasGGamerM
f486b1a9ce Merge pull request #24
Feat/favourite animation
2023-01-04 17:59:12 -03:00
LucasGGamerM
3dd6638ef3 Merge pull request #25
German translation - suggested by FineFindus
2023-01-04 17:58:37 -03:00
FineFindus
bcfb63b57c feat(linkSpan/longClick): use share intent istead of copy text 2023-01-04 20:53:30 +01:00
FineFindus
c7f5f6827a refactor(drawables/botIcon): decrease size and change color 2023-01-04 17:25:30 +01:00
FineFindus
8ed88b2b29 refactor(HeaderStatusDisplayItem): add bot icon to username 2023-01-04 17:24:53 +01:00
FineFindus
75db9f4623 refactor(profile/botIcon): move icon to username 2023-01-04 17:24:09 +01:00
LucasGGamerM
b69015a25a Its closer to working 2023-01-03 15:18:34 -03:00
LucasGGamerM
1cdcc8794c Maybe it will work one day? 2023-01-03 11:21:30 -03:00
LucasGGamerM
a0c26b748a It already works, although not to my standard yet 2023-01-03 11:10:53 -03:00
FineFindus
ce5e733c05 fix(drawable/bot): remove unused icons 2023-01-03 14:18:18 +01:00
FineFindus
de485272c5 feat(profile/bot): add bot icon before name 2023-01-03 13:53:04 +01:00
FineFindus
eab53b805e feat(drawable) add filled bot icon 2023-01-03 13:52:09 +01:00
dontobi
a60046f6ef German translation - suggested by FineFindus 2023-01-02 22:15:00 +01:00
FineFindus
b0d223c47c Merge branch 'master' of https://github.com/LucasGGamerM/moshidon into feat/favourite-animation 2023-01-02 21:59:39 +01:00
FineFindus
185a8c776b feat(StatusDisplayItem/favouriteIcon): add spin animation when clicked 2023-01-02 21:57:19 +01:00
LucasGGamerM
f510ee3b4d Merge pull request #23
Update german translation
2023-01-02 17:56:59 -03:00
dontobi
e7a29824e8 Update german translation 2023-01-02 21:54:35 +01:00
LucasGGamerM
69b86dd98c Merge pull request #21
feat(compose/emoji) add a recent category to the emoji keyboard
2023-01-02 17:48:39 -03:00
LucasGGamerM
55807dc7c6 Merge pull request #22
small update of the german translation
2023-01-02 17:45:43 -03:00
dontobi
9a6ee719c4 small update of the german translation 2023-01-02 20:57:23 +01:00
FineFindus
1b02af382c fix(emoji/recent): remove unused imports 2023-01-02 19:43:30 +01:00
FineFindus
4fe87a9888 merge: merge newest upstream/master 2023-01-02 19:42:42 +01:00
LucasGGamerM
91995155e9 Updating 84 changelog 2023-01-02 13:47:42 -03:00
LucasGGamerM
aae0ff5aa7 Fixing the bug with the german translation 2023-01-02 13:42:45 -03:00
LucasGGamerM
821d9b8a5e 84 changelog 2023-01-02 13:41:51 -03:00
LucasGGamerM
2070aed38f Bumping version number 2023-01-02 13:40:03 -03:00
LucasGGamerM
986979eefc Merge remote-tracking branch 'origin/master' 2023-01-02 13:38:59 -03:00
LucasGGamerM
ed3ce54b24 Merge pull request #20
German translation
2023-01-02 13:38:52 -03:00
LucasGGamerM
c80ebf2eda Fixing the login error 2023-01-02 13:38:29 -03:00
FineFindus
42fcd6df51 refactor(emoji/recent): rename NEW_RECENT to NEW_RECENT_VALUE 2023-01-02 16:58:31 +01:00
LucasGGamerM
1f4031da61 Editing the 83 changelog once more 2023-01-02 12:53:39 -03:00
Tobias S
d0ebee74ca Merge branch 'LucasGGamerM:master' into master 2023-01-02 16:52:51 +01:00
Tobias S
e8ec042d96 German translation 2023-01-02 16:52:11 +01:00
LucasGGamerM
bd61bf32b6 Changing the 83 changelog 2023-01-02 12:49:17 -03:00
FineFindus
8b12fac766 feat(emoji/recent): add clear recent emoji setting 2023-01-02 16:47:41 +01:00
LucasGGamerM
ab7e6b3332 Bump version number 2023-01-02 12:45:27 -03:00
LucasGGamerM
548da48615 Adding 83 changelog 2023-01-02 12:44:57 -03:00
FineFindus
0b6128bcdd feat(compose/emojis): add category of recent emojis 2023-01-02 16:44:14 +01:00
LucasGGamerM
198a7d5ad3 Merge pull request #18
feat(compose/publish): Add warning dialog when no image description is provided
2023-01-02 12:33:10 -03:00
LucasGGamerM
040f244e15 Fixing up the bot icon for good 2023-01-02 12:28:07 -03:00
LucasGGamerM
98bc6f14a9 Fixing some icons again 2023-01-02 12:14:27 -03:00
LucasGGamerM
b92e6d2c48 Fixing some icons 2023-01-02 12:12:55 -03:00
LucasGGamerM
ac3875fe08 Fixing #19 2023-01-02 11:46:15 -03:00
FineFindus
6fdbafc67b fix(compose/publish): remove unnecessary import 2023-01-02 12:57:18 +01:00
FineFindus
5aebdcaa6a feat(compose/publish): add warning when no image description provided 2023-01-02 12:53:34 +01:00
LucasGGamerM
2969a3e4fd Including @FineFindus on the changelog 2023-01-01 19:41:42 -03:00
LucasGGamerM
e1fcf44aa6 Adding more stuff onto the changelog 2023-01-01 19:28:26 -03:00
LucasGGamerM
ddbfe9de57 Changelog for 82 and updating screenshots 2023-01-01 19:25:17 -03:00
LucasGGamerM
b2ee1527af Bump version number 2023-01-01 19:17:40 -03:00
LucasGGamerM
c94706745f Merge pull request #17
German translation
2023-01-01 19:13:13 -03:00
LucasGGamerM
8aef61ff09 Second iteration of the bot icon 2023-01-01 19:12:29 -03:00
Tobias S
5d6e245ec2 Merge branch 'LucasGGamerM:master' into master 2023-01-01 22:13:11 +01:00
dontobi
8dd4d1a41d German translation 2023-01-01 22:12:36 +01:00
LucasGGamerM
10f9230139 First iteration of the bot account tag 2023-01-01 18:06:26 -03:00
LucasGGamerM
32c94c8948 Merge remote-tracking branch 'origin/master' 2023-01-01 17:24:23 -03:00
LucasGGamerM
4b947dd1f9 Merge pull request #16
feat(profile) add icon to bot accounts
2023-01-01 17:24:06 -03:00
LucasGGamerM
93a7d86f78 Merge branch 'development'
# Conflicts:
#	mastodon/src/main/res/values-de-rDE/strings_sk.xml
2023-01-01 17:21:32 -03:00
LucasGGamerM
6ed3a57c58 Rebranding Megalodon to Moshidon.
Here is the command in case anyone is interested
find values* -type f -exec sed -i 's/Megalodon/Moshidon/g' {} \;
2023-01-01 17:16:45 -03:00
LucasGGamerM
ecdba3898f Merge pull request #15
German translation fix
2023-01-01 17:14:36 -03:00
LucasGGamerM
d82a9b25cf Revert "Revert "match navigation bar color with toolbar""
This reverts commit 0fdae0c775.
2023-01-01 17:12:29 -03:00
LucasGGamerM
da4849e526 Removing unnecessary needAppRestart on the relocatePublishButton setting 2023-01-01 17:11:33 -03:00
LucasGGamerM
da1b47ea0d Adds a whole lot of icons for the relocated publish button 2023-01-01 17:09:49 -03:00
LucasGGamerM
a617693f93 Add a drafts icon on the relocated publish button 2023-01-01 16:53:31 -03:00
LucasGGamerM
f4c573a95e Readding the relocated publish button 2023-01-01 16:28:04 -03:00
LucasGGamerM
d3476c1473 Deleting metadata 2023-01-01 10:49:48 -03:00
LucasGGamerM
d58247d996 Editing the padding and making the 3 dot menu invisible 2023-01-01 10:49:34 -03:00
LucasGGamerM
ee441d5b4a Glory! it finally works! 2022-12-31 21:46:37 -03:00
LucasGGamerM
97a5c6f5cb Bringing back the verify for updates button 2022-12-31 21:27:12 -03:00
LucasGGamerM
b6566a2bcc It works now? 2022-12-31 21:23:50 -03:00
LucasGGamerM
af389f7a47 Its as broken as it was before 2022-12-31 20:35:29 -03:00
LucasGGamerM
45577fc423 The settings page is unfucked again 2022-12-31 20:24:14 -03:00
sk
c00b9b3035 fix empty space when no text 2022-12-31 20:05:12 -03:00
sk
f17879783c tweak scroll height/timing 2022-12-31 20:05:08 -03:00
sk
08dfcdf508 Revert "match navigation bar color with toolbar"
This reverts commit 43d334259b.
2022-12-31 20:05:03 -03:00
sk
c97e8fffc4 add option to reduce motion 2022-12-31 20:04:53 -03:00
sk
120585954f peek original post before scrolling 2022-12-31 20:00:12 -03:00
sk
247669644c display original post when replying
closes sk22#193
2022-12-31 20:00:05 -03:00
sk
ff15bdeaea change interact with strings 2022-12-31 20:00:02 -03:00
ihor_ck
8b67320e20 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-31 19:59:00 -03:00
gallegonovato
48828ed1b7 Translated using Weblate (Spanish)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-31 19:58:53 -03:00
Linerly
1e615db77e Translated using Weblate (Indonesian)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-31 19:58:47 -03:00
Choukajohn
629416ef2f Translated using Weblate (French)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-31 19:58:40 -03:00
ihor_ck
d6b1c88085 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-31 19:58:34 -03:00
Linerly
3b7079be17 Translated using Weblate (Indonesian)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-31 19:58:34 -03:00
Choukajohn
48d26017b6 Translated using Weblate (French)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-31 19:58:34 -03:00
gallegonovato
8bdbe1c4d7 Translated using Weblate (Spanish)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-31 19:58:33 -03:00
bubblineyuri
4093443bf8 Translated using Weblate (German)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2022-12-31 19:58:32 -03:00
bubblineyuri
c6709aba56 Translated using Weblate (German)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-31 19:58:24 -03:00
ihor_ck
a817e03fa0 Translated using Weblate (Ukrainian)
Currently translated at 96.6% (115 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-31 19:58:08 -03:00
Linerly
a757607f35 Translated using Weblate (Indonesian)
Currently translated at 100.0% (119 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-31 19:58:07 -03:00
ling0412
ecc9c45f6e Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (119 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-31 19:58:07 -03:00
irure
2e157a9e68 Translated using Weblate (Basque)
Currently translated at 18.1% (2 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/eu/
2022-12-31 19:58:07 -03:00
AiOO
8a39154bd3 Translated using Weblate (Korean)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-31 19:58:06 -03:00
nkufideal
b5e2aa8b7f Translated using Weblate (Russian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2022-12-31 19:58:05 -03:00
nkufideal
aaea709201 Translated using Weblate (Russian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-31 19:57:56 -03:00
itslameni
c2a0f5e8bc Translated using Weblate (Russian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-31 19:57:56 -03:00
edxkl
2873a66450 Translated using Weblate (Portuguese (Brazil))
Currently translated at 80.3% (82 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-31 19:57:54 -03:00
irure
7331a5a0cd Translated using Weblate (Basque)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2022-12-31 19:57:41 -03:00
AiOO
c331cc95c2 Translated using Weblate (Korean)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-31 19:57:41 -03:00
edxkl
91aa425f43 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-31 19:57:40 -03:00
ihor_ck
fd5165428e Translated using Weblate (Ukrainian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-31 19:57:40 -03:00
Linerly
6df57aebf4 Translated using Weblate (Indonesian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-31 19:57:40 -03:00
Choukajohn
40b2e4c42e Translated using Weblate (French)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-31 19:57:40 -03:00
gallegonovato
a762408df8 Translated using Weblate (Spanish)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-31 19:57:40 -03:00
ling0412
9fb6a0261b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-31 19:57:40 -03:00
sk
d90edc8cf1 add compose fab to drafts list 2022-12-31 19:57:29 -03:00
sk
eecc1242be handle recursive compose/drafts list 2022-12-31 19:57:25 -03:00
sk
6eedb0e156 move and refine scheduling and drafting 2022-12-31 19:57:22 -03:00
sk
519894a461 Adding a whole lot of stuff 2022-12-31 19:55:53 -03:00
sk
fe3052a359 schedule/draft in overflow menu 2022-12-31 19:53:50 -03:00
sk
245b91bebd don't finish compose fragment 2022-12-31 19:53:21 -03:00
sk
2e47147367 offer to save when scheduledAt changed
closes sk22#218
2022-12-31 19:53:09 -03:00
LucasGGamerM
8fec3fe56c Making it not show the text when the publishButton is relocated 2022-12-31 14:52:19 -03:00
LucasGGamerM
c4d56179f3 Fixing the icons on the bottom row of the compose fragment 2022-12-31 14:42:57 -03:00
LucasGGamerM
64d24f6002 Drafts and scheduled posts (#217)
closes #100
closes #59

* enable saving as draft (scheduled)
* add scheduled posts list
* fix NoSuchMethodError
* editable drafts/scheduled posts
* ui for drafts
* use instants between 9999-01-01 and 9999-12-31
* use save and draft strings
* map scheduled status params to status
* implement scheduling posts
* improve save/discard draft dialog
* persist scheduled date in state
* add unsent posts button to toolbar
* clean up imports
2022-12-31 14:28:06 -03:00
sk22
074efb0813 Drafts and scheduled posts (#217)
closes #100
closes #59

* enable saving as draft (scheduled)
* add scheduled posts list
* fix NoSuchMethodError
* editable drafts/scheduled posts
* ui for drafts
* use instants between 9999-01-01 and 9999-12-31
* use save and draft strings
* map scheduled status params to status
* implement scheduling posts
* improve save/discard draft dialog
* persist scheduled date in state
* add unsent posts button to toolbar
* clean up imports
2022-12-31 14:05:03 -03:00
Thiago 'Jedi' Abreu
98b96c78d7 Better handling of filter expiration date (#212)
* Better handling of filter expiration date
* Simplify Thread and Home Timeline filtering
2022-12-31 13:53:33 -03:00
LucasGGamerM
3a962c7c05 Refactoring the uniform notification icon setting 2022-12-31 13:47:55 -03:00
LucasGGamerM
99e3658938 Making fab feel better on light themes. Merging this might be good for you too, @sk22 2022-12-31 12:49:16 -03:00
FineFindus
9cd7ad3601 feat(profile) add icon to bot accounts 2022-12-31 15:11:52 +01:00
FineFindus
96db2b7a8b feat(icons) add bot icon 2022-12-31 15:01:24 +01:00
LucasGGamerM
a881f23253 Fixing the PublishButton background and also fixing the Fab icon background on trueblack theme. Its a relevant thing, i beliebe you shall merge this @sk22 2022-12-30 20:23:13 -03:00
LucasGGamerM
54106c497b Unfucking your own profile icons. They now look how they should 2022-12-30 20:00:11 -03:00
LucasGGamerM
1554c6d422 Editing settings page 2022-12-30 19:43:07 -03:00
LucasGGamerM
dccd9dcb97 Editing the InstanceRulesFragment for allowing a more coherent color scheme 2022-12-30 19:42:16 -03:00
LucasGGamerM
16c0866f7f It fucking compiles. Still a whole lot of stuff to be done 2022-12-30 14:12:10 -03:00
sk
db88de206b add mention with quasi-quoting 2022-12-30 13:32:51 -03:00
dontobi
92f353e1c5 Updated german translation 2022-12-30 12:06:20 +01:00
sk
71c80dd381 I want to fucking die 2022-12-29 18:01:59 -03:00
sk
d2be917bd4 fix akkoma crash on edit 2022-12-29 17:56:15 -03:00
ihor_ck
ea3dc32e98 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-29 17:56:06 -03:00
edxkl
be86f1e96f Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-29 17:55:48 -03:00
ihor_ck
d626d45f5c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-29 17:55:48 -03:00
sk
f007bdb39c revert broken color attribute for notif icon
closes #190
2022-12-29 17:55:19 -03:00
sk
a0cbfe9a36 fix #200 2022-12-29 17:54:01 -03:00
sk
21f99081f2 long-click to compose from other account 2022-12-29 17:53:32 -03:00
Pointifurry
45952ef143 Translated using Weblate (Spanish)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-29 17:50:16 -03:00
Linerly
fedaaa6fc8 Translated using Weblate (Indonesian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-29 17:50:16 -03:00
ling0412
1eb08e40be Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2022-12-29 17:50:16 -03:00
Choukajohn
6a4936853b Translated using Weblate (French)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-29 17:50:16 -03:00
nitrogenez
b277eb4990 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-29 17:50:16 -03:00
itslameni
74efc5c332 Translated using Weblate (Russian)
Currently translated at 96.7% (88 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-29 17:50:15 -03:00
goliv
6653dd97a6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 74.7% (68 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-29 17:50:08 -03:00
Linerly
d031acabf5 Translated using Weblate (Indonesian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-29 17:49:57 -03:00
Choukajohn
852f666b78 Translated using Weblate (French)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-29 17:49:57 -03:00
Pointifurry
9f729e9ef0 Translated using Weblate (Spanish)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-29 17:49:57 -03:00
ling0412
027f19e710 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-29 17:49:57 -03:00
sk22
14786f796b Translated using Weblate (German)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-29 17:49:56 -03:00
sk
411e39a096 fix default reblog visibility 2022-12-29 17:49:28 -03:00
Linerly
8a72c5b9ca Translated using Weblate (Indonesian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-29 17:49:22 -03:00
Choukajohn
9e88e28a59 Translated using Weblate (French)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-29 17:49:22 -03:00
Linerly
df73c5ad8d Translated using Weblate (Indonesian)
Currently translated at 100.0% (89 of 89 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-29 17:49:22 -03:00
Choukajohn
98707dde76 Translated using Weblate (French)
Currently translated at 100.0% (89 of 89 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-29 17:49:22 -03:00
sk
444a9afabe disable block domain from post 2022-12-29 17:49:15 -03:00
sk
315bcd5b1a fix instance info v2 never getting saved 2022-12-29 17:48:51 -03:00
sk
c719ac22da improve settings items 2022-12-29 17:48:44 -03:00
nitrogenez
f4596998aa Translated using Weblate (Ukrainian)
Currently translated at 90.0% (9 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-29 17:46:40 -03:00
ling0412
5414f5cf41 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (10 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2022-12-29 17:46:39 -03:00
nitrogenez
d919827bc8 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (86 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-29 17:46:36 -03:00
Linerly
7b40bc157d Translated using Weblate (Indonesian)
Currently translated at 100.0% (86 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-29 17:46:36 -03:00
Choukajohn
c3ec64d1ff Translated using Weblate (French)
Currently translated at 100.0% (86 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-29 17:46:36 -03:00
gallegonovato
3464cb4a05 Translated using Weblate (Spanish)
Currently translated at 100.0% (86 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-29 17:46:36 -03:00
ling0412
21f40e5b42 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (86 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-29 17:46:36 -03:00
benjaminwolkchen
6d8ecab766 Translated using Weblate (German)
Currently translated at 98.8% (85 of 86 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-29 17:46:36 -03:00
sk
fc2e5112c0 add option to open post with other account
closes #182
2022-12-29 17:45:09 -03:00
sk
8f6c57d5c9 use popup background color for dialogs 2022-12-29 17:44:15 -03:00
sk
8455dc7bd2 implement long-click to copy links
closes sk22#84
2022-12-29 17:44:05 -03:00
sk
4677cef580 remove selectable background for username
closes sk22#187
2022-12-29 17:44:01 -03:00
sk
9ab6bf3da1 only show boost visibility for own posts 2022-12-29 17:43:57 -03:00
sk
bc65fa6654 remove selectable background 2022-12-29 17:43:45 -03:00
AiOO
9fe5960449 Translated using Weblate (Korean)
Currently translated at 100.0% (10 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-29 17:43:23 -03:00
Linerly
b684dd5a3b Translated using Weblate (Indonesian)
Currently translated at 100.0% (10 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-29 17:43:18 -03:00
Christian Elbrianno
5fff0cbdb0 Translated using Weblate (Indonesian)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-29 17:43:17 -03:00
Linerly
99dc479d78 Translated using Weblate (Indonesian)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-29 17:43:17 -03:00
Choukajohn
275eef912c Translated using Weblate (French)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-29 17:43:17 -03:00
gallegonovato
b9de7632e6 Translated using Weblate (Spanish)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-29 17:43:12 -03:00
AiOO
7a000f2a44 Translated using Weblate (Korean)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-29 17:43:01 -03:00
braydofficial
9d91b8c0fb Translated using Weblate (German)
Currently translated at 100.0% (83 of 83 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-29 17:42:41 -03:00
sk
84d213bdd1 add missing fedi url format 2022-12-29 17:41:57 -03:00
sk
a2302ad318 implement setting boost visibility 2022-12-29 17:41:51 -03:00
sk
42eacea4be use color attributes instead of fixed colors 2022-12-29 17:39:36 -03:00
LucasGGamerM
664bfe895e Trying to process wtf happened here 2022-12-29 17:38:19 -03:00
LucasGGamerM
9c1812ce08 Trying to process wtf happened here 2022-12-29 17:38:01 -03:00
sk
49c83581f9 Trying to process wtf happened here 2022-12-29 17:31:22 -03:00
gallegonovato
6aa0428879 Translated using Weblate (Spanish)
Currently translated at 100.0% (10 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-29 17:24:41 -03:00
Choukajohn
838d0c678b Translated using Weblate (French)
Currently translated at 100.0% (10 of 10 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-29 17:24:40 -03:00
Choukajohn
35555d1362 Translated using Weblate (French)
Currently translated at 100.0% (70 of 70 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-29 17:24:40 -03:00
gallegonovato
6c6b5d7be9 Translated using Weblate (Spanish)
Currently translated at 100.0% (70 of 70 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-29 17:24:34 -03:00
sk
037b8cc54e no ellipsis for settings items 2022-12-29 17:23:50 -03:00
sk
afb518cf8e animate translate button opacity 2022-12-29 17:23:15 -03:00
LucasGGamerM
b36c7375dd Fixing the compile problems 2022-12-29 17:22:37 -03:00
sk
069356418d Replace custom symbol with fluent mention symbol 2022-12-29 17:18:55 -03:00
sk
6b1c725ebd fix toolbar inset not applying in true black 2022-12-29 17:14:58 -03:00
LucasGGamerM
a46f8f1c20 Adding a new fluent edit icon for the fab 2022-12-29 17:13:44 -03:00
Tobias S
3b3d98c4be Merge branch 'LucasGGamerM:master' into master 2022-12-29 19:05:21 +01:00
LucasGGamerM
3d4742e8f7 Metada improvements for 81 2022-12-29 14:08:19 -03:00
LucasGGamerM
a6f1c21e1c Bumping version number 2022-12-29 14:06:33 -03:00
LucasGGamerM
a0b62ab434 Merge branch 'feature/publish_button_improvements' 2022-12-29 14:04:08 -03:00
LucasGGamerM
0b8501e92b Merge branch 'feature/show_changelog_button' 2022-12-29 14:01:34 -03:00
LucasGGamerM
9a0851cb06 Fixing some padding issues with the changelog 2022-12-29 14:00:53 -03:00
dontobi
0cd5d12d42 German translation 2022-12-29 07:41:19 +01:00
LucasGGamerM
a4581dc61b More changes to the 80 changelog 2022-12-28 20:33:19 -03:00
LucasGGamerM
50d4130b3f Making the relocated button default 2022-12-28 20:14:45 -03:00
LucasGGamerM
1147087531 Update settings page screenshot 2022-12-28 20:07:22 -03:00
LucasGGamerM
4653a22635 Merge pull request #12 from dontobi/master
Adding some german translation
2022-12-28 20:06:29 -03:00
LucasGGamerM
e79501857f Adding changelog for the 80th release of Moshidon! 2022-12-28 19:58:53 -03:00
LucasGGamerM
a8c05f6a32 Bump version number 2022-12-28 19:55:27 -03:00
LucasGGamerM
26297fbb5b Lastcomm 2022-12-28 18:02:38 -03:00
LucasGGamerM
cd342d1034 I am trying my best 2022-12-28 14:56:49 -03:00
LucasGGamerM
029650ef2d Its almost working, the UI side is done. But the request is being canceled when the back button is pressed. 2022-12-28 12:34:00 -03:00
dontobi
ca5f189e70 German translation 2022-12-28 16:24:58 +01:00
LucasGGamerM
ac24f636df Its a little less broken 2022-12-28 11:15:12 -03:00
LucasGGamerM
1688168bc1 The ui is less broken 2022-12-28 11:05:33 -03:00
LucasGGamerM
46b842afc4 It still does not work, but its one step closer to working 2022-12-28 10:45:15 -03:00
LucasGGamerM
3f773a52cc Created the class. Lets leave this for tomorrow 2022-12-27 21:43:42 -03:00
LucasGGamerM
48664bb580 Idk what to call this commit, so Note Improver it is 2022-12-27 21:42:21 -03:00
LucasGGamerM
094cd67728 Now adding a confirm button 2022-12-27 21:19:54 -03:00
LucasGGamerM
9d800106cc Added a framelayout just for adding the confirm button 2022-12-27 20:20:44 -03:00
LucasGGamerM
68bb23e3b4 It actually loads something. Perfect 2022-12-27 18:48:08 -03:00
LucasGGamerM
68397bd487 How did I add some many lines? Removing them 2022-12-27 18:27:57 -03:00
LucasGGamerM
3104ddb4b6 More polishes the UI side a little more, now also hides the private note on the personal profile 2022-12-27 18:26:35 -03:00
LucasGGamerM
9bddd6b274 The ui side is ready somehow. Still need to handle it all 2022-12-27 16:53:21 -03:00
533 changed files with 12780 additions and 1988 deletions

View File

@@ -5,7 +5,7 @@
> 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 wont ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FLucasGGamerM%2Fmoshidon%2Freleases%2Flatest&style=for-the-badge)](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=282C37&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FLucasGGamerM%2Fmoshidon%2Freleases%2Flatest&style=for-the-badge)](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
---
@@ -14,15 +14,6 @@
### **Material you theme support on Android 12+ devices!**
### **Translate button**
**Allows you to translate posts in instances with the translate feature!**
**Screenshots**
![Screenshot_20221209-135457_1](https://user-images.githubusercontent.com/71328265/206753830-cdb8bc65-7732-4a6a-8bcd-bbc4ca311d19.png)
![Screenshot_20221209-135409_1](https://user-images.githubusercontent.com/71328265/206753831-7af92a48-d7a5-4780-9beb-90acef4e141b.png)
### **Color themes**
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
@@ -88,7 +79,12 @@ Variant with an integrated updater. If you download Moshidon from here (and not
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
* [Add “Federation” tab and change Discover tab order](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/add-federated-timeline) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/8))
* Adding a useful private profile note box!*
* Auto hiding the compose button on scroll!*
* Adding the hability to remind yourself to add alt text to images!*
* Adding the ability to have drafts!*
* Also adding the ability to view announcements from your instance!*
* Adding the ability to post for local timeline only (Only on instances that support it!)*
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
@@ -110,6 +106,7 @@ Variant with an integrated updater. If you download Moshidon from here (and not
### Behavior
* Adding a bottom option for the publish button, allowing for easier user on larger screens!
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
* [Display full image when adding image description](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/compose-image-description-full-image) ([Pull request](https://github.com/mastodon/mastodon-android/pull/182))
@@ -140,4 +137,6 @@ This project is released under the [GPL-3 License](./LICENSE).
## Links
[Official matrix chatroom:](https://matrix.to/#/#moshidon:matrix.org) https://matrix.to/#/#moshidon:matrix.org
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>

View File

@@ -9,8 +9,8 @@ android {
applicationId "org.joinmastodon.android.moshinda"
minSdk 23
targetSdk 33
versionCode 79
versionName "1.1.4+fork.79.moshinda"
versionCode 91
versionName "1.1.4+fork.91.moshinda"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
}

View File

@@ -14,12 +14,14 @@ import android.os.Build;
import android.util.Log;
import android.widget.Toast;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
@@ -113,64 +115,70 @@ public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
private void actuallyCheckForUpdates(){
Request req=new Request.Builder()
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases/latest")
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
.build();
Call call=MastodonAPIController.getHttpClient().newCall(req);
try(Response resp=call.execute()){
JsonObject obj=JsonParser.parseReader(resp.body().charStream()).getAsJsonObject();
String changelog=obj.get("body").getAsString();
String tag=obj.get("tag_name").getAsString();
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
Matcher matcher=pattern.matcher(tag);
if(!matcher.find()){
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
return;
}
int newMajor=Integer.parseInt(matcher.group(1)),
newMinor=Integer.parseInt(matcher.group(2)),
newRevision=Integer.parseInt(matcher.group(3)),
newForkNumber=Integer.parseInt(matcher.group(4));
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
if(!matcher.find()){
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
return;
}
int curMajor=Integer.parseInt(matcher.group(1)),
curMinor=Integer.parseInt(matcher.group(2)),
curRevision=Integer.parseInt(matcher.group(3)),
curForkNumber=Integer.parseInt(matcher.group(4));
long newVersion=((long)newMajor << 32) | ((long)newMinor << 16) | newRevision;
long curVersion=((long)curMajor << 32) | ((long)curMinor << 16) | curRevision;
if(newVersion>curVersion || newForkNumber>curForkNumber){
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
for(JsonElement el:obj.getAsJsonArray("assets")){
JsonObject asset=el.getAsJsonObject();
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
long size=asset.get("size").getAsLong();
String url=asset.get("browser_download_url").getAsString();
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
for (JsonElement jsonElement : arr) {
JsonObject obj = jsonElement.getAsJsonObject();
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
UpdateInfo info=new UpdateInfo();
info.size=size;
info.version=version;
info.changelog=changelog;
this.info=info;
String tag=obj.get("tag_name").getAsString();
String changelog=obj.get("body").getAsString();
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
Matcher matcher=pattern.matcher(tag);
if(!matcher.find()){
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
return;
}
int newMajor=Integer.parseInt(matcher.group(1)),
newMinor=Integer.parseInt(matcher.group(2)),
newRevision=Integer.parseInt(matcher.group(3)),
newForkNumber=Integer.parseInt(matcher.group(4));
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
if(!matcher.find()){
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
return;
}
int curMajor=Integer.parseInt(matcher.group(1)),
curMinor=Integer.parseInt(matcher.group(2)),
curRevision=Integer.parseInt(matcher.group(3)),
curForkNumber=Integer.parseInt(matcher.group(4));
long newVersion=((long)newMajor << 32) | ((long)newMinor << 16) | newRevision;
long curVersion=((long)curMajor << 32) | ((long)curMinor << 16) | curRevision;
if(newVersion>curVersion || newForkNumber>curForkNumber){
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
for(JsonElement el:obj.getAsJsonArray("assets")){
JsonObject asset=el.getAsJsonObject();
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
long size=asset.get("size").getAsLong();
String url=asset.get("browser_download_url").getAsString();
getPrefs().edit()
.putLong("apkSize", size)
.putString("version", version)
.putString("apkURL", url)
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
.putString("changelog", changelog)
.remove("downloadID")
.apply();
UpdateInfo info=new UpdateInfo();
info.size=size;
info.version=version;
info.changelog=changelog;
this.info=info;
break;
getPrefs().edit()
.putLong("apkSize", size)
.putString("version", version)
.putString("apkURL", url)
.putString("changelog", changelog)
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
.remove("downloadID")
.apply();
break;
}
}
}
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
break;
}
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
}catch(Exception x){
Log.w(TAG, "actuallyCheckForUpdates", x);
}finally{

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M3.897 4.054L3.97 3.97c0.266-0.267 0.683-0.29 0.976-0.073L5.03 3.97 10 8.939l4.97-4.97c0.266-0.266 0.683-0.29 0.976-0.072L16.03 3.97c0.267 0.266 0.29 0.683 0.073 0.976L16.03 5.03 11.061 10l4.97 4.97c0.266 0.266 0.29 0.683 0.072 0.976L16.03 16.03c-0.266 0.267-0.683 0.29-0.976 0.073L14.97 16.03 10 11.061l-4.97 4.97c-0.266 0.266-0.683 0.29-0.976 0.072L3.97 16.03c-0.267-0.266-0.29-0.683-0.073-0.976L3.97 14.97 8.939 10l-4.97-4.97C3.704 4.764 3.68 4.347 3.898 4.054L3.97 3.97 3.897 4.054z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M22 6.5c0 3.038-2.462 5.5-5.5 5.5S11 9.538 11 6.5 13.462 1 16.5 1 22 3.462 22 6.5zm-7.146-2.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L15.793 6.5l-1.647 1.646c-0.195 0.196-0.195 0.512 0 0.707 0.196 0.196 0.512 0.196 0.708 0L16.5 7.208l1.646 1.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.707L17.207 6.5l1.647-1.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L16.5 5.793l-1.646-1.647zM19.5 14v-1.732c0.551-0.287 1.056-0.651 1.5-1.078v7.56c0 1.733-1.357 3.15-3.066 3.245L17.75 22H6.25c-1.733 0-3.15-1.357-3.245-3.066L3 18.75V7.25C3 5.517 4.356 4.1 6.066 4.005L6.25 4h4.248c-0.198 0.474-0.34 0.977-0.422 1.5H6.25c-0.918 0-1.671 0.707-1.744 1.606L4.5 7.25V14H9c0.38 0 0.694 0.282 0.743 0.648L9.75 14.75C9.75 15.993 10.757 17 12 17c1.19 0 2.166-0.925 2.245-2.096l0.005-0.154c0-0.38 0.282-0.694 0.648-0.743L15 14h4.5zm-15 1.5v3.25c0 0.918 0.707 1.671 1.606 1.744L6.25 20.5h11.5c0.918 0 1.671-0.707 1.744-1.607L19.5 18.75V15.5h-3.825c-0.335 1.648-1.75 2.904-3.475 2.995L12 18.5c-1.747 0-3.215-1.195-3.632-2.812L8.325 15.5H4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -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>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M10 2c4.418 0 8 3.582 8 8 0 2.706-1.142 4.5-3 4.5-1.226 0-2.14-0.781-2.62-2.09C11.784 13.393 10.781 14 9.5 14 7.36 14 6 12.307 6 10c0-2.337 1.313-4 3.5-4 1.052 0 1.901 0.385 2.5 1.044V6.5C12 6.224 12.224 6 12.5 6c0.245 0 0.45 0.177 0.492 0.41L13 6.5V10c0 2.223 0.813 3.5 2 3.5s2-1.277 2-3.5c0-3.866-3.134-7-7-7s-7 3.134-7 7 3.134 7 7 7c0.823 0 1.626-0.142 2.383-0.416 0.26-0.094 0.547 0.04 0.64 0.3 0.095 0.26-0.04 0.546-0.3 0.64C11.859 17.838 10.94 18 10 18c-4.418 0-8-3.582-8-8s3.582-8 8-8zM9.5 7C7.924 7 7 8.17 7 10c0 1.797 0.966 3 2.5 3s2.5-1.203 2.5-3c0-1.83-0.924-3-2.5-3z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M6.25 4.5C5.283 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.783 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75v-4c0-0.414 0.335-0.75 0.75-0.75 0.414 0 0.75 0.336 0.75 0.75v4c0 1.795-1.456 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25C3 4.455 4.455 3 6.25 3h4C10.664 3 11 3.336 11 3.75S10.664 4.5 10.25 4.5h-4zM13 3.75C13 3.336 13.335 3 13.75 3h6.5C20.664 3 21 3.336 21 3.75v6.5c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75V5.56l-5.22 5.22c-0.293 0.293-0.768 0.293-1.06 0-0.293-0.293-0.293-0.768 0-1.06l5.22-5.22h-4.69C13.335 4.5 13 4.164 13 3.75z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M8.502 11.5c0.554 0 1.002 0.448 1.002 1.002 0 0.553-0.448 1.002-1.002 1.002-0.553 0-1.002-0.449-1.002-1.002 0-0.554 0.449-1.003 1.002-1.003zM12 4.353v6.651h7.442L17.72 9.28c-0.267-0.266-0.29-0.683-0.073-0.977L17.72 8.22c0.266-0.266 0.683-0.29 0.976-0.072L18.78 8.22l2.997 2.998c0.266 0.266 0.29 0.682 0.073 0.976l-0.073 0.084-2.996 3.003c-0.293 0.294-0.767 0.294-1.06 0.002-0.267-0.266-0.292-0.683-0.075-0.977l0.073-0.084 1.713-1.717h-7.431L12 19.25c0 0.466-0.421 0.82-0.88 0.738l-8.5-1.501C2.26 18.424 2 18.112 2 17.748V5.75c0-0.368 0.266-0.681 0.628-0.74l8.5-1.396C11.585 3.539 12 3.89 12 4.354zm-1.5 0.883l-7 1.15v10.732l7 1.236V5.237zM13 18.5h0.765l0.102-0.007c0.366-0.05 0.649-0.364 0.648-0.744l-0.007-4.25H13v5zm0.002-8.502L13 8.726V5h0.745c0.38 0 0.693 0.281 0.743 0.647l0.007 0.101L14.502 10h-1.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M14.704 3.44C14.895 3.667 15 3.953 15 4.248V19.75c0 0.69-0.56 1.25-1.25 1.25-0.296 0-0.582-0.105-0.808-0.296l-4.967-4.206H4.25c-1.243 0-2.25-1.008-2.25-2.25v-4.5c0-1.243 1.007-2.25 2.25-2.25h3.725l4.968-4.204c0.526-0.446 1.315-0.38 1.761 0.147zM13.5 4.787l-4.975 4.21H4.25c-0.414 0-0.75 0.337-0.75 0.75v4.5c0 0.415 0.336 0.75 0.75 0.75h4.275L13.5 19.21V4.787z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -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="M16.5 4.814c0-1.094-1.307-1.66-2.105-0.912l-4.937 4.63C9.134 8.836 8.706 9.005 8.261 9.005H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912V4.814zm-6.016 4.812L15 5.39v17.216l-4.516-4.232c-0.602-0.564-1.397-0.878-2.222-0.878H5.25c-0.966 0-1.75-0.784-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.826 0 1.62-0.314 2.223-0.88z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06L6.438 7.5H4.25C3.007 7.499 2 8.506 2 9.749v4.497c0 1.243 1.007 2.25 2.25 2.25h3.68c0.183 0 0.36 0.068 0.498 0.19l4.491 3.994C13.725 21.396 15 20.824 15 19.746V16.06l5.72 5.72c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM13.5 14.56v4.629l-4.075-3.624c-0.412-0.366-0.944-0.569-1.495-0.569H4.25c-0.414 0-0.75-0.335-0.75-0.75V9.75C3.5 9.335 3.836 9 4.25 9h3.688l5.562 5.56zm0-9.753v5.511l1.5 1.5V4.25c0-1.079-1.274-1.65-2.08-0.934l-3.4 3.022 1.063 1.063L13.5 4.807zm3.641 9.152l1.138 1.138C18.741 14.163 19 13.111 19 12c0-1.203-0.304-2.338-0.84-3.328-0.198-0.364-0.653-0.5-1.017-0.303-0.364 0.197-0.5 0.653-0.303 1.017 0.42 0.777 0.66 1.666 0.66 2.614 0 0.691-0.127 1.351-0.359 1.96zm2.247 2.247l1.093 1.094C21.445 15.763 22 13.946 22 12c0-2.226-0.728-4.284-1.96-5.946-0.246-0.333-0.716-0.403-1.048-0.157-0.333 0.247-0.403 0.716-0.157 1.05C19.881 8.358 20.5 10.106 20.5 12c0 1.531-0.404 2.966-1.112 4.206z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -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="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l5.724 5.725H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912v-5.623l8.22 8.22c0.292 0.292 0.767 0.292 1.06 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zM15 16.06v6.547l-4.516-4.231c-0.602-0.565-1.397-0.879-2.222-0.879H5.25c-0.966 0-1.75-0.783-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.35 0 0.693-0.056 1.02-0.164L15 16.061zm-4.378-8.62l1.061 1.061L15 5.392v6.427l1.5 1.5V4.814c0-1.094-1.307-1.66-2.105-0.912L10.622 7.44zm9.55 9.55l1.137 1.137C21.912 16.88 22.25 15.478 22.25 14c0-2.136-0.706-4.11-1.897-5.697-0.249-0.332-0.719-0.399-1.05-0.15-0.332 0.249-0.399 0.719-0.15 1.05C20.156 10.54 20.75 12.199 20.75 14c0 1.058-0.205 2.067-0.578 2.99zm2.803 2.803l1.095 1.096c1.224-2.008 1.93-4.366 1.93-6.89 0-3.35-1.245-6.414-3.298-8.747-0.274-0.31-0.747-0.341-1.058-0.068-0.311 0.274-0.342 0.748-0.068 1.059C23.396 8.313 24.5 11.027 24.5 14c0 2.107-0.554 4.084-1.525 5.793z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -12,10 +12,17 @@
<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>
</queries>
<application
android:name=".MastodonApp"
android:allowBackup="true"
android:label="@string/app_name"
android:label="@string/mo_app_name"
android:supportsRtl="true"
android:localeConfig="@xml/locales_config"
android:icon="@mipmap/ic_launcher"

View File

@@ -0,0 +1,89 @@
# lists.d Mastodon Blocklist (c) 2022 Greyhat Academy LICENSED UNDER: CC-BY-NC-SA 4.0
# https://raw.githubusercontent.com/greyhat-academy/lists.d/main/mastodon.domains.block.list.tsv
# This list contains domains of toxic mastodon instances
# Last-Modified: 1672044500
# gab - a neonazi social network
gab.ai
gab.com
gab.protohype.net
# consequence-free speech
social.unzensiert.to
freeatlantis.com
# reactionary bigotry and hatespeech against magrinalized groups
poa.st
freespeechextremist.com
rdrama.cc
outpoa.st
anime.website
gameliberty.club
social.byoblu.com
yggdrasil.social
smuglo.li
dogeposting.social
unsafe.space
freezepeach.xyz
# + CSAM
rojogato.com
# antivaxxer shitposting & fearmongering
shadowsocial.org
# Kiwifarms
kiwifarms.net
kiwifarms.cc
kiwifarms.is
kiwifarms.pleroma.net
# https://mastodon.art/@Curator/109649354849593592
poa.st antisemitic racist homophobic
nicecrew.digital antisemitic
beefyboys.win antisemitic racist homophobic harassment
cawfee.club antisemitic racist homophobic
comfyboy.club antisemitic racist homophobic
freespeechextremist.com racist homophobic
cum.salon racist misogynist
bae.st racist
natehiggers.online racist
rapemeat.solutions misogynist
rapist.town misogynist
rapefeminists.network misogynist
kiwifarms.cc harassment
noagendasocial.com noagenda
posting.lolicon.rocks underage
urchan.org harassment homophobic racist
ryona.agency harassment
yggdrasil.social antisemitic homophobic racist
genderheretics.xyz transphobic
baraag.net underage
lolison.top underage
shota.house underage
shota.social underage
aethy.com underage
taullo.social underage
childpawn.shop underage
posting.lolicon.rocks underage
loli.best underage
gothloli.club underage
smuglo.li underage
youjo.love underage
pedo.school underage
lolison.network underage
freak.university underage
mirr0r.city underage
xhais.love underage
refusal.biz underage
refusal.llc underage
mirr0r.city underage
nnia.space underage
ignorelist.com malicious
repl.co malicious
# custom
pawoo.net csam
1 # lists.d Mastodon Blocklist (c) 2022 Greyhat Academy LICENSED UNDER: CC-BY-NC-SA 4.0
2 # https://raw.githubusercontent.com/greyhat-academy/lists.d/main/mastodon.domains.block.list.tsv
3 # This list contains domains of toxic mastodon instances
4 # Last-Modified: 1672044500
5 # gab - a neonazi social network
6 gab.ai
7 gab.com
8 gab.protohype.net
9 # consequence-free speech
10 social.unzensiert.to
11 freeatlantis.com
12 # reactionary bigotry and hatespeech against magrinalized groups
13 poa.st
14 freespeechextremist.com
15 rdrama.cc
16 outpoa.st
17 anime.website
18 gameliberty.club
19 social.byoblu.com
20 yggdrasil.social
21 smuglo.li
22 dogeposting.social
23 unsafe.space
24 freezepeach.xyz
25 # + CSAM
26 rojogato.com
27 # antivaxxer shitposting & fearmongering
28 shadowsocial.org
29 # Kiwifarms
30 kiwifarms.net
31 kiwifarms.cc
32 kiwifarms.is
33 kiwifarms.pleroma.net
34 # https://mastodon.art/@Curator/109649354849593592
35 poa.st antisemitic racist homophobic
36 nicecrew.digital antisemitic
37 beefyboys.win antisemitic racist homophobic harassment
38 cawfee.club antisemitic racist homophobic
39 comfyboy.club antisemitic racist homophobic
40 freespeechextremist.com racist homophobic
41 cum.salon racist misogynist
42 bae.st racist
43 natehiggers.online racist
44 rapemeat.solutions misogynist
45 rapist.town misogynist
46 rapefeminists.network misogynist
47 kiwifarms.cc harassment
48 noagendasocial.com noagenda
49 posting.lolicon.rocks underage
50 urchan.org harassment homophobic racist
51 ryona.agency harassment
52 yggdrasil.social antisemitic homophobic racist
53 genderheretics.xyz transphobic
54 baraag.net underage
55 lolison.top underage
56 shota.house underage
57 shota.social underage
58 aethy.com underage
59 taullo.social underage
60 childpawn.shop underage
61 posting.lolicon.rocks underage
62 loli.best underage
63 gothloli.club underage
64 smuglo.li underage
65 youjo.love underage
66 pedo.school underage
67 lolison.network underage
68 freak.university underage
69 mirr0r.city underage
70 xhais.love underage
71 refusal.biz underage
72 refusal.llc underage
73 mirr0r.city underage
74 nnia.space underage
75 ignorelist.com malicious
76 repl.co malicious
77 # custom
78 pawoo.net csam

View File

@@ -53,7 +53,10 @@ public class ExternalShareActivity extends FragmentStackActivity{
Intent intent=getIntent();
StringBuilder builder=new StringBuilder();
String subject = "";
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) builder.append(subject = intent.getStringExtra(Intent.EXTRA_SUBJECT)).append("\n\n");
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) {
subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
if (!subject.isBlank()) builder.append(subject).append("\n\n");
}
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
String text=builder.toString();
List<Uri> mediaUris;

View File

@@ -9,10 +9,14 @@ import android.os.Build;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.model.TimelineDefinition;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class GlobalUserPreferences{
public static boolean playGifs;
@@ -21,26 +25,43 @@ public class GlobalUserPreferences{
public static boolean showReplies;
public static boolean showBoosts;
public static boolean loadNewPosts;
public static boolean showFederatedTimeline;
public static boolean showNewPostsButton;
public static boolean showInteractionCounts;
public static boolean alwaysExpandContentWarnings;
public static boolean disableMarquee;
public static boolean disableSwipe;
public static boolean disableDividers;
public static boolean voteButtonForSingleChoice;
public static boolean showDifferentiatedPushNoticationIcons;
public static boolean uniformNotificationIcon;
public static boolean enableDeleteNotifications;
public static boolean relocatePublishButton;
public static boolean reduceMotion;
public static boolean keepOnlyLatestNotification;
public static boolean enableFabAutoHide;
public static boolean disableAltTextReminder;
public static boolean showAltIndicator;
public static boolean showNoAltIndicator;
public static String publishButtonText;
public static ThemePreference theme;
public static ColorPreference color;
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
public static Map<String, List<String>> recentLanguages;
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
public static Set<String> accountsWithLocalOnlySupport;
public static Set<String> accountsInGlitchMode;
private static SharedPreferences getPrefs(){
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
public static Map<String, Integer> recentEmojis;
public static boolean enablePreReleases;
private static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
}
private static <T> T fromJson(String json, Type type, T orElse) {
if (json == null) return orElse;
try { return gson.fromJson(json, type); }
catch (JsonSyntaxException ignored) { return orElse; }
}
@@ -53,8 +74,8 @@ public class GlobalUserPreferences{
showReplies=prefs.getBoolean("showReplies", true);
showBoosts=prefs.getBoolean("showBoosts", true);
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
showDifferentiatedPushNoticationIcons=prefs.getBoolean("showDifferentiatedPushNoticationIcons", false);
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
showNewPostsButton=prefs.getBoolean("showNewPostsButton", true);
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", true);
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
disableMarquee=prefs.getBoolean("disableMarquee", false);
@@ -62,8 +83,22 @@ public class GlobalUserPreferences{
disableDividers=prefs.getBoolean("disableDividers", true);
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", true);
reduceMotion=prefs.getBoolean("reduceMotion", false);
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
enableFabAutoHide=prefs.getBoolean("enableFabAutoHide", true);
disableAltTextReminder=prefs.getBoolean("disableAltTextReminder", false);
showAltIndicator =prefs.getBoolean("showAltIndicator", true);
showNoAltIndicator =prefs.getBoolean("showNoAltIndicator", true);
enablePreReleases =prefs.getBoolean("enablePreReleases", false);
publishButtonText=prefs.getString("publishButtonText", "");
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
publishButtonText=prefs.getString("publishButtonText", "");
pinnedTimelines=fromJson(prefs.getString("pinnedTimelines", null), pinnedTimelinesType, new HashMap<>());
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
try {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
@@ -84,7 +119,7 @@ public class GlobalUserPreferences{
.putBoolean("showReplies", showReplies)
.putBoolean("showBoosts", showBoosts)
.putBoolean("loadNewPosts", loadNewPosts)
.putBoolean("showFederatedTimeline", showFederatedTimeline)
.putBoolean("showNewPostsButton", showNewPostsButton)
.putBoolean("trueBlackTheme", trueBlackTheme)
.putBoolean("showInteractionCounts", showInteractionCounts)
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
@@ -92,10 +127,23 @@ public class GlobalUserPreferences{
.putBoolean("disableSwipe", disableSwipe)
.putBoolean("disableDividers", disableDividers)
.putBoolean("relocatePublishButton", relocatePublishButton)
.putBoolean("showDifferentiatedPushNoticationIcons", showDifferentiatedPushNoticationIcons)
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
.putBoolean("reduceMotion", reduceMotion)
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
.putBoolean("enableFabAutoHide", enableFabAutoHide)
.putBoolean("disableAltTextReminder", disableAltTextReminder)
.putBoolean("showAltIndicator", showAltIndicator)
.putBoolean("showNoAltIndicator", showNoAltIndicator)
.putBoolean("enablePreReleases", enablePreReleases)
.putString("publishButtonText", publishButtonText)
.putInt("theme", theme.ordinal())
.putString("color", color.name())
.putString("recentLanguages", gson.toJson(recentLanguages))
.putString("pinnedTimelines", gson.toJson(pinnedTimelines))
.putString("recentEmojis", gson.toJson(recentEmojis))
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
.apply();
}

View File

@@ -16,7 +16,8 @@ import org.joinmastodon.android.fragments.HomeFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
import org.joinmastodon.android.fragments.onboarding.CustomLoginFragment;
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
import org.joinmastodon.android.fragments.onboarding.CustomWelcomeFragment;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
@@ -33,18 +34,19 @@ public class MainActivity extends FragmentStackActivity{
if(savedInstanceState==null){
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
showFragmentClearingBackStack(new CustomLoginFragment());
showFragmentClearingBackStack(new CustomWelcomeFragment());
}else{
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
AccountSession session;
Bundle args=new Bundle();
Intent intent=getIntent();
if(intent.getBooleanExtra("fromNotification", false)){
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
boolean hasNotification = intent.hasExtra("notification");
if(fromNotification){
String accountID=intent.getStringExtra("accountID");
try{
session=AccountSessionManager.getInstance().getAccount(accountID);
if(!intent.hasExtra("notification"))
args.putString("tab", "notifications");
if(!hasNotification) args.putString("tab", "notifications");
}catch(IllegalStateException x){
session=AccountSessionManager.getInstance().getLastActiveAccount();
}
@@ -54,13 +56,13 @@ public class MainActivity extends FragmentStackActivity{
args.putString("account", session.getID());
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
fragment.setArguments(args);
showFragmentClearingBackStack(fragment);
if(intent.getBooleanExtra("fromNotification", false) && intent.hasExtra("notification")){
if(fromNotification && hasNotification){
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
showFragmentForNotification(notification, session.getID());
}else if(intent.getBooleanExtra("compose", false)){
} else if (intent.getBooleanExtra("compose", false)){
showCompose();
}else{
} else {
showFragmentClearingBackStack(fragment);
maybeRequestNotificationsPermission();
}
}
@@ -139,4 +141,30 @@ public class MainActivity extends FragmentStackActivity{
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 (this.fragmentContainers.size() == 1
&& currentArgs.getBoolean("_can_go_back", false)
&& currentArgs.containsKey("account")) {
Bundle args = new Bundle();
args.putString("account", currentArgs.getString("account"));
args.putString("tab", "notifications");
Fragment fragment=new HomeFragment();
fragment.setArguments(args);
showFragmentClearingBackStack(fragment);
} else {
super.onBackPressed();
}
}
}

View File

@@ -8,9 +8,7 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
@@ -39,6 +37,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
private static final String TAG="PushNotificationReceive";
public static final int NOTIFICATION_ID=178;
private static final int SUMMARY_ID = 791;
private static int notificationId = 0;
@Override
public void onReceive(Context context, Intent intent){
@@ -99,6 +99,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
Account self=AccountSessionManager.getInstance().getAccount(accountID).self;
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
Notification.Builder builder;
Notification.Builder summaryNotification;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
boolean hasGroup=false;
List<NotificationChannelGroup> channelGroups=nm.getNotificationChannelGroups();
@@ -121,39 +122,50 @@ public class PushNotificationReceiver extends BroadcastReceiver{
nm.createNotificationChannels(channels);
}
builder=new Notification.Builder(context, accountID+"_"+pn.notificationType);
// summaryNotification=new Notification.Builder(context, accountID);
}else{
builder=new Notification.Builder(context)
.setPriority(Notification.PRIORITY_DEFAULT)
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
summaryNotification=new Notification.Builder(context)
.setPriority(Notification.PRIORITY_DEFAULT)
.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE);
}
Drawable avatar=ImageCache.getInstance(context).get(new UrlImageLoaderRequest(pn.icon, V.dp(50), V.dp(50)));
Intent contentIntent=new Intent(context, MainActivity.class);
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
contentIntent.putExtra("fromNotification", true);
contentIntent.putExtra("accountID", accountID);
contentIntent.putExtra("notificationID", notificationId);
if(notification!=null){
contentIntent.putExtra("notification", Parcels.wrap(notification));
}
builder.setContentTitle(pn.title)
.setContentText(pn.body)
.setContentTitle(pn.title)
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
.setContentIntent(PendingIntent.getActivity(context, accountID.hashCode() & 0xFFFF, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
.setSmallIcon(R.drawable.ic_ntf_logo)
.setContentIntent(PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
.setShowWhen(true)
.setCategory(Notification.CATEGORY_SOCIAL)
.setAutoCancel(true)
.setColor(context.getColor(R.color.shortcut_icon_background));
if(GlobalUserPreferences.showDifferentiatedPushNoticationIcons){
switch (pn.notificationType) {
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
}
}else{
builder.setSmallIcon(R.drawable.ic_ntf_logo);
.setColor(context.getColor(R.color.shortcut_icon_background))
.setGroup(accountID);
if (!GlobalUserPreferences.uniformNotificationIcon) {
builder.setSmallIcon(switch (pn.notificationType) {
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
case POLL -> R.drawable.ic_fluent_poll_24_filled;
case STATUS -> R.drawable.ic_fluent_chat_24_filled;
case UPDATE -> R.drawable.ic_fluent_history_24_filled;
case REPORT -> R.drawable.ic_fluent_warning_24_filled;
case SIGN_UP -> R.drawable.ic_fluent_person_available_24_filled;
});
}
if(avatar!=null){
@@ -162,6 +174,9 @@ public class PushNotificationReceiver extends BroadcastReceiver{
if(AccountSessionManager.getInstance().getLoggedInAccounts().size()>1){
builder.setSubText(accountName);
}
nm.notify(accountID, NOTIFICATION_ID, builder.build());
notificationId++;
nm.notify(accountID, GlobalUserPreferences.keepOnlyLatestNotification ? NOTIFICATION_ID : notificationId, builder.build());
}
}

View File

@@ -12,11 +12,14 @@ import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter;
import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter;
import org.joinmastodon.android.api.session.AccountSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.time.Instant;
import java.time.LocalDate;
@@ -47,9 +50,26 @@ public class MastodonAPIController{
private static OkHttpClient httpClient=new OkHttpClient.Builder().build();
private AccountSession session;
private static List<String> badDomains = new ArrayList<>();
static{
thread.start();
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(
MastodonApp.context.getAssets().open("blocks.tsv")
));
String line;
while ((line = reader.readLine()) != null) {
if (line.isBlank() || line.startsWith("#")) continue;
String[] parts = line.replaceAll("\"", "").split("[\s,;]");
if (parts.length == 0) continue;
String domain = parts[0].toLowerCase().trim();
if (domain.isBlank()) continue;
badDomains.add(domain);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public MastodonAPIController(@Nullable AccountSession session){
@@ -57,8 +77,11 @@ public class MastodonAPIController{
}
public <T> void submitRequest(final MastodonAPIRequest<T> req){
final String host = req.getURL().getHost();
final boolean isBad = host == null || badDomains.stream().anyMatch(h -> h.equalsIgnoreCase(host) || host.toLowerCase().endsWith("." + h));
thread.postRunnable(()->{
try{
if (isBad) throw new IllegalArgumentException();
if(req.canceled)
return;
Request.Builder builder=new Request.Builder()

View File

@@ -162,6 +162,8 @@ public class PushSubscriptionManager{
@Override
public void onSuccess(PushSubscription result){
MastodonAPIController.runInBackground(()->{
result.serverKey=result.serverKey.replace('/','_');
result.serverKey=result.serverKey.replace('+','-');
serverKey=deserializeRawPublicKey(Base64.decode(result.serverKey, Base64.URL_SAFE));
AccountSession session=AccountSessionManager.getInstance().tryGetAccount(accountID);

View File

@@ -9,6 +9,7 @@ import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import java.util.HashMap;
import java.util.function.Consumer;
@@ -18,12 +19,18 @@ import me.grishka.appkit.api.ErrorResponse;
public class StatusInteractionController{
private final String accountID;
private final boolean updateCounters;
private final HashMap<String, SetStatusFavorited> runningFavoriteRequests=new HashMap<>();
private final HashMap<String, SetStatusReblogged> runningReblogRequests=new HashMap<>();
private final HashMap<String, SetStatusBookmarked> runningBookmarkRequests=new HashMap<>();
public StatusInteractionController(String accountID){
public StatusInteractionController(String accountID, boolean updateCounters) {
this.accountID=accountID;
this.updateCounters=updateCounters;
}
public StatusInteractionController(String accountID){
this(accountID, true);
}
public void setFavorited(Status status, boolean favorited, Consumer<Status> cb){
@@ -41,7 +48,7 @@ public class StatusInteractionController{
runningFavoriteRequests.remove(status.id);
result.favouritesCount = Math.max(0, status.favouritesCount) + (favorited ? 1 : -1);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -50,16 +57,16 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.favourited=!favorited;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningFavoriteRequests.put(status.id, req);
status.favourited=favorited;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
public void setReblogged(Status status, boolean reblogged, Consumer<Status> cb){
public void setReblogged(Status status, boolean reblogged, StatusPrivacy visibility, Consumer<Status> cb){
if(!Looper.getMainLooper().isCurrentThread())
throw new IllegalStateException("Can only be called from main thread");
@@ -67,7 +74,7 @@ public class StatusInteractionController{
if(current!=null){
current.cancel();
}
SetStatusReblogged req=(SetStatusReblogged) new SetStatusReblogged(status.id, reblogged)
SetStatusReblogged req=(SetStatusReblogged) new SetStatusReblogged(status.id, reblogged, visibility)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Status reblog){
@@ -75,7 +82,7 @@ public class StatusInteractionController{
runningReblogRequests.remove(status.id);
result.reblogsCount = Math.max(0, status.reblogsCount) + (reblogged ? 1 : -1);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -84,13 +91,13 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.reblogged=!reblogged;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningReblogRequests.put(status.id, req);
status.reblogged=reblogged;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
public void setBookmarked(Status status, boolean bookmarked){
@@ -111,7 +118,7 @@ public class StatusInteractionController{
public void onSuccess(Status result){
runningBookmarkRequests.remove(status.id);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -120,12 +127,12 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.bookmarked=!bookmarked;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningBookmarkRequests.put(status.id, req);
status.bookmarked=bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
}

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,10 @@
package org.joinmastodon.android.api.requests.announcements;
import org.joinmastodon.android.api.MastodonAPIRequest;
public class DismissAnnouncement extends MastodonAPIRequest<Object>{
public DismissAnnouncement(String id){
super(HttpMethod.POST, "/announcements/" + id + "/dismiss", Object.class);
setRequestBody(new Object());
}
}

View File

@@ -0,0 +1,15 @@
package org.joinmastodon.android.api.requests.announcements;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Announcement;
import java.util.List;
public class GetAnnouncements extends MastodonAPIRequest<List<Announcement>> {
public GetAnnouncements(boolean withDismissed) {
super(MastodonAPIRequest.HttpMethod.GET, "/announcements", new TypeToken<>(){});
addQueryParameter("with_dismissed", withDismissed ? "true" : "false");
}
}

View File

@@ -0,0 +1,19 @@
package org.joinmastodon.android.api.requests.lists;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.ListTimeline;
public class CreateList extends MastodonAPIRequest<ListTimeline> {
public CreateList(String title, ListTimeline.RepliesPolicy repliesPolicy) {
super(HttpMethod.POST, "/lists", ListTimeline.class);
Request req = new Request();
req.title = title;
req.repliesPolicy = repliesPolicy;
setRequestBody(req);
}
public static class Request {
public String title;
public ListTimeline.RepliesPolicy repliesPolicy;
}
}

View File

@@ -0,0 +1,10 @@
package org.joinmastodon.android.api.requests.lists;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.ListTimeline;
public class DeleteList extends MastodonAPIRequest<Object> {
public DeleteList(String id) {
super(HttpMethod.DELETE, "/lists/" + id, Object.class);
}
}

View File

@@ -0,0 +1,10 @@
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);
}
}

View File

@@ -0,0 +1,14 @@
package org.joinmastodon.android.api.requests.lists;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.ListTimeline;
public class UpdateList extends MastodonAPIRequest<ListTimeline> {
public UpdateList(String id, String title, ListTimeline.RepliesPolicy repliesPolicy) {
super(HttpMethod.PUT, "/lists/" + id, ListTimeline.class);
CreateList.Request req = new CreateList.Request();
req.title = title;
req.repliesPolicy = repliesPolicy;
setRequestBody(req);
}
}

View File

@@ -0,0 +1,17 @@
package org.joinmastodon.android.api.requests.notifications;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.ApiUtils;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Notification;
import java.util.EnumSet;
import java.util.List;
public class DismissNotification extends MastodonAPIRequest<Object>{
public DismissNotification(String id){
super(HttpMethod.POST, "/notifications/" + (id != null ? id + "/dismiss" : "clear"), Object.class);
setRequestBody(new Object());
}
}

View File

@@ -9,7 +9,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
Request r=new Request();
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
r.data.alerts=alerts;
r.data.policy=policy;
r.policy=policy;
r.subscription.keys.p256dh=encryptionKey;
r.subscription.keys.auth=authKey;
setRequestBody(r);
@@ -18,6 +18,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
private static class Request{
public Subscription subscription=new Subscription();
public Data data=new Data();
public PushSubscription.Policy policy;
private static class Keys{
public String p256dh;
@@ -31,7 +32,6 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
private static class Data{
public PushSubscription.Alerts alerts;
public PushSubscription.Policy policy;
}
}
}

View File

@@ -3,23 +3,36 @@ package org.joinmastodon.android.api.requests.notifications;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.PushSubscription;
import java.io.IOException;
import okhttp3.Response;
public class UpdatePushSettings extends MastodonAPIRequest<PushSubscription>{
private final PushSubscription.Policy policy;
public UpdatePushSettings(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
super(HttpMethod.PUT, "/push/subscription", PushSubscription.class);
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{
public Data data=new Data();
public PushSubscription.Policy policy;
public Request(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
this.data.alerts=alerts;
this.data.policy=policy;
this.policy=policy;
}
private static class Data{
public PushSubscription.Alerts alerts;
public PushSubscription.Policy policy;
}
}
}

View File

@@ -1,6 +1,7 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
@@ -9,18 +10,36 @@ import java.util.ArrayList;
import java.util.List;
public class CreateStatus extends MastodonAPIRequest<Status>{
public static final Instant DRAFTS_AFTER_INSTANT = Instant.ofEpochMilli(253370764799999L) /* end of 9998 */;
private static final float draftFactor = 31536000000f /* one year */ / 253370764799999f /* end of 9998 */;
public static Instant getDraftInstant() {
// returns an instant between 9999-01-01 00:00:00 and 9999-12-31 23:59:59
// yes, this is a weird implementation for something that hardly matters
return DRAFTS_AFTER_INSTANT.plusMillis(1 + (long) (System.currentTimeMillis() * draftFactor));
}
public CreateStatus(CreateStatus.Request req, String uuid){
super(HttpMethod.POST, "/statuses", Status.class);
setRequestBody(req);
addHeader("Idempotency-Key", uuid);
}
public static class Scheduled extends MastodonAPIRequest<ScheduledStatus>{
public Scheduled(CreateStatus.Request req, String uuid){
super(HttpMethod.POST, "/statuses", ScheduledStatus.class);
setRequestBody(req);
addHeader("Idempotency-Key", uuid);
}
}
public static class Request{
public String status;
public List<String> mediaIds;
public Poll poll;
public String inReplyToId;
public boolean sensitive;
public boolean localOnly;
public String spoilerText;
public StatusPrivacy visibility;
public Instant scheduledAt;

View File

@@ -7,4 +7,10 @@ public class DeleteStatus extends MastodonAPIRequest<Status>{
public DeleteStatus(String id){
super(HttpMethod.DELETE, "/statuses/"+id, Status.class);
}
public static class Scheduled extends MastodonAPIRequest<Object> {
public Scheduled(String id) {
super(HttpMethod.DELETE, "/scheduled_statuses/"+id, Object.class);
}
}
}

View File

@@ -0,0 +1,16 @@
package org.joinmastodon.android.api.requests.statuses;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
import org.joinmastodon.android.model.ScheduledStatus;
public class GetScheduledStatuses extends HeaderPaginationRequest<ScheduledStatus>{
public GetScheduledStatuses(String maxID, int limit){
super(HttpMethod.GET, "/scheduled_statuses", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(limit>0)
addQueryParameter("limit", limit+"");
}
}

View File

@@ -2,10 +2,17 @@ package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
public class SetStatusReblogged extends MastodonAPIRequest<Status>{
public SetStatusReblogged(String id, boolean reblogged){
public SetStatusReblogged(String id, boolean reblogged, StatusPrivacy visibility){
super(HttpMethod.POST, "/statuses/"+id+"/"+(reblogged ? "reblog" : "unreblog"), Status.class);
setRequestBody(new Object());
Request req = new Request();
req.visibility = visibility;
setRequestBody(req);
}
public static class Request {
public StatusPrivacy visibility;
}
}

View File

@@ -9,6 +9,10 @@ import org.joinmastodon.android.model.Hashtag;
import java.util.List;
public class GetFollowedHashtags extends HeaderPaginationRequest<Hashtag> {
public GetFollowedHashtags() {
this(null, null, -1, null);
}
public GetFollowedHashtags(String maxID, String minID, int limit, String sinceID){
super(HttpMethod.GET, "/followed_tags", new TypeToken<>(){});
if(maxID!=null)

View File

@@ -8,9 +8,11 @@ import org.joinmastodon.android.model.Status;
import java.util.List;
public class GetTrendingStatuses extends MastodonAPIRequest<List<Status>>{
public GetTrendingStatuses(int limit){
public GetTrendingStatuses(int offset, int limit){
super(HttpMethod.GET, "/trends/statuses", new TypeToken<>(){});
if(limit>0)
addQueryParameter("limit", ""+limit);
if(offset>0)
addQueryParameter("offset", ""+offset);
}
}

View File

@@ -32,7 +32,7 @@ public class AccountSession{
public Preferences preferences;
public AccountActivationInfo activationInfo;
private transient MastodonAPIController apiController;
private transient StatusInteractionController statusInteractionController;
private transient StatusInteractionController statusInteractionController, remoteStatusInteractionController;
private transient CacheController cacheController;
private transient PushSubscriptionManager pushSubscriptionManager;
@@ -52,6 +52,10 @@ public class AccountSession{
return domain+"_"+self.id;
}
public String getFullUsername() {
return "@"+self.username+"@"+domain;
}
public MastodonAPIController getApiController(){
if(apiController==null)
apiController=new MastodonAPIController(this);
@@ -64,6 +68,12 @@ public class AccountSession{
return statusInteractionController;
}
public StatusInteractionController getRemoteStatusInteractionController(){
if(remoteStatusInteractionController==null)
remoteStatusInteractionController=new StatusInteractionController(getID(), false);
return remoteStatusInteractionController;
}
public CacheController getCacheController(){
if(cacheController==null)
cacheController=new CacheController(getID());

View File

@@ -13,8 +13,6 @@ import android.net.Uri;
import android.os.Build;
import android.util.Log;
import com.google.gson.JsonParseException;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.E;
import org.joinmastodon.android.MainActivity;
@@ -104,12 +102,11 @@ public class AccountSessionManager{
public void addAccount(Instance instance, Token token, Account self, Application app, AccountActivationInfo activationInfo){
instances.put(instance.uri, instance);
updateInstanceInfoV2(instance);
AccountSession session=new AccountSession(token, self, app, instance.uri, activationInfo==null, activationInfo);
sessions.put(session.getID(), session);
lastActiveAccountID=session.getID();
writeAccountsFile();
updateInstanceEmojis(instance, instance.uri);
updateMoreInstanceInfo(instance, instance.uri);
if(PushSubscriptionManager.arePushNotificationsAvailable()){
session.getPushSubscriptionManager().registerAccountForPush(null);
}
@@ -328,10 +325,7 @@ public class AccountSessionManager{
@Override
public void onSuccess(Instance instance){
instances.put(domain, instance);
updateInstanceEmojis(instance, domain);
try {
updateInstanceInfoV2(instance);
} catch (Exception ignored) {}
updateMoreInstanceInfo(instance, domain);
}
@Override
@@ -342,16 +336,18 @@ public class AccountSessionManager{
.execNoAuth(domain);
}
public void updateInstanceInfoV2(Instance instance) {
public void updateMoreInstanceInfo(Instance instance, String domain) {
new GetInstance.V2().setCallback(new Callback<>() {
@Override
public void onSuccess(Instance.V2 v2) {
if (instance != null) instance.v2 = v2;
writeAccountsFile();
updateInstanceEmojis(instance, domain);
}
@Override
public void onError(ErrorResponse errorResponse) {}
public void onError(ErrorResponse errorResponse) {
updateInstanceEmojis(instance, domain);
}
}).execNoAuth(instance.uri);
}

View File

@@ -0,0 +1,11 @@
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;
}
}

View File

@@ -0,0 +1,9 @@
package org.joinmastodon.android.events;
public class ListDeletedEvent {
public final String id;
public ListDeletedEvent(String id) {
this.id = id;
}
}

View File

@@ -0,0 +1,15 @@
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;
}
}

View File

@@ -0,0 +1,13 @@
package org.joinmastodon.android.events;
import org.joinmastodon.android.model.ScheduledStatus;
public class ScheduledStatusCreatedEvent {
public final ScheduledStatus scheduledStatus;
public final String accountID;
public ScheduledStatusCreatedEvent(ScheduledStatus scheduledStatus, String accountID){
this.scheduledStatus = scheduledStatus;
this.accountID=accountID;
}
}

View File

@@ -0,0 +1,13 @@
package org.joinmastodon.android.events;
import org.joinmastodon.android.model.ScheduledStatus;
public class ScheduledStatusDeletedEvent{
public final String id;
public final String accountID;
public ScheduledStatusDeletedEvent(String id, String accountID){
this.id=id;
this.accountID=accountID;
}
}

View File

@@ -3,6 +3,10 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.TranslateAnimation;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
@@ -56,8 +60,7 @@ public class AccountTimelineFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(getActivity()==null)
return;
if(getActivity()==null) return;
onDataLoaded(result, !result.isEmpty());
}
})
@@ -67,6 +70,7 @@ public class AccountTimelineFragment extends StatusListFragment{
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
fab = ((ProfileFragment) getParentFragment()).getFab();
}
@Override

View File

@@ -0,0 +1,106 @@
package org.joinmastodon.android.fragments;
import static java.util.stream.Collectors.toList;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageButton;
import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.GetScheduledStatuses;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Announcement;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
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.UiUtils;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.PaginatedList;
import me.grishka.appkit.api.SimpleCallback;
public class AnnouncementsFragment extends BaseStatusListFragment<Announcement> {
private Instance instance;
private AccountSession session;
private List<String> unreadIDs = null;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setTitle(R.string.sk_announcements);
session = AccountSessionManager.getInstance().getAccount(accountID);
instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
loadData();
}
@Override
protected List<StatusDisplayItem> buildDisplayItems(Announcement a) {
if(TextUtils.isEmpty(a.content)) return List.of();
Account instanceUser = new Account();
instanceUser.id = instanceUser.acct = instanceUser.username = session.domain;
instanceUser.displayName = instance.title;
instanceUser.url = "https://"+session.domain+"/about";
instanceUser.avatar = instanceUser.avatarStatic = instance.thumbnail;
instanceUser.emojis = List.of();
Status fakeStatus = a.toStatus();
TextStatusDisplayItem textItem = new TextStatusDisplayItem(a.id, HtmlParser.parse(a.content, a.emojis, a.mentions, a.tags, accountID), this, fakeStatus, true);
textItem.textSelectable = true;
return List.of(
HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead),
textItem
);
}
public void onMarkAsRead(String id) {
if (unreadIDs == null) return;
unreadIDs.remove(id);
if (unreadIDs.isEmpty()) setResult(true, null);
}
@Override
protected void addAccountToKnown(Announcement s) {}
@Override
public void onItemClick(String id) {}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetAnnouncements(true)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Announcement> result){
if (getActivity() == null) return;
List<Announcement> unread = result.stream().filter(a -> !a.read).collect(toList());
List<Announcement> read = result.stream().filter(a -> a.read).collect(toList());
onDataLoaded(unread, true);
onDataLoaded(read, false);
if (unread.isEmpty()) setResult(true, null);
else unreadIDs = unread.stream().map(a -> a.id).collect(toList());
}
})
.exec(accountID);
}
}

View File

@@ -16,6 +16,8 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.animation.TranslateAnimation;
import android.widget.ImageButton;
import android.widget.Toolbar;
import org.joinmastodon.android.E;
@@ -40,6 +42,7 @@ import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.UiUtils;
@@ -71,6 +74,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
protected DisplayItemsAdapter adapter;
protected String accountID;
protected PhotoViewer currentPhotoViewer;
protected ImageButton fab;
protected int scrollDiff = 0;
protected HashMap<String, Account> knownAccounts=new HashMap<>();
protected HashMap<String, Relationship> relationships=new HashMap<>();
protected Rect tmpRect=new Rect();
@@ -273,11 +278,46 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
@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);
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
if(dy > 0){
scrollDiff = 0;
}
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
TranslateAnimation animate = new TranslateAnimation(
0,
0,
0,
fab.getHeight() * 2);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
fab.setVisibility(View.INVISIBLE);
scrollDiff = 0;
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
if (scrollDiff > 800) {
fab.setVisibility(View.VISIBLE);
TranslateAnimation animate = new TranslateAnimation(
0,
0,
fab.getHeight() * 2,
0);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
scrollDiff = 0;
} else {
scrollDiff += Math.abs(dy);
}
}
}
}
});
list.addItemDecoration(new StatusListItemDecoration());
@@ -504,6 +544,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
public void onGapClick(GapStatusDisplayItem.Holder item){}
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warningItem){
int i = warningItem.getAbsoluteAdapterPosition();
for(StatusDisplayItem item:warningItem.filteredItems){
i++;
displayItems.add(i, item);
}
displayItems.remove(warningItem.getAbsoluteAdapterPosition());
adapter.notifyItemChanged(warningItem.getAbsoluteAdapterPosition());
}
public String getAccountID(){
return accountID;
}

View File

@@ -25,6 +25,7 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if (getActivity() == null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -0,0 +1,352 @@
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.model.Hashtag;
import org.joinmastodon.android.model.HeaderPaginationList;
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.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public class EditTimelinesFragment extends BaseRecyclerFragment<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.ALL_TIMELINES.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.DEFAULT_TIMELINES), 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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -25,6 +25,7 @@ public class FavoritedStatusListFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if (getActivity() == null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -80,6 +80,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Account> result){
if (getActivity() == null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -1,6 +1,7 @@
package org.joinmastodon.android.fragments;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -11,6 +12,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.utils.UiUtils;
import me.grishka.appkit.api.SimpleCallback;
@@ -41,12 +43,19 @@ public class FollowedHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
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
protected void doLoadData(int offset, int count){
currentRequest=new GetFollowedHashtags(offset==0 ? null : nextMaxID, null, count, null)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Hashtag> result){
if (getActivity() == null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -2,6 +2,7 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.view.HapticFeedbackConstants;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -10,12 +11,16 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.tags.GetHashtag;
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
import org.joinmastodon.android.events.HashtagUpdatedEvent;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
@@ -25,7 +30,7 @@ import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.V;
public class HashtagTimelineFragment extends StatusListFragment{
public class HashtagTimelineFragment extends PinnableStatusListFragment {
private String hashtag;
private boolean following;
private ImageButton fab;
@@ -40,7 +45,6 @@ public class HashtagTimelineFragment extends StatusListFragment{
super.onAttach(activity);
updateTitle(getArguments().getString("hashtag"));
following=getArguments().getBoolean("following", false);
setHasOptionsMenu(true);
}
@@ -53,35 +57,20 @@ public class HashtagTimelineFragment extends StatusListFragment{
this.following = newFollowing;
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);
E.post(new HashtagUpdatedEvent(hashtag, following));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.hashtag_timeline, menu);
super.onCreateOptionsMenu(menu, inflater);
followButton = menu.findItem(R.id.follow_hashtag);
updateFollowingState(following);
followButton.setOnMenuItemClickListener(i -> {
updateFollowingState(!following);
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
@Override
public void onSuccess(Hashtag i) {
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);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getActivity());
updateFollowingState(!following);
}
}).exec(accountID);
return true;
});
new GetHashtag(hashtag).setCallback(new Callback<>() {
@Override
public void onSuccess(Hashtag hashtag) {
if (getActivity() == null) return;
updateTitle(hashtag.name);
updateFollowingState(hashtag.following);
}
@@ -93,12 +82,44 @@ public class HashtagTimelineFragment extends StatusListFragment{
}).exec(accountID);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (super.onOptionsItemSelected(item)) return true;
if (item.getItemId() == R.id.follow_hashtag) {
updateFollowingState(!following);
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
@Override
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();
updateFollowingState(i.following);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getActivity());
updateFollowingState(!following);
}
}).exec(accountID);
return true;
}
return false;
}
@Override
protected TimelineDefinition makeTimelineDefinition() {
return TimelineDefinition.ofHashtag(hashtag);
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if (getActivity() == null) return;
onDataLoaded(result, !result.isEmpty());
}
})
@@ -117,6 +138,7 @@ public class HashtagTimelineFragment extends StatusListFragment{
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' '));
}
private void onFabClick(View v){

View File

@@ -5,6 +5,7 @@ import android.app.NotificationManager;
import android.graphics.Outline;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -15,7 +16,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.joinmastodon.android.PushNotificationReceiver;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -41,7 +41,11 @@ import me.grishka.appkit.views.FragmentRootLinearLayout;
public class HomeFragment extends AppKitFragment implements OnBackPressedListener{
private FragmentRootLinearLayout content;
private HomeTimelineFragment homeTimelineFragment;
private HomeTabFragment homeTabFragment;
// private HomeTimelineFragment homeTimelineFragment;
private NotificationsFragment notificationsFragment;
private DiscoverFragment searchFragment;
private ProfileFragment profileFragment;
@@ -57,7 +61,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
setTitle(R.string.sk_app_name);
setTitle(R.string.mo_app_name);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);
@@ -65,8 +69,13 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
if(savedInstanceState==null){
Bundle args=new Bundle();
args.putString("account", accountID);
homeTimelineFragment=new HomeTimelineFragment();
homeTimelineFragment.setArguments(args);
homeTabFragment=new HomeTabFragment();
homeTabFragment.setArguments(args);
// homeTimelineFragment=new HomeTimelineFragment();
// homeTimelineFragment.setArguments(args);
args=new Bundle(args);
args.putBoolean("noAutoLoad", true);
searchFragment=new DiscoverFragment();
@@ -110,12 +119,19 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
if(savedInstanceState==null){
getChildFragmentManager().beginTransaction()
.add(R.id.fragment_wrap, homeTimelineFragment)
.add(R.id.fragment_wrap, homeTabFragment)
.add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
.add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
.commit();
// getChildFragmentManager().beginTransaction()
// .add(R.id.fragment_wrap, homeTimelineFragment)
// .add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
// .add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
// .add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
// .commit();
String defaultTab=getArguments().getString("tab");
if("notifications".equals(defaultTab)){
tabBar.selectTab(R.id.tab_notifications);
@@ -136,21 +152,36 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
@Override
public void onViewStateRestored(Bundle savedInstanceState){
super.onViewStateRestored(savedInstanceState);
if(savedInstanceState==null || homeTimelineFragment!=null)
return;
homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
if(savedInstanceState==null) return;
// if(savedInstanceState==null || homeTimelineFragment!=null)
// return;
homeTabFragment=(HomeTabFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTabFragment");
// homeTimelineFragment=(HomeTimelineFragment) getChildFragmentManager().getFragment(savedInstanceState, "homeTimelineFragment");
searchFragment=(DiscoverFragment) getChildFragmentManager().getFragment(savedInstanceState, "searchFragment");
notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment");
profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment");
currentTab=savedInstanceState.getInt("selectedTab");
Fragment current=fragmentForTab(currentTab);
getChildFragmentManager().beginTransaction()
.hide(homeTimelineFragment)
.hide(homeTabFragment)
.hide(searchFragment)
.hide(notificationsFragment)
.hide(profileFragment)
.show(current)
.commit();
// getChildFragmentManager().beginTransaction()
// .hide(homeTimelineFragment)
// .hide(searchFragment)
// .hide(notificationsFragment)
// .hide(profileFragment)
// .show(current)
// .commit();
maybeTriggerLoading(current);
}
@@ -180,7 +211,11 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
}
WindowInsets topOnlyInsets=insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
homeTimelineFragment.onApplyWindowInsets(topOnlyInsets);
homeTabFragment.onApplyWindowInsets(topOnlyInsets);
// homeTimelineFragment.onApplyWindowInsets(topOnlyInsets);
searchFragment.onApplyWindowInsets(topOnlyInsets);
notificationsFragment.onApplyWindowInsets(topOnlyInsets);
profileFragment.onApplyWindowInsets(topOnlyInsets);
@@ -188,7 +223,10 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
private Fragment fragmentForTab(@IdRes int tab){
if(tab==R.id.tab_home){
return homeTimelineFragment;
return homeTabFragment;
// if(tab==R.id.tab_home){
// return homeTimelineFragment;
}else if(tab==R.id.tab_search){
return searchFragment;
}else if(tab==R.id.tab_notifications){
@@ -202,6 +240,17 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
private void onTabSelected(@IdRes int tab){
Fragment newFragment=fragmentForTab(tab);
if(tab==currentTab){
if(tab == R.id.tab_search){
if(newFragment instanceof ScrollableToTop scrollable)
scrollable.scrollToTop();
searchFragment.selectSearch();
return;
}
if(newFragment instanceof ScrollableToTop scrollable)
scrollable.scrollToTop();
return;
}
if(tab==currentTab && tab == R.id.tab_search){
if(newFragment instanceof ScrollableToTop scrollable)
scrollable.scrollToTop();
return;
@@ -222,7 +271,11 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
((NotificationsFragment) newFragment).loadData();
// TODO make an interface?
NotificationManager nm=getActivity().getSystemService(NotificationManager.class);
nm.cancel(accountID, PushNotificationReceiver.NOTIFICATION_ID);
for (StatusBarNotification notification : nm.getActiveNotifications()) {
if (accountID.equals(notification.getTag())) {
nm.cancel(accountID, notification.getId());
}
}
}
}
@@ -235,6 +288,12 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
new AccountSwitcherSheet(getActivity()).show();
return true;
}
if(tab==R.id.tab_search){
onTabSelected(R.id.tab_search);
tabBar.selectTab(R.id.tab_search);
searchFragment.selectSearch();
return true;
}
return false;
}
@@ -248,17 +307,24 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
tabBar.selectTab(R.id.tab_home);
onTabSelected(R.id.tab_home);
return true;
} else {
return homeTabFragment.onBackPressed();
}
return false;
}
@Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putInt("selectedTab", currentTab);
getChildFragmentManager().putFragment(outState, "homeTimelineFragment", homeTimelineFragment);
getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
if (homeTabFragment.isAdded()) getChildFragmentManager().putFragment(outState, "homeTabFragment", homeTabFragment);
if (searchFragment.isAdded()) getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
if (notificationsFragment.isAdded()) getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
if (profileFragment.isAdded()) getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
// getChildFragmentManager().putFragment(outState, "homeTimelineFragment", homeTimelineFragment);
// getChildFragmentManager().putFragment(outState, "searchFragment", searchFragment);
// getChildFragmentManager().putFragment(outState, "notificationsFragment", notificationsFragment);
// getChildFragmentManager().putFragment(outState, "profileFragment", profileFragment);
}
}

View File

@@ -0,0 +1,683 @@
package org.joinmastodon.android.fragments;
import static org.joinmastodon.android.GlobalUserPreferences.reduceMotion;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toolbar;
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.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.announcements.GetAnnouncements;
import org.joinmastodon.android.api.requests.lists.GetLists;
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.model.Announcement;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.ListTimeline;
import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.SimpleViewHolder;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
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.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.fragments.OnBackPressedListener;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener {
private static final int ANNOUNCEMENTS_RESULT = 654;
private String accountID;
private MenuItem announcements, announcementsAction, settings, settingsAction;
// private ImageView toolbarLogo;
private Button toolbarShowNewPostsBtn;
private boolean newPostsBtnShown;
private AnimatorSet currentNewPostsAnim;
private ViewPager2 pager;
private View switcher;
private FrameLayout toolbarFrame;
private ImageView timelineIcon;
private ImageView collapsedChevron;
private TextView timelineTitle;
private PopupMenu switcherPopup;
private final Map<Integer, ListTimeline> listItems = 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;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
E.register(this);
accountID = getArguments().getString("account");
timelineDefinitions = GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES);
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
public void onAttach(Activity activity) {
super.onAttach(activity);
setHasOptionsMenu(true);
}
@Override
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
FrameLayout view = new FrameLayout(getContext());
pager = new ViewPager2(getContext());
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
if (fragments[0] == null) {
Bundle args = new Bundle();
args.putString("account", accountID);
args.putBoolean("__is_tab", true);
args.putBoolean("onlyPosts", true);
for (int i = 0; i < timelineDefinitions.size(); i++) {
TimelineDefinition tl = timelineDefinitions.get(i);
fragments[i] = tl.getFragment();
timelines[i] = tl;
}
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
for (int i = 0; i < count; i++) {
fragments[i].setArguments(timelines[i].populateArguments(new Bundle(args)));
FrameLayout tabView = new FrameLayout(getActivity());
tabView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
tabView.setVisibility(View.GONE);
tabView.setId(i + 1);
transaction.add(i + 1, fragments[i]);
view.addView(tabView);
tabViews[i] = tabView;
}
transaction.commit();
}
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;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
timelineIcon = toolbarFrame.findViewById(R.id.timeline_icon);
timelineTitle = toolbarFrame.findViewById(R.id.timeline_title);
collapsedChevron = toolbarFrame.findViewById(R.id.collapsed_chevron);
switcher = toolbarFrame.findViewById(R.id.switcher_btn);
switcherPopup = new PopupMenu(getContext(), switcher);
switcherPopup.setOnMenuItemClickListener(this::onSwitcherItemSelected);
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
switcher.setOnClickListener(v->switcherPopup.show());
switcher.setOnTouchListener(switcherPopup.getDragToOpenListener());
updateSwitcherMenu();
UiUtils.reduceSwipeSensitivity(pager);
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
pager.setAdapter(new HomePagerAdapter());
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
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);
if (!timelines[position].equals(TimelineDefinition.HOME_TIMELINE)) hideNewPostsButton();
if (fragments[position] instanceof BaseRecyclerFragment<?> page){
if(!page.loaded && !page.isDataLoading()) page.loadData();
}
}
});
if (!reduceMotion) {
pager.setPageTransformer((v, pos) -> {
if (reduceMotion || tabViews[pager.getCurrentItem()] != v) return;
float scaleFactor = Math.max(0.85f, 1 - Math.abs(pos) * 0.06f);
switcher.setScaleY(scaleFactor);
switcher.setScaleX(scaleFactor);
switcher.setAlpha(Math.max(0.65f, 1 - Math.abs(pos)));
});
}
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()){
updateUpdateState(GithubSelfUpdater.getInstance().getState());
}
new GetLists().setCallback(new Callback<>() {
@Override
public void onSuccess(List<ListTimeline> lists) {
updateList(lists, listItems);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getContext());
}
}).exec(accountID);
new GetFollowedHashtags().setCallback(new Callback<>() {
@Override
public void onSuccess(HeaderPaginationList<Hashtag> hashtags) {
updateList(hashtags, hashtagsItems);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getContext());
}
}).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 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(){
Toolbar toolbar = getToolbar();
ViewParent parentView = toolbarFrame.getParent();
if (parentView == toolbar) return;
if (parentView instanceof Toolbar parentToolbar) parentToolbar.removeView(toolbarFrame);
toolbar.addView(toolbarFrame, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
toolbar.setOnClickListener(v->scrollToTop());
toolbar.setNavigationContentDescription(R.string.back);
toolbar.setContentInsetsAbsolute(0, toolbar.getContentInsetRight());
updateSwitcherIcon(pager.getCurrentItem());
toolbarShowNewPostsBtn=toolbarFrame.findViewById(R.id.show_new_posts_btn);
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
toolbarShowNewPostsBtn.setOnClickListener(this::onNewPostsBtnClick);
if(newPostsBtnShown){
toolbarShowNewPostsBtn.setVisibility(View.VISIBLE);
collapsedChevron.setVisibility(View.VISIBLE);
collapsedChevron.setAlpha(1f);
timelineTitle.setVisibility(View.GONE);
timelineTitle.setAlpha(0f);
}else{
toolbarShowNewPostsBtn.setVisibility(View.INVISIBLE);
toolbarShowNewPostsBtn.setAlpha(0f);
collapsedChevron.setVisibility(View.GONE);
collapsedChevron.setAlpha(0f);
toolbarShowNewPostsBtn.setScaleX(.8f);
toolbarShowNewPostsBtn.setScaleY(.8f);
timelineTitle.setVisibility(View.VISIBLE);
}
}
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) {
m.setGroupDividerEnabled(true);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.home, menu);
menu.findItem(R.id.overflow).setActionView(overflowActionView);
announcementsAction = menu.findItem(R.id.announcements_action);
settingsAction = menu.findItem(R.id.settings_action);
updateOverflowMenu();
}
private <T> void updateList(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));
updateOverflowMenu();
}
private void updateSwitcherMenu() {
Menu switcherMenu = switcherPopup.getMenu();
switcherMenu.clear();
timelinesByMenuItem.clear();
for (TimelineDefinition tl : timelines) {
int menuItemId = View.generateViewId();
timelinesByMenuItem.put(menuItemId, tl);
MenuItem item = switcherMenu.add(0, menuItemId, 0, tl.getTitle(getContext()));
item.setIcon(tl.getIcon().iconRes);
}
UiUtils.enablePopupMenuIcons(getContext(), switcherPopup);
}
private boolean onSwitcherItemSelected(MenuItem item) {
int id = item.getItemId();
Bundle args = new Bundle();
args.putString("account", accountID);
if (id == R.id.menu_back) {
switcher.post(() -> switcherPopup.show());
return true;
}
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;
}
private void navigateTo(int i) {
navigateTo(i, !reduceMotion);
}
private void navigateTo(int i, boolean smooth) {
pager.setCurrentItem(i, smooth);
updateSwitcherIcon(i);
}
private void updateSwitcherIcon(int i) {
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
timelineTitle.setText(timelines[i].getTitle(getContext()));
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
Bundle args=new Bundle();
args.putString("account", accountID);
int id = item.getItemId();
ListTimeline list;
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);
} 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;
}
@Override
public void scrollToTop(){
((ScrollableToTop) fragments[pager.getCurrentItem()]).scrollToTop();
}
public void hideNewPostsButton(){
if(!newPostsBtnShown)
return;
newPostsBtnShown=false;
if(currentNewPostsAnim!=null){
currentNewPostsAnim.cancel();
}
timelineTitle.setVisibility(View.VISIBLE);
AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(timelineTitle, View.ALPHA, 1f),
ObjectAnimator.ofFloat(timelineTitle, View.SCALE_X, 1f),
ObjectAnimator.ofFloat(timelineTitle, View.SCALE_Y, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.ALPHA, 0f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_X, .8f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f),
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 0f)
);
set.setDuration(reduceMotion ? 0 : 300);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
toolbarShowNewPostsBtn.setVisibility(View.INVISIBLE);
collapsedChevron.setVisibility(View.GONE);
currentNewPostsAnim=null;
}
});
currentNewPostsAnim=set;
set.start();
}
public void showNewPostsButton(){
if(newPostsBtnShown || pager == null || !timelines[pager.getCurrentItem()].equals(TimelineDefinition.HOME_TIMELINE))
return;
newPostsBtnShown=true;
if(currentNewPostsAnim!=null){
currentNewPostsAnim.cancel();
}
toolbarShowNewPostsBtn.setVisibility(View.VISIBLE);
collapsedChevron.setVisibility(View.VISIBLE);
AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(timelineTitle, View.ALPHA, 0f),
ObjectAnimator.ofFloat(timelineTitle, View.SCALE_X, .8f),
ObjectAnimator.ofFloat(timelineTitle, View.SCALE_Y, .8f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.ALPHA, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_X, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f),
ObjectAnimator.ofFloat(collapsedChevron, View.ALPHA, 1f)
);
set.setDuration(reduceMotion ? 0 : 300);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
timelineTitle.setVisibility(View.GONE);
currentNewPostsAnim=null;
}
});
currentNewPostsAnim=set;
set.start();
}
public boolean isNewPostsBtnShown() {
return newPostsBtnShown;
}
private void onNewPostsBtnClick(View view) {
if(newPostsBtnShown){
hideNewPostsButton();
scrollToTop();
}
}
@Override
public void onFragmentResult(int reqCode, boolean success, Bundle result){
if (reqCode == ANNOUNCEMENTS_RESULT && success) {
announcementsBadged = false;
announcements.setVisible(true);
announcementsAction.setVisible(false);
}
}
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING) {
settingsBadged = true;
settingsAction.setVisible(true);
settings.setVisible(false);
}
}
@Subscribe
public void onSelfUpdateStateChanged(SelfUpdateStateChangedEvent ev){
updateUpdateState(ev.state);
}
@Override
public boolean onBackPressed(){
if(pager.getCurrentItem() > 0){
navigateTo(0);
return true;
}
return false;
}
@Override
public void onDestroyView(){
super.onDestroyView();
if (overflowPopup != null) {
overflowPopup.dismiss();
overflowPopup = null;
}
if (switcherPopup != null) {
switcherPopup.dismiss();
switcherPopup = null;
}
if(GithubSelfUpdater.needSelfUpdating()){
E.unregister(this);
}
}
@Override
protected void onShown() {
super.onShown();
Object pinnedTimelines = GlobalUserPreferences.pinnedTimelines.get(accountID);
if (pinnedTimelines != null && timelineDefinitions != pinnedTimelines) UiUtils.restartApp();
}
@Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState == null) return;
navigateTo(savedInstanceState.getInt("selectedTab"), false);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
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();
}
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
@NonNull
@Override
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
FrameLayout tabView = tabViews[viewType % getItemCount()];
((ViewGroup)tabView.getParent()).removeView(tabView);
tabView.setVisibility(View.VISIBLE);
return new SimpleViewHolder(tabView);
}
@Override
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){}
@Override
public int getItemCount(){
return count;
}
@Override
public int getItemViewType(int position){
return position;
}
}
}

View File

@@ -1,82 +1,51 @@
package org.joinmastodon.android.fragments;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toolbar;
import com.squareup.otto.Subscribe;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
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.ui.displayitems.GapStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class HomeTimelineFragment extends StatusListFragment{
private ImageButton fab;
private ImageView toolbarLogo;
private Button toolbarShowNewPostsBtn;
private boolean newPostsBtnShown;
private AnimatorSet currentNewPostsAnim;
public class HomeTimelineFragment extends FabStatusListFragment {
private HomeTabFragment parent;
private String maxID;
public HomeTimelineFragment(){
setListLayoutId(R.layout.recycler_fragment_with_fab);
}
private String lastSavedMarkerID;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setHasOptionsMenu(true);
if (getParentFragment() instanceof HomeTabFragment home) parent = home;
loadData();
}
private List<Status> filterPosts(List<Status> items) {
// This is the function I must use to solve the filters thing for real
return items.stream().filter(i ->
(GlobalUserPreferences.showReplies || i.inReplyToId == null) &&
(GlobalUserPreferences.showBoosts || i.reblog == null)
@@ -90,8 +59,7 @@ public class HomeTimelineFragment extends StatusListFragment{
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
@Override
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
if(getActivity()==null)
return;
if (getActivity() == null) return;
List<Status> filteredItems = filterPosts(result.items);
onDataLoaded(filteredItems, !result.items.isEmpty());
maxID=result.maxID;
@@ -104,41 +72,15 @@ public class HomeTimelineFragment extends StatusListFragment{
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setOnClickListener(this::onFabClick);
updateToolbarLogo();
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
if(newPostsBtnShown && list.getChildAdapterPosition(list.getChildAt(0))<=getMainAdapterOffset()){
hideNewPostsButton();
if(parent != null && parent.isNewPostsBtnShown() && list.getChildAdapterPosition(list.getChildAt(0))<=getMainAdapterOffset()){
parent.hideNewPostsButton();
}
}
});
if(GithubSelfUpdater.needSelfUpdating()){
E.register(this);
updateUpdateState(GithubSelfUpdater.getInstance().getState());
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.home, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), SettingsFragment.class, args);
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
updateToolbarLogo();
}
@Override
@@ -153,14 +95,31 @@ public class HomeTimelineFragment extends StatusListFragment{
}
}
public void onStatusCreated(StatusCreatedEvent ev){
prependItems(Collections.singletonList(ev.status), true);
@Override
protected void onHidden(){
super.onHidden();
// if(!data.isEmpty()){
// String topPostID=displayItems.get(list.getChildAdapterPosition(list.getChildAt(0))-getMainAdapterOffset()).parentID;
// if(!topPostID.equals(lastSavedMarkerID)){
// lastSavedMarkerID=topPostID;
// new SaveMarkers(topPostID, null)
// .setCallback(new Callback<>(){
// @Override
// public void onSuccess(SaveMarkers.Response result){
// }
//
// @Override
// public void onError(ErrorResponse error){
// lastSavedMarkerID=null;
// }
// })
// .exec(accountID);
// }
// }
}
private void onFabClick(View v){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), ComposeFragment.class, args);
public void onStatusCreated(StatusCreatedEvent ev){
prependItems(Collections.singletonList(ev.status), true);
}
private void loadNewPosts(){
@@ -187,13 +146,11 @@ public class HomeTimelineFragment extends StatusListFragment{
result.get(result.size()-1).hasGapAfter=true;
toAdd=result;
}
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList());
if(!filters.isEmpty()){
toAdd=toAdd.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList());
}
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList());
if(!toAdd.isEmpty()){
prependItems(toAdd, true);
showNewPostsButton();
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
}
}
@@ -264,18 +221,14 @@ public class HomeTimelineFragment extends StatusListFragment{
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
targetList.clear();
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList());
outer:
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
for(Status s:result){
if(idsBelowGap.contains(s.id))
break;
for(Filter filter:filters){
if(filter.matches(s)){
continue outer;
}
if(filterPredicate.test(s)){
targetList.addAll(buildDisplayItems(s));
insertedPosts.add(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.
@@ -313,132 +266,10 @@ public class HomeTimelineFragment extends StatusListFragment{
currentRequest=null;
dataLoading=false;
}
if (parent != null) parent.hideNewPostsButton();
super.onRefresh();
}
private void updateToolbarLogo(){
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)));
// toolbarLogo =new TextView(getActivity());
// toolbarLogo.setText(getString(R.string.app_name).toLowerCase(Locale.getDefault()));
// toolbarLogo.setTextAppearance(R.style.app_title);
toolbarShowNewPostsBtn=new Button(getActivity());
toolbarShowNewPostsBtn.setTextAppearance(R.style.m3_title_medium);
toolbarShowNewPostsBtn.setTextColor(0xffffffff);
toolbarShowNewPostsBtn.setStateListAnimator(null);
toolbarShowNewPostsBtn.setBackgroundResource(R.drawable.bg_button_new_posts);
toolbarShowNewPostsBtn.setText(R.string.see_new_posts);
toolbarShowNewPostsBtn.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_fluent_arrow_up_16_filled, 0, 0, 0);
toolbarShowNewPostsBtn.setCompoundDrawableTintList(toolbarShowNewPostsBtn.getTextColors());
toolbarShowNewPostsBtn.setCompoundDrawablePadding(V.dp(8));
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(toolbarShowNewPostsBtn);
toolbarShowNewPostsBtn.setOnClickListener(this::onNewPostsBtnClick);
if(newPostsBtnShown){
toolbarShowNewPostsBtn.setVisibility(View.VISIBLE);
toolbarLogo.setVisibility(View.INVISIBLE);
toolbarLogo.setAlpha(0f);
}else{
toolbarShowNewPostsBtn.setVisibility(View.INVISIBLE);
toolbarShowNewPostsBtn.setAlpha(0f);
toolbarShowNewPostsBtn.setScaleX(.8f);
toolbarShowNewPostsBtn.setScaleY(.8f);
toolbarLogo.setVisibility(View.VISIBLE);
}
FrameLayout logoWrap=new FrameLayout(getActivity());
logoWrap.addView(toolbarLogo, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
logoWrap.addView(toolbarShowNewPostsBtn, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, V.dp(32), Gravity.CENTER));
Toolbar toolbar=getToolbar();
toolbar.addView(logoWrap, new Toolbar.LayoutParams(Gravity.CENTER));
}
private void showNewPostsButton(){
if(newPostsBtnShown)
return;
newPostsBtnShown=true;
if(currentNewPostsAnim!=null){
currentNewPostsAnim.cancel();
}
toolbarShowNewPostsBtn.setVisibility(View.VISIBLE);
AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(toolbarLogo, View.ALPHA, 0f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.ALPHA, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_X, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, 1f)
);
set.setDuration(300);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
toolbarLogo.setVisibility(View.INVISIBLE);
currentNewPostsAnim=null;
}
});
currentNewPostsAnim=set;
set.start();
}
private void hideNewPostsButton(){
if(!newPostsBtnShown)
return;
newPostsBtnShown=false;
if(currentNewPostsAnim!=null){
currentNewPostsAnim.cancel();
}
toolbarLogo.setVisibility(View.VISIBLE);
AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(toolbarLogo, View.ALPHA, 1f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.ALPHA, 0f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_X, .8f),
ObjectAnimator.ofFloat(toolbarShowNewPostsBtn, View.SCALE_Y, .8f)
);
set.setDuration(300);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
toolbarShowNewPostsBtn.setVisibility(View.INVISIBLE);
currentNewPostsAnim=null;
}
});
currentNewPostsAnim=set;
set.start();
}
private void onNewPostsBtnClick(View v){
if(newPostsBtnShown){
hideNewPostsButton();
scrollToTop();
}
}
@Override
public void onDestroyView(){
super.onDestroyView();
if(GithubSelfUpdater.needSelfUpdating()){
E.unregister(this);
}
}
private void updateUpdateState(GithubSelfUpdater.UpdateState state){
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING)
getToolbar().getMenu().findItem(R.id.settings).setIcon(R.drawable.ic_settings_24_badged);
}
@Subscribe
public void onSelfUpdateStateChanged(SelfUpdateStateChangedEvent ev){
updateUpdateState(ev.state);
}
@Override
protected boolean shouldRemoveAccountPostsWhenUnfollowing(){
return true;

View File

@@ -0,0 +1,13 @@
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);
}
}

View File

@@ -1,28 +1,44 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.media.MediaRouter;
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.ImageButton;
import androidx.annotation.Nullable;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.lists.GetList;
import org.joinmastodon.android.api.requests.lists.UpdateList;
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.ListTimeline;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ListTimelineEditor;
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.V;
public class ListTimelineFragment extends StatusListFragment {
public class ListTimelineFragment extends PinnableStatusListFragment {
private String listID;
private String listTitle;
@Nullable
private ListTimeline.RepliesPolicy repliesPolicy;
private ImageButton fab;
public ListTimelineFragment() {
@@ -32,17 +48,83 @@ public class ListTimelineFragment extends StatusListFragment {
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
listID=getArguments().getString("listID");
listTitle=getArguments().getString("listTitle");
Bundle args = getArguments();
listID = args.getString("listID");
listTitle = args.getString("listTitle");
repliesPolicy = ListTimeline.RepliesPolicy.values()[args.getInt("repliesPolicy", 0)];
setTitle(listTitle);
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
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.list, menu);
super.onCreateOptionsMenu(menu, inflater);
// TODO: implement edit, delete
// inflater.inflate(R.menu.list, menu);
UiUtils.enableOptionsMenuIcons(getContext(), menu, R.id.pin);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (super.onOptionsItemSelected(item)) return true;
if (item.getItemId() == R.id.edit) {
ListTimelineEditor editor = new ListTimelineEditor(getContext());
editor.applyList(listTitle, repliesPolicy);
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.sk_edit_list_title)
.setIcon(R.drawable.ic_fluent_people_28_regular)
.setView(editor)
.setPositiveButton(R.string.save, (d, which) -> {
String newTitle = editor.getTitle().trim();
setTitle(newTitle);
new UpdateList(listID, newTitle, editor.getRepliesPolicy()).setCallback(new Callback<>() {
@Override
public void onSuccess(ListTimeline list) {
if (getActivity() == null) return;
setTitle(list.title);
listTitle = list.title;
repliesPolicy = list.repliesPolicy;
E.post(new ListUpdatedCreatedEvent(listID, listTitle, repliesPolicy));
}
@Override
public void onError(ErrorResponse error) {
setTitle(listTitle);
error.showToast(getContext());
}
}).exec(accountID);
})
.setNegativeButton(R.string.cancel, (d, which) -> {})
.show();
} else if (item.getItemId() == R.id.delete) {
UiUtils.confirmDeleteList(getActivity(), accountID, listID, listTitle, () -> {
E.post(new ListDeletedEvent(listID));
Nav.finish(this);
});
}
return true;
}
@Override
protected TimelineDefinition makeTimelineDefinition() {
return TimelineDefinition.ofList(listID, listTitle);
}
@Override
@@ -51,6 +133,7 @@ public class ListTimelineFragment extends StatusListFragment {
.setCallback(new SimpleCallback<>(this) {
@Override
public void onSuccess(List<Status> result) {
if (getActivity() == null) return;
onDataLoaded(result, !result.isEmpty());
}
})
@@ -69,6 +152,7 @@ public class ListTimelineFragment extends StatusListFragment {
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){

View File

@@ -1,6 +1,9 @@
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;
@@ -9,173 +12,249 @@ 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.utils.UiUtils;
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 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 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 ListTimelinesFragment() {
super(10);
}
public ListTimelinesFragment() {
super(10);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args=getArguments();
accountId=args.getString("account");
@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");
profileDisplayUsername=args.getString("profileDisplayUsername");
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
// setHasOptionsMenu(true);
}
}
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
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
// @Override
// public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Button saveButton=new Button(getActivity());
// saveButton.setText(R.string.save);
// saveButton.setOnClickListener(this::onSaveClick);
// LinearLayout wrap=new LinearLayout(getActivity());
// wrap.setOrientation(LinearLayout.HORIZONTAL);
// wrap.addView(saveButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
// wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
// wrap.setClipToPadding(false);
// MenuItem item=menu.add(R.string.save);
// item.setActionView(wrap);
// item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
// }
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 0.5f, 56, 16));
}
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
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_list, menu);
}
@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;
@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);
E.post(new ListUpdatedCreatedEvent(list.id, list.title, list.repliesPolicy));
}
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 onError(ErrorResponse error) {
error.showToast(getContext());
}
}).exec(accountId)
)
.setNegativeButton(R.string.cancel, (d, which) -> {})
.show();
}
return true;
}
@Override
protected RecyclerView.Adapter getAdapter() {
return new ListsAdapter();
}
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 scrollToTop() {
smoothScrollRecyclerViewToTop(list);
}
@Override
public void onError(ErrorResponse error) {
error.showToast(getContext());
}
}).exec(accountId);
}
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
@NonNull
@Override
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new ListViewHolder();
}
@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;
@Override
public void onBindViewHolder(@NonNull ListViewHolder holder, int position) {
holder.bind(data.get(position));
}
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListTimelinesFragment.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);
}
@Override
public int getItemCount() {
return data.size();
}
}
@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;
}
}
}
private class ListViewHolder extends BindableViewHolder<ListTimeline> implements UsableRecyclerView.Clickable{
private final TextView title;
private final CheckBox listToggle;
@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;
}
}
}
public ListViewHolder(){
super(getActivity(), R.layout.item_text, list);
title=findViewById(R.id.title);
listToggle=findViewById(R.id.list_toggle);
}
@Override
protected RecyclerView.Adapter<ListViewHolder> getAdapter() {
return adapter = new ListsAdapter();
}
@Override
public void onBind(ListTimeline item) {
title.setText(item.title);
title.setCompoundDrawablesRelativeWithIntrinsicBounds(itemView.getContext().getDrawable(R.drawable.ic_fluent_people_community_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);
}
}
@Override
public void scrollToTop() {
smoothScrollRecyclerViewToTop(list);
}
private void onClickToggle(View view) {
saveListMembership(item.id, listToggle.isChecked());
}
private class ListsAdapter extends RecyclerView.Adapter<ListViewHolder>{
@NonNull
@Override
public ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new ListViewHolder();
}
@Override
public void onClick() {
UiUtils.openListTimeline(getActivity(), accountId, item);
}
}
@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);
}
}
}

View File

@@ -74,15 +74,26 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.notifications, menu);
menu.findItem(R.id.clear_notifications).setVisible(GlobalUserPreferences.enableDeleteNotifications);
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.follow_requests);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() != R.id.follow_requests) return false;
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FollowRequestsListFragment.class, args);
return true;
if (item.getItemId() == R.id.follow_requests) {
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FollowRequestsListFragment.class, args);
return true;
} else if (item.getItemId() == R.id.clear_notifications) {
UiUtils.confirmDeleteNotification(getActivity(), accountID, null, ()->{
for (int i = 0; i < tabViews.length; i++) {
getFragmentForPage(i).reload();
}
});
return true;
}
return false;
}
@Override
@@ -91,6 +102,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
tabLayout=view.findViewById(R.id.tabbar);
pager=view.findViewById(R.id.pager);
UiUtils.reduceSwipeSensitivity(pager);
tabViews=new FrameLayout[3];
for(int i=0;i<tabViews.length;i++){
@@ -108,6 +120,18 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
tabLayout.setTabTextSize(V.dp(16));
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {
scrollToTop();
}
});
pager.setOffscreenPageLimit(4);
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
@@ -171,6 +195,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
@Override
public void onSuccess(HeaderPaginationList<Account> accounts) {
if (getActivity() == null) return;
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
}
@@ -199,6 +224,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
protected void updateToolbar(){
super.updateToolbar();
getToolbar().setOutlineProvider(null);
getToolbar().setOnClickListener(v->scrollToTop());
}
private NotificationsListFragment getFragmentForPage(int page){

View File

@@ -2,6 +2,7 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import com.squareup.otto.Subscribe;
@@ -10,17 +11,22 @@ import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.NotificationDeletedEvent;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.util.ArrayList;
@@ -40,6 +46,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
private boolean onlyMentions;
private boolean onlyPosts;
private String maxID;
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
@Override
public void onCreate(Bundle savedInstanceState){
@@ -70,6 +77,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
@Override
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
n.report.targetAccount;
String extraText=switch(n.type){
case FOLLOW -> getString(R.string.user_followed_you);
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
@@ -77,10 +86,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
case REBLOG -> getString(R.string.notification_boosted);
case FAVORITE -> getString(R.string.user_favorited);
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);
};
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText) : null;
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
if(n.status!=null){
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null);
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, Filter.FilterContext.NOTIFICATIONS);
if(titleItem!=null){
for(StatusDisplayItem item:items){
if(item instanceof ImageStatusDisplayItem imgItem){
@@ -92,8 +104,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
items.add(0, titleItem);
return items;
}else if(titleItem!=null){
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this, n.account, n);
return Arrays.asList(titleItem, card);
AccountCardStatusDisplayItem card=new AccountCardStatusDisplayItem(n.id, this,
reportTarget != null ? reportTarget : n.account, n);
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{
return Collections.emptyList();
}
@@ -114,8 +131,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
.getNotifications(offset>0 ? maxID : null, count, onlyMentions, onlyPosts, refreshing, new SimpleCallback<>(this){
@Override
public void onSuccess(PaginatedResponse<List<Notification>> result){
if(getActivity()==null)
return;
if (getActivity() == null) return;
if(refreshing)
relationships.clear();
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
@@ -162,6 +178,9 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
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{
Bundle args=new Bundle();
args.putString("account", accountID);
@@ -174,6 +193,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
list.addItemDecoration(new InsetStatusItemDecoration(this));
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
}
private Notification getNotificationByID(String id){
@@ -210,7 +230,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
}
}
private void removeNotification(Notification n){
public void removeNotification(Notification n){
data.remove(n);
preloadedData.remove(n);
int index=-1;
@@ -230,4 +250,4 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
}
}

View File

@@ -0,0 +1,82 @@
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 boolean pinnedUpdated;
protected List<TimelineDefinition> pinnedTimelines;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pinnedTimelines = new ArrayList<>(GlobalUserPreferences.pinnedTimelines.getOrDefault(accountID, TimelineDefinition.DEFAULT_TIMELINES));
}
@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) {
pinnedUpdated = 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);
}
protected Bundle getResultArgs() {
return new Bundle();
}
@Override
public void onDestroy() {
super.onDestroy();
Bundle resultArgs = getResultArgs();
if (pinnedUpdated) {
resultArgs.putBoolean("pinnedUpdated", true);
setResult(true, resultArgs);
}
}
}

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.app.Fragment;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -12,11 +13,23 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
import org.joinmastodon.android.model.AccountField;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
@@ -26,11 +39,8 @@ import org.joinmastodon.android.ui.views.LinkedTextView;
import java.util.Collections;
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 me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.WindowInsetsAwareFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
@@ -46,6 +56,11 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
private static final int MAX_FIELDS=4;
public UsableRecyclerView list;
public FrameLayout noteWrap;
public EditText noteEdit;
private String accountID;
private String profileAccountID;
private String note;
private List<AccountField> fields=Collections.emptyList();
private AboutAdapter adapter;
private Paint dividerPaint=new Paint();
@@ -64,11 +79,49 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
adapter.notifyDataSetChanged();
}
public void setNote(String note, String accountID, String profileAccountID){
this.note=note;
this.accountID=accountID;
this.profileAccountID=profileAccountID;
// noteWrap.setVisibility(View.VISIBLE);
// noteEdit.setVisibility(View.VISIBLE);
// noteEdit.setText(note);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
list=new UsableRecyclerView(getActivity());
list.setId(R.id.list);
View view = inflater.inflate(R.layout.fragment_profile_about, null);
noteEdit = view.findViewById(R.id.note_edit);
noteWrap = view.findViewById(R.id.note_edit_wrap);
ImageButton noteEditConfirm = view.findViewById(R.id.note_edit_confirm);
noteEdit.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
noteEditConfirm.setVisibility(View.VISIBLE);
noteEditConfirm.animate()
.alpha(1.0f)
.setDuration(700);
} else {
noteEditConfirm.animate()
.alpha(0.0f)
.setDuration(700);
noteEditConfirm.setVisibility(View.INVISIBLE);
}
});
noteEditConfirm.setOnClickListener((v -> {
if (!noteEdit.getText().toString().trim().equals(note)) {
savePrivateNote();
}
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(this.getView().getRootView().getWindowToken(), 0);
noteEdit.clearFocus();
}));
list = view.findViewById(R.id.list);
list.setItemAnimator(new BetterItemAnimator());
list.setDrawSelectorOnTop(true);
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.mo_personal_note_update_failed), Toast.LENGTH_LONG).show();
}
}).exec(accountID);
}
public void enterEditMode(List<AccountField> editableFields){
isInEditMode=true;

View File

@@ -1,16 +1,11 @@
package org.joinmastodon.android.fragments;
import static android.content.Context.CLIPBOARD_SERVICE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.Fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Outline;
@@ -19,8 +14,6 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ImageSpan;
@@ -34,17 +27,24 @@ import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager2.widget.ViewPager2;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountByID;
@@ -52,6 +52,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.api.requests.accounts.SetPrivateNote;
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
@@ -83,10 +84,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager2.widget.ViewPager2;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -123,6 +120,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private TabLayoutMediator tabLayoutMediator;
private TextView followsYouView;
public FrameLayout noteWrap;
public EditText noteEdit;
private String note;
private Account account;
private String accountID;
private Relationship relationship;
@@ -134,10 +134,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private Uri editNewAvatar, editNewCover;
private String profileAccountID;
private boolean refreshing;
private View fab;
private ImageButton fab;
private WindowInsets childInsets;
private PhotoViewer currentPhotoViewer;
private boolean editModeLoading;
private boolean isScrollingUp = false;
public ProfileFragment(){
super(R.layout.loader_fragment_overlay_toolbar);
@@ -164,6 +165,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
}
private String getPrefilledText() {
return account == null || AccountSessionManager.getInstance().isSelf(accountID, account)
? null : '@'+account.acct+' ';
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
@@ -186,6 +192,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
followingCount=content.findViewById(R.id.following_count);
followingLabel=content.findViewById(R.id.following_label);
followingBtn=content.findViewById(R.id.following_btn);
postsCount=content.findViewById(R.id.posts_count);
postsLabel=content.findViewById(R.id.posts_label);
postsBtn=content.findViewById(R.id.posts_btn);
@@ -202,6 +209,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
fab=content.findViewById(R.id.fab);
followsYouView=content.findViewById(R.id.follows_you);
noteEdit = content.findViewById(R.id.note_edit);
noteWrap = content.findViewById(R.id.note_edit_wrap);
Button noteEditConfirm = content.findViewById(R.id.note_edit_confirm);
avatar.setOutlineProvider(new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
@@ -210,6 +221,49 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
});
avatar.setClipToOutline(true);
noteEdit.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
fab.setVisibility(View.INVISIBLE);
TranslateAnimation animate = new TranslateAnimation(
0,
0,
0,
fab.getHeight() * 2);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
noteEditConfirm.setVisibility(View.VISIBLE);
noteEditConfirm.animate()
.alpha(1.0f)
.setDuration(700);
} else {
fab.setVisibility(View.VISIBLE);
TranslateAnimation animate = new TranslateAnimation(
0,
0,
fab.getHeight() * 2,
0);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
noteEditConfirm.animate()
.alpha(0.0f)
.setDuration(700);
noteEditConfirm.setVisibility(View.INVISIBLE);
}
});
noteEditConfirm.setOnClickListener((v -> {
if (!noteEdit.getText().toString().trim().equals(note)) {
savePrivateNote();
}
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(this.getView().getRootView().getWindowToken(), 0);
noteEdit.clearFocus();
}));
FrameLayout sizeWrapper=new FrameLayout(getActivity()){
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
@@ -236,6 +290,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
tabViews[i]=tabView;
}
UiUtils.reduceSwipeSensitivity(pager);
pager.setOffscreenPageLimit(5);
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
pager.setAdapter(new ProfilePagerAdapter());
@@ -275,6 +330,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
cover.setOnClickListener(this::onCoverClick);
refreshLayout.setOnRefreshListener(this);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v->UiUtils.pickAccountForCompose(getActivity(), accountID, getPrefilledText()));
if(loaded){
bindHeaderView();
@@ -288,23 +344,43 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
username.setOnLongClickListener(v->{
String username=account.acct;
if(!username.contains("@")){
username+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
String usernameString=account.acct;
if(!usernameString.contains("@")){
usernameString+="@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
}
UiUtils.copyText(getActivity(), '@'+username);
UiUtils.copyText(username, '@'+usernameString);
return true;
});
return sizeWrapper;
}
public void setNote(String note){
this.note=note;
noteWrap.setVisibility(View.VISIBLE);
noteEdit.setVisibility(View.VISIBLE);
noteEdit.setText(note);
}
private void savePrivateNote(){
new SetPrivateNote(profileAccountID, noteEdit.getText().toString()).setCallback(new Callback<>() {
@Override
public void onSuccess(Relationship result) {}
@Override
public void onError(ErrorResponse result) {
Toast.makeText(getActivity(), getString(R.string.mo_personal_note_update_failed), Toast.LENGTH_LONG).show();
}
}).exec(accountID);
}
@Override
protected void doLoadData(){
currentRequest=new GetAccountByID(profileAccountID)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(Account result){
if (getActivity() == null) return;
account=result;
isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account);
bindHeaderView();
@@ -446,6 +522,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
if(account.locked){
ssb=new SpannableStringBuilder("@");
ssb.append(account.acct);
@@ -459,6 +536,19 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
lock.setTint(username.getCurrentTextColor());
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
username.setText(ssb);
}else if(account.bot){
ssb=new SpannableStringBuilder("@");
ssb.append(account.acct);
if(isSelf){
ssb.append('@');
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
}
ssb.append(" ");
Drawable botIcon=username.getResources().getDrawable(R.drawable.ic_bot, getActivity().getTheme()).mutate();
botIcon.setBounds(0, 0, botIcon.getIntrinsicWidth(), botIcon.getIntrinsicHeight());
botIcon.setTint(username.getCurrentTextColor());
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(botIcon, ImageSpan.ALIGN_BASELINE), 0);
username.setText(ssb);
}else{
// noinspection SetTextI18n
username.setText('@'+account.acct+(isSelf ? ('@'+AccountSessionManager.getInstance().getAccount(accountID).domain) : ""));
@@ -470,6 +560,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
bio.setVisibility(View.VISIBLE);
bio.setText(parsedBio);
}
followersCount.setText(UiUtils.abbreviateNumber(account.followersCount));
followingCount.setText(UiUtils.abbreviateNumber(account.followingCount));
postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount));
@@ -549,21 +641,32 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
if(relationship==null && !isOwnProfile)
return;
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getDisplayUsername()));
if(isOwnProfile){
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.scheduled, R.id.bookmarks, R.id.favorites);
}else{
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
}
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
if(isOwnProfile)
return;
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
MenuItem mute = menu.findItem(R.id.mute);
mute.setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername()));
mute.setIcon(relationship.muting ? R.drawable.ic_fluent_speaker_0_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
UiUtils.insetPopupMenuIcon(getContext(), mute);
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
menu.findItem(R.id.manage_user_lists).setVisible(relationship.following);
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
if(relationship.following) {
menu.findItem(R.id.hide_boosts).setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
manageUserLists.setVisible(true);
MenuItem hideBoosts = menu.findItem(R.id.hide_boosts);
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getShortUsername()));
hideBoosts.setIcon(relationship.showingReblogs ? R.drawable.ic_fluent_arrow_repeat_all_off_24_regular : R.drawable.ic_fluent_arrow_repeat_all_24_regular);
UiUtils.insetPopupMenuIcon(getContext(), hideBoosts);
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername()));
}else {
menu.findItem(R.id.hide_boosts).setVisible(false);
manageUserLists.setVisible(false);
}
if(!account.isLocal())
menu.findItem(R.id.block_domain).setTitle(getString(relationship.domainBlocking ? R.string.unblock_domain : R.string.block_domain, account.getDomain()));
@@ -583,6 +686,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
confirmToggleMuted();
}else if(id==R.id.block){
confirmToggleBlocked();
}else if(id==R.id.soft_block){
confirmSoftBlockUser();
}else if(id==R.id.report){
Bundle args=new Bundle();
args.putString("account", accountID);
@@ -621,13 +726,19 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}else if(id==R.id.manage_user_lists){
final Bundle args=new Bundle();
args.putString("account", accountID);
args.putString("profileAccount", profileAccountID);
args.putString("profileDisplayUsername", account.getDisplayUsername());
if (!isOwnProfile) {
args.putString("profileAccount", profileAccountID);
args.putString("profileDisplayUsername", account.getDisplayUsername());
}
Nav.go(getActivity(), ListTimelinesFragment.class, args);
}else if(id==R.id.followed_hashtags){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FollowedHashtagsFragment.class, args);
}else if(id==R.id.scheduled){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), ScheduledStatusListFragment.class, args);
}
return true;
}
@@ -657,6 +768,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
private void updateRelationship(){
if (getActivity() == null) return;
invalidateOptionsMenu();
actionButton.setVisibility(View.VISIBLE);
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
@@ -666,7 +778,15 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
notifyButton.setSelected(relationship.notifying);
if (getActivity() != null) 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);
}
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
}
public ImageButton getFab() {
return fab;
}
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
@@ -749,8 +869,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
@Override
public void onSuccess(Account result){
editModeLoading=false;
if(getActivity()==null)
return;
if (getActivity() == null) return;
enterEditMode(result);
setActionProgressVisible(false);
}
@@ -758,8 +877,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
@Override
public void onError(ErrorResponse error){
editModeLoading=false;
if(getActivity()==null)
return;
if (getActivity() == null) return;
error.showToast(getActivity());
setActionProgressVisible(false);
}
@@ -862,6 +980,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
public void onSuccess(Account result){
account=result;
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
if (getActivity() == null) return;
exitEditMode();
setActionProgressVisible(false);
}
@@ -883,6 +1002,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
UiUtils.confirmToggleBlockUser(getActivity(), accountID, account, relationship.blocking, this::updateRelationship);
}
private void confirmSoftBlockUser(){
UiUtils.confirmSoftBlockUser(getActivity(), accountID, account, this::updateRelationship);
}
private void updateRelationship(Relationship r){
relationship=r;
updateRelationship();
@@ -890,6 +1013,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
@Override
public boolean onBackPressed(){
if(noteEdit.hasFocus()) {
savePrivateNote();
}
if(isInEditMode){
exitEditMode();
return true;
@@ -939,9 +1065,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private void onFabClick(View v){
Bundle args=new Bundle();
args.putString("account", accountID);
if(!AccountSessionManager.getInstance().isSelf(accountID, account)){
args.putString("prefilledText", '@'+account.acct+' ');
}
if(getPrefilledText() != null) args.putString("prefilledText", getPrefilledText());
Nav.go(getActivity(), ComposeFragment.class, args);
}

View File

@@ -0,0 +1,173 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.GetScheduledStatuses;
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.util.Collections;
import java.util.List;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.SimpleCallback;
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
private String nextMaxID;
private ImageButton fab;
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
public ScheduledStatusListFragment() {
setListLayoutId(R.layout.recycler_fragment_with_fab);
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
E.register(this);
}
@Override
public void onDestroy(){
super.onDestroy();
E.unregister(this);
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setTitle(R.string.sk_unsent_posts);
loadData();
}
@Override
public void onViewCreated(View view, Bundle 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);
}
@Override
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, Filter.FilterContext.HOME);
}
@Override
protected void addAccountToKnown(ScheduledStatus s) {}
@Override
public void onItemClick(String id) {
final Bundle args=new Bundle();
args.putString("account", accountID);
ScheduledStatus scheduledStatus = getStatusByID(id);
Status status = scheduledStatus.toStatus();
args.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
args.putParcelable("editStatus", Parcels.wrap(status));
args.putString("sourceText", status.text);
args.putString("sourceSpoiler", status.spoilerText);
args.putBoolean("redraftStatus", true);
setResult(true, null);
// closing this scheduled status list if another status list is opened from compose fragment
Nav.goForResult(getActivity(), ComposeFragment.class, args, SCHEDULED_STATUS_LIST_OPENED, this);
}
@Override
public void onFragmentResult(int reqCode, boolean success, Bundle result) {
if (reqCode == SCHEDULED_STATUS_LIST_OPENED && success && getActivity() != null) {
Nav.finish(this);
}
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetScheduledStatuses(offset==0 ? null : nextMaxID, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<ScheduledStatus> result){
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
if (getActivity() == null) return;
onDataLoaded(result, nextMaxID!=null);
}
})
.exec(accountID);
}
// copied from StatusListFragment.java
@Subscribe
public void onScheduledStatusDeleted(ScheduledStatusDeletedEvent ev){
if(!ev.accountID.equals(accountID)) return;
ScheduledStatus status=getStatusByID(ev.id);
if(status==null) return;
removeStatus(status);
}
// copied from StatusListFragment.java
@Subscribe
public void onScheduledStatusCreated(ScheduledStatusCreatedEvent ev){
if(!ev.accountID.equals(accountID)) return;
prependItems(Collections.singletonList(ev.scheduledStatus), true);
scrollToTop();
}
// copied from StatusListFragment.java
protected void removeStatus(ScheduledStatus status){
data.remove(status);
preloadedData.remove(status);
int index=-1;
for(int i=0;i<displayItems.size();i++){
if(status.id.equals(displayItems.get(i).parentID)){
index=i;
break;
}
}
if(index==-1)
return;
int lastIndex;
for(lastIndex=index;lastIndex<displayItems.size();lastIndex++){
if(!displayItems.get(lastIndex).parentID.equals(status.id))
break;
}
displayItems.subList(index, lastIndex).clear();
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
// copied from StatusListFragment.java
protected ScheduledStatus getStatusByID(String id){
for(ScheduledStatus s:data){
if(s.id.equals(id)){
return s;
}
}
for(ScheduledStatus s:preloadedData){
if(s.id.equals(id)){
return s;
}
}
return null;
}
}

View File

@@ -9,7 +9,9 @@ import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.LruCache;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -19,8 +21,11 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.RadioButton;
@@ -43,12 +48,15 @@ import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.PushNotification;
import org.joinmastodon.android.model.PushSubscription;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -58,6 +66,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageCache;
@@ -70,13 +80,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
private ArrayList<Item> items=new ArrayList<>();
private ThemeItem themeItem;
private NotificationPolicyItem notificationPolicyItem;
private SwitchItem showNewPostsButtonItem, glitchModeItem;
private String accountID;
private boolean needUpdateNotificationSettings;
private boolean needAppRestart;
private PushSubscription pushSubscription;
private ImageView themeTransitionWindowView;
private TextItem checkForUpdateItem;
private TextItem checkForUpdateItem, clearImageCacheItem;
private ImageCache imageCache;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -84,8 +96,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);
setTitle(R.string.settings);
imageCache = ImageCache.getInstance(getActivity());
accountID=getArguments().getString("account");
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
String instanceName = UiUtils.getInstanceName(accountID);
if(GithubSelfUpdater.needSelfUpdating()){
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
@@ -102,6 +117,17 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
GlobalUserPreferences.uniformNotificationIcon=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
GlobalUserPreferences.reduceMotion=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
popupMenu.inflate(R.menu.color_palettes);
@@ -118,9 +144,42 @@ public class SettingsFragment extends MastodonToolbarFragment{
case BROWN -> R.string.sk_color_palette_brown;
case RED -> R.string.sk_color_palette_red;
case YELLOW -> R.string.sk_color_palette_yellow;
case NORD -> R.string.sk_color_palette_nord;
case NORD -> R.string.mo_color_palette_nord;
});
}));
items.add(new ButtonItem(R.string.sk_settings_publish_button_text, R.drawable.ic_fluent_send_24_regular, b-> {
updatePublishText(b);
if (GlobalUserPreferences.relocatePublishButton) {
b.setOnClickListener(l -> {
Toast.makeText(getActivity(), R.string.mo_disable_relocate_publish_button_to_enable_customization,
Toast.LENGTH_LONG).show();
});
} else {
b.setOnClickListener(l -> {
FrameLayout inputWrap = new FrameLayout(getContext());
EditText input = new EditText(getContext());
input.setHint(R.string.publish);
input.setText(GlobalUserPreferences.publishButtonText.trim());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(V.dp(16), V.dp(4), V.dp(16), V.dp(16));
input.setLayoutParams(params);
inputWrap.addView(input);
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_publish_button_text_title).setView(inputWrap)
.setPositiveButton(R.string.save, (d, which) -> {
GlobalUserPreferences.publishButtonText = input.getText().toString().trim();
GlobalUserPreferences.save();
updatePublishText(b);
})
.setNeutralButton(R.string.clear, (d, which) -> {
GlobalUserPreferences.publishButtonText = "";
GlobalUserPreferences.save();
updatePublishText(b);
})
.setNegativeButton(R.string.cancel, (d, which) -> {
})
.show();
});}
}));
items.add(new HeaderItem(R.string.settings_behavior));
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
@@ -144,18 +203,36 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showDifferentiatedPushNoticationIcons, this::onNotificationStyleChanged));
items.add(new SwitchItem(R.string.sk_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
items.add(new SwitchItem(R.string.mo_disable_dividers, R.drawable.ic_fluent_timeline_24_regular, GlobalUserPreferences.disableDividers, i->{
GlobalUserPreferences.disableDividers=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
GlobalUserPreferences.enableFabAutoHide =i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.mo_relocate_publish_button, R.drawable.ic_fluent_arrow_autofit_down_24_regular, GlobalUserPreferences.relocatePublishButton, i->{
GlobalUserPreferences.relocatePublishButton=i.checked;
GlobalUserPreferences.save();
}));
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;
GlobalUserPreferences.save();
}));
// 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();
// }));
// 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.home_timeline));
items.add(new HeaderItem(R.string.sk_timelines));
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
GlobalUserPreferences.showReplies=i.checked;
GlobalUserPreferences.save();
@@ -164,45 +241,131 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.showBoosts=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
GlobalUserPreferences.loadNewPosts=i.checked;
showNewPostsButtonItem.enabled = i.checked;
if (!i.checked) {
GlobalUserPreferences.showNewPostsButton = false;
showNewPostsButtonItem.checked = false;
}
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsButtonItem)) instanceof SwitchViewHolder svh) svh.rebind();
GlobalUserPreferences.save();
}));
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;
items.add(showNewPostsButtonItem = new SwitchItem(R.string.sk_settings_show_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
GlobalUserPreferences.showNewPostsButton=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_show_alt_indicator, R.drawable.ic_fluent_scan_text_24_regular, GlobalUserPreferences.showAltIndicator, i->{
GlobalUserPreferences.showAltIndicator=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_settings_show_no_alt_indicator, R.drawable.ic_fluent_important_24_regular, GlobalUserPreferences.showNoAltIndicator, i->{
GlobalUserPreferences.showNoAltIndicator=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new HeaderItem(R.string.settings_notifications));
items.add(notificationPolicyItem=new NotificationPolicyItem());
PushSubscription pushSubscription=getPushSubscription();
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_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_mention, R.drawable.ic_at_symbol, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
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)));
boolean switchEnabled=pushSubscription.policy!=PushSubscription.Policy.NONE;
items.add(new HeaderItem(R.string.settings_boring));
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")));
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")));
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_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_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.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_notify_posts, R.drawable.ic_fluent_chat_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked), switchEnabled));
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));
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 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_posting, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/settings/preferences/other"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.sk_settings_filters, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/filters"), 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 TextItem(R.string.sk_settings_rules, ()->{
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
}, 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.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.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
items.add(new HeaderItem(R.string.sk_instance_features));
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
glitchModeItem.enabled = i.checked;
if (i.checked) {
GlobalUserPreferences.accountsWithLocalOnlySupport.add(accountID);
if (instance.pleroma == null) 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)));
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
items.add(new SmallTextItem(getString(translationAvailable ?
R.string.sk_settings_translation_availability_note_available :
R.string.sk_settings_translation_availability_note_unavailable, instance.title)));
items.add(new HeaderItem(R.string.sk_settings_about));
items.add(new RedHeaderItem(R.string.settings_spicy));
if (GithubSelfUpdater.needSelfUpdating()) {
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
items.add(checkForUpdateItem);
items.add(new SwitchItem(R.string.sk_updater_enable_pre_releases, 0, GlobalUserPreferences.enablePreReleases, i->{
GlobalUserPreferences.enablePreReleases=i.checked;
GlobalUserPreferences.save();
}));
}
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon")));
items.add(new TextItem(R.string.mo_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.sk_settings_donate, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sponsors/LucasGGamerM"), R.drawable.ic_fluent_heart_24_regular));
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
LruCache<?, ?> cache = imageCache == null ? null : imageCache.getLruCache();
clearImageCacheItem = new TextItem(R.string.settings_clear_cache, UiUtils.formatFileSize(getContext(), cache != null ? cache.size() : 0, true), this::clearImageCache, 0);
items.add(clearImageCacheItem);
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.save();
})));
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
items.add(new TextItem(R.string.mo_clear_recent_emoji, ()-> {
GlobalUserPreferences.recentEmojis.clear();
GlobalUserPreferences.save();
}));
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
}
private void updatePublishText(Button btn) {
if (GlobalUserPreferences.publishButtonText.isBlank()) btn.setText(R.string.publish);
else btn.setText(GlobalUserPreferences.publishButtonText);
}
@Override
@@ -250,11 +413,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
}
if(needAppRestart){
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
MastodonApp.context.startActivity(intent);
Runtime.getRuntime().exit(0);
}
if(needAppRestart) UiUtils.restartApp();
}
@Override
@@ -358,14 +517,16 @@ public class SettingsFragment extends MastodonToolbarFragment{
case FAVORITE -> subscription.alerts.favourite=enabled;
case FOLLOW -> subscription.alerts.follow=enabled;
case REBLOG -> subscription.alerts.reblog=enabled;
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
case MENTION -> subscription.alerts.mention=enabled;
case POLL -> subscription.alerts.poll=enabled;
case STATUS -> subscription.alerts.status=enabled;
case UPDATE -> subscription.alerts.update=enabled;
}
needUpdateNotificationSettings=true;
}
private void onNotificationStyleChanged(SwitchItem item){
GlobalUserPreferences.showDifferentiatedPushNoticationIcons=item.checked;
GlobalUserPreferences.uniformNotificationIcon=item.checked;
GlobalUserPreferences.save();
}
@@ -384,9 +545,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
list.getAdapter().notifyItemChanged(index);
}
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++;
while(items.get(index) instanceof SwitchItem si){
si.enabled=si.checked=policy!=PushSubscription.Policy.NONE;
si.enabled=si.checked=newState;
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(index);
if(holder!=null)
((BindableViewHolder<?>)holder).rebind();
@@ -426,6 +591,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
private void onLoggedOut(){
if (getActivity() == null) return;
AccountSessionManager.getInstance().removeAccount(accountID);
getActivity().finish();
Intent intent=new Intent(getActivity(), MainActivity.class);
@@ -435,9 +601,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
private void clearImageCache(){
MastodonAPIController.runInBackground(()->{
Activity activity=getActivity();
ImageCache.getInstance(getActivity()).clear();
imageCache.clear();
Toast.makeText(activity, R.string.media_cache_cleared, Toast.LENGTH_SHORT).show();
});
if (list.findViewHolderForAdapterPosition(items.indexOf(clearImageCacheItem)) instanceof TextViewHolder tvh) {
clearImageCacheItem.secondaryText = UiUtils.formatFileSize(getContext(), 0, true);
tvh.rebind();
}
}
@Subscribe
@@ -475,6 +645,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
this.text=getString(text);
}
public HeaderItem(String text) {
this.text=text;
}
@Override
public int getViewType(){
return 0;
@@ -495,7 +669,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
this.onChanged=onChanged;
}
public SwitchItem(@StringRes int text, int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
public SwitchItem(@StringRes int text, @DrawableRes int icon, boolean checked, Consumer<SwitchItem> onChanged, boolean enabled){
this.text=getString(text);
this.icon=icon;
this.checked=checked;
@@ -549,29 +723,44 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
}
private class SmallTextItem extends Item {
private String text;
public SmallTextItem(String text) {
this.text = text;
}
@Override
public int getViewType() {
return 9;
}
}
private class TextItem extends Item{
private String text;
private String secondaryText;
private Runnable onClick;
private boolean loading;
private int icon;
public TextItem(@StringRes int text, Runnable onClick) {
this(text, onClick, false, 0);
}
public TextItem(@StringRes int text, Runnable onClick, boolean loading) {
this(text, onClick, loading, 0);
this(text, null, onClick, false, 0);
}
public TextItem(@StringRes int text, Runnable onClick, @DrawableRes int icon) {
this(text, onClick, false, icon);
this(text, null, onClick, false, icon);
}
public TextItem(@StringRes int text, Runnable onClick, boolean loading, @DrawableRes int icon){
public TextItem(@StringRes int text, String secondaryText, Runnable onClick, @DrawableRes int icon) {
this(text, secondaryText, onClick, false, icon);
}
public TextItem(@StringRes int text, String secondaryText, Runnable onClick, boolean loading, @DrawableRes int icon){
this.text=getString(text);
this.onClick=onClick;
this.loading=loading;
this.icon=icon;
this.secondaryText = secondaryText;
}
@Override
@@ -628,6 +817,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
case 6 -> new FooterViewHolder();
case 7 -> new UpdateViewHolder();
case 8 -> new ButtonViewHolder();
case 9 -> new SmallTextViewHolder();
default -> throw new IllegalStateException("Unexpected value: "+viewType);
};
}
@@ -678,7 +868,12 @@ public class SettingsFragment extends MastodonToolbarFragment{
@Override
public void onBind(SwitchItem item){
text.setText(item.text);
icon.setImageResource(item.icon);
if (item.icon == 0) {
icon.setVisibility(View.GONE);
} else {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(item.icon);
}
checkbox.setChecked(item.checked && item.enabled);
checkbox.setEnabled(item.enabled);
}
@@ -822,22 +1017,27 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
private class TextViewHolder extends BindableViewHolder<TextItem> implements UsableRecyclerView.Clickable{
private final TextView text;
private final TextView text, secondaryText;
private final ProgressBar progress;
private final ImageView icon;
public TextViewHolder(){
super(getActivity(), R.layout.item_settings_text, list);
text = itemView.findViewById(R.id.text);
secondaryText = itemView.findViewById(R.id.secondary_text);
progress = itemView.findViewById(R.id.progress);
icon = itemView.findViewById(R.id.icon);
}
@Override
public void onBind(TextItem item){
icon.setVisibility(item.icon != 0 ? View.VISIBLE : View.GONE);
secondaryText.setVisibility(item.secondaryText != null ? View.VISIBLE : View.GONE);
text.setText(item.text);
progress.animate().alpha(item.loading ? 1 : 0);
if (item.icon != 0) icon.setImageDrawable(getActivity().getTheme().getDrawable(item.icon));
icon.setImageResource(item.icon);
secondaryText.setText(item.secondaryText);
}
@Override
@@ -846,6 +1046,24 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
}
private class SmallTextViewHolder extends BindableViewHolder<SmallTextItem> {
private final TextView text;
public SmallTextViewHolder(){
super(getActivity(), R.layout.item_settings_text, list);
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
public void onBind(SmallTextItem item){
text.setText(item.text);
}
}
private class FooterViewHolder extends BindableViewHolder<FooterItem>{
private final TextView text;
public FooterViewHolder(){
@@ -897,10 +1115,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
text.setText(getString(R.string.sk_update_available, info.version));
text.setText(getString(R.string.mo_update_available, info.version));
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
}else{
text.setText(getString(R.string.sk_update_ready, info.version));
text.setText(getString(R.string.mo_update_ready, info.version));
button.setText(R.string.install_update);
}
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){

View File

@@ -173,16 +173,7 @@ public class SplashFragment extends AppKitFragment{
TextView title=new TextView(getActivity());
title.setTextAppearance(R.style.m3_headline_medium);
title.setText(switch(page){
case 0 -> {
String src=getString(R.string.welcome_page1_title);
SpannableString ss=new SpannableString(src);
int start=src.indexOf("{logo}");
if(start!=-1){
LogoSpan span=new LogoSpan(getResources().getDrawable(R.drawable.splash_logo, getActivity().getTheme()));
ss.setSpan(span, start, start+6, 0);
}
yield ss;
}
case 0 -> getString(R.string.welcome_page1_title);
case 1 -> getString(R.string.welcome_page2_title);
case 2 -> getString(R.string.welcome_page3_title);
default -> throw new IllegalStateException("Unexpected value: "+page);
@@ -204,26 +195,4 @@ public class SplashFragment extends AppKitFragment{
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
private class LogoSpan extends ReplacementSpan{
private final Drawable drawable;
private LogoSpan(Drawable drawable){
this.drawable=drawable;
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
return drawable.getIntrinsicWidth();
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint){
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
canvas.save();
canvas.translate(x, y-V.dp(20));
drawable.draw(canvas);
canvas.restore();
}
}
}

View File

@@ -6,6 +6,7 @@ import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
@@ -46,6 +47,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
@Override
public void onSuccess(List<Status> result){
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
if (getActivity() == null) return;
onDataLoaded(result, false);
}
})
@@ -54,7 +56,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
@Override
protected List<StatusDisplayItem> buildDisplayItems(Status s){
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false);
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, Filter.FilterContext.HOME);
int idx=data.indexOf(s);
if(idx>=0){
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
@@ -139,7 +141,8 @@ public class StatusEditHistoryFragment extends StatusListFragment{
action=getString(R.string.edit_multiple_changed);
}
}
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" · "+date, Collections.emptyList(), 0, null));
String sep = getString(R.string.sk_separator);
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null));
}
return items;
}

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.fragments;
import android.content.res.Configuration;
import android.os.Bundle;
import com.squareup.otto.Subscribe;
@@ -11,6 +12,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.events.StatusDeletedEvent;
import org.joinmastodon.android.events.StatusUpdatedEvent;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
@@ -29,7 +31,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
protected EventListener eventListener=new EventListener();
protected List<StatusDisplayItem> buildDisplayItems(Status s){
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, true);
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, true, null, Filter.FilterContext.HOME);
}
@Override
@@ -55,6 +57,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
Status status=getContentStatusByID(id);
if(status==null)
return;
status.filterRevealed = true;
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("status", Parcels.wrap(status));
@@ -171,6 +174,12 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
adapter.notifyItemRangeRemoved(index, lastIndex-index);
}
@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
if (getParentFragment() instanceof HomeTabFragment home) home.updateToolbarLogo();
}
public class EventListener{
@Subscribe

View File

@@ -5,7 +5,6 @@ import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
@@ -17,6 +16,7 @@ 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.UiUtils;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
import java.util.Collections;
@@ -61,8 +61,7 @@ public class ThreadFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(StatusContext result){
if(getActivity()==null)
return;
if (getActivity() == null) return;
if(refreshing){
data.clear();
displayItems.clear();
@@ -92,16 +91,10 @@ public class ThreadFragment extends StatusListFragment{
}
private List<Status> filterStatuses(List<Status> statuses){
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.THREAD)).collect(Collectors.toList());
if(filters.isEmpty())
return statuses;
return statuses.stream().filter(status->{
for(Filter filter:filters){
if(filter.matches(status))
return false;
}
return true;
}).collect(Collectors.toList());
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD);
return statuses.stream()
.filter(statusFilterPredicate)
.collect(Collectors.toList());
}
@Override
@@ -132,4 +125,14 @@ public class ThreadFragment extends StatusListFragment{
public boolean isItemEnabled(String id){
return !id.equals(mainStatus.id);
}
@Override
public boolean wantsLightStatusBar(){
return !UiUtils.isDarkTheme();
}
@Override
public boolean wantsLightNavigationBar(){
return !UiUtils.isDarkTheme();
}
}

View File

@@ -101,6 +101,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
for(Relationship rel:result){
relationships.put(rel.id, rel);
}
if (getActivity() == null) return;
if(list==null)
return;
for(int i=0;i<list.getChildCount();i++){
@@ -225,6 +226,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
contextMenu=new PopupMenu(getActivity(), menuAnchor);
contextMenu.inflate(R.menu.profile);
contextMenu.setOnMenuItemClickListener(this::onContextMenuItemSelected);
UiUtils.enablePopupMenuIcons(getActivity(), contextMenu);
}
@Override
@@ -283,29 +285,32 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
Menu menu=contextMenu.getMenu();
Account account=item.account;
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getDisplayUsername()));
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername())).setVisible(relationship.following);
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
MenuItem mute = menu.findItem(R.id.mute);
mute.setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername()));
mute.setIcon(relationship.muting ? R.drawable.ic_fluent_speaker_0_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
UiUtils.insetPopupMenuIcon(getContext(), mute);
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername())).setVisible(relationship.following);
menu.findItem(R.id.soft_block).setVisible(relationship.followedBy && !relationship.following);
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
if(relationship.following){
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getShortUsername()));
hideBoosts.setIcon(relationship.showingReblogs ? R.drawable.ic_fluent_arrow_repeat_all_off_24_regular : R.drawable.ic_fluent_arrow_repeat_all_24_regular);
hideBoosts.setVisible(true);
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
UiUtils.insetPopupMenuIcon(getContext(), hideBoosts);
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername()));
manageUserLists.setVisible(true);
}else{
hideBoosts.setVisible(false);
manageUserLists.setVisible(true);
}
MenuItem blockDomain=menu.findItem(R.id.block_domain);
if(!account.isLocal()){
blockDomain.setTitle(getString(relationship.domainBlocking ? R.string.unblock_domain : R.string.block_domain, account.getDomain()));
blockDomain.setVisible(true);
}else{
blockDomain.setVisible(false);
}
menu.findItem(R.id.block_domain).setVisible(false);
menuAnchor.setTranslationX(x);
menuAnchor.setTranslationY(y);
@@ -346,6 +351,8 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
UiUtils.confirmToggleMuteUser(getActivity(), accountID, account, relationship.muting, this::updateRelationship);
}else if(id==R.id.block){
UiUtils.confirmToggleBlockUser(getActivity(), accountID, account, relationship.blocking, this::updateRelationship);
}else if(id==R.id.soft_block){
UiUtils.confirmSoftBlockUser(getActivity(), accountID, account, this::updateRelationship);
}else if(id==R.id.report){
Bundle args=new Bundle();
args.putString("account", accountID);
@@ -364,6 +371,7 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
@Override
public void onSuccess(Relationship result){
relationships.put(AccountViewHolder.this.item.account.id, result);
if (getActivity() == null) return;
bindRelationship();
}

View File

@@ -23,6 +23,7 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
if (getActivity() == null) return;
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
}
})

View File

@@ -73,6 +73,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<FollowSuggestion> result){
if (getActivity() == null) return;
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
loadRelationships();
}
@@ -107,6 +108,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
public void onSuccess(List<Relationship> result){
relationshipsRequest=null;
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
if (getActivity() == null) return;
if(list==null)
return;
for(int i=0;i<list.getChildCount();i++){

View File

@@ -62,7 +62,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
private String accountID;
private Runnable searchDebouncer=this::onSearchChangedDebounced;
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
// private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -81,20 +81,30 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
tabLayout=view.findViewById(R.id.tabbar);
pager=view.findViewById(R.id.pager);
tabViews=new FrameLayout[noFederated ? 6 : 7];
// tabViews=new FrameLayout[noFederated ? 5 : 6];
tabViews=new FrameLayout[4];
for(int i=0;i<tabViews.length;i++){
FrameLayout tabView=new FrameLayout(getActivity());
int switchIndex = noFederated && i > 0 ? i + 1 : i;
tabView.setId(switch(switchIndex){
case 0 -> R.id.discover_local_timeline;
case 1 -> R.id.discover_federated_timeline;
case 2 -> R.id.discover_lists;
case 3 -> R.id.discover_hashtags;
case 4 -> R.id.discover_posts;
case 5 -> R.id.discover_news;
case 6 -> R.id.discover_users;
default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
/// int switchIndex = noFederated && i > 0 ? i + 1 : i;
// tabView.setId(switch(switchIndex){
// case 0 -> R.id.discover_local_timeline;
// case 1 -> R.id.discover_federated_timeline;
// case 2 -> R.id.discover_hashtags;
// case 3 -> R.id.discover_posts;
// case 4 -> R.id.discover_news;
// case 5 -> R.id.discover_users;
// default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
// });
tabView.setId(switch(i){
case 0 -> R.id.discover_hashtags;
case 1 -> R.id.discover_posts;
case 2 -> R.id.discover_news;
case 3 -> R.id.discover_users;
default -> throw new IllegalStateException("Unexpected value: "+i);
});
tabView.setVisibility(View.GONE);
view.addView(tabView); // needed so the fragment manager will have somewhere to restore the tab fragment
tabViews[i]=tabView;
@@ -103,6 +113,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
tabLayout.setTabTextSize(V.dp(16));
tabLayout.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorTabInactive), UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
UiUtils.reduceSwipeSensitivity(pager);
pager.setOffscreenPageLimit(4);
pager.setUserInputEnabled(!GlobalUserPreferences.disableSwipe);
pager.setAdapter(new DiscoverPagerAdapter());
@@ -119,7 +130,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
});
if(localTimelineFragment==null){
if(localTimelineFragment==null || hashtagsFragment==null){
Bundle args=new Bundle();
args.putString("account", accountID);
args.putBoolean("__is_tab", true);
@@ -139,39 +150,56 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
localTimelineFragment=new LocalTimelineFragment();
localTimelineFragment.setArguments(args);
listTimelinesFragment=new ListTimelinesFragment();
listTimelinesFragment.setArguments(args);
// listTimelinesFragment=new ListTimelinesFragment();
// listTimelinesFragment.setArguments(args);
//
// FragmentTransaction transaction = getChildFragmentManager().beginTransaction()
// .add(R.id.discover_posts, postsFragment)
// .add(R.id.discover_local_timeline, localTimelineFragment)
// .add(R.id.discover_hashtags, hashtagsFragment)
// .add(R.id.discover_news, newsFragment)
// .add(R.id.discover_users, accountsFragment)
// .add(R.id.discover_lists, listTimelinesFragment);
//
// if (!noFederated) {
// federatedTimelineFragment=new FederatedTimelineFragment();
// federatedTimelineFragment.setArguments(args);
// transaction.add(R.id.discover_federated_timeline, federatedTimelineFragment);
// }
//
// transaction.commit();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction()
getChildFragmentManager().beginTransaction()
.add(R.id.discover_posts, postsFragment)
.add(R.id.discover_local_timeline, localTimelineFragment)
.add(R.id.discover_hashtags, hashtagsFragment)
.add(R.id.discover_news, newsFragment)
.add(R.id.discover_users, accountsFragment)
.add(R.id.discover_lists, listTimelinesFragment);
if (!noFederated) {
federatedTimelineFragment=new FederatedTimelineFragment();
federatedTimelineFragment.setArguments(args);
transaction.add(R.id.discover_federated_timeline, federatedTimelineFragment);
}
transaction.commit();
.commit();
}
tabLayoutMediator=new TabLayoutMediator(tabLayout, pager, new TabLayoutMediator.TabConfigurationStrategy(){
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
if (noFederated && position > 0) position++;
tab.setText(switch(position){
case 0 -> R.string.local_timeline;
case 1 -> R.string.sk_federated_timeline;
case 2 -> R.string.sk_list_timelines;
case 3 -> R.string.hashtags;
case 4 -> R.string.posts;
case 5 -> R.string.news;
case 6 -> R.string.for_you;
// if (noFederated && position > 0) position++;
// tab.setText(switch(position){
// case 0 -> R.string.local_timeline;
// case 1 -> R.string.sk_federated_timeline;
// case 2 -> R.string.sk_list_timelines;
// case 3 -> R.string.hashtags;
// case 4 -> R.string.posts;
// case 5 -> R.string.news;
// case 6 -> R.string.for_you;
//
// default -> throw new IllegalStateException("Unexpected value: "+position);
// });
tab.setText(switch(position){
case 0 -> R.string.hashtags;
case 1 -> R.string.posts;
case 2 -> R.string.news;
case 3 -> R.string.for_you;
default -> throw new IllegalStateException("Unexpected value: "+position);
});
tab.view.textView.setAllCaps(true);
@@ -257,8 +285,11 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
public void loadData(){
if(localTimelineFragment!=null && !localTimelineFragment.loaded && !localTimelineFragment.dataLoading)
localTimelineFragment.loadData();
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
hashtagsFragment.loadData();
// if(localTimelineFragment!=null && !localTimelineFragment.loaded && !localTimelineFragment.dataLoading)
// localTimelineFragment.loadData();
}
private void onSearchEditFocusChanged(View v, boolean hasFocus){
@@ -293,15 +324,24 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
private Fragment getFragmentForPage(int page){
if (noFederated && page > 0) page++;
// if (noFederated && page > 0) page++;
// return switch(page){
// case 0 -> localTimelineFragment;
// case 1 -> federatedTimelineFragment;
// case 2 -> hashtagsFragment;
// case 3 -> postsFragment;
// case 4 -> newsFragment;
// case 5 -> accountsFragment;
// case 6 -> listTimelinesFragment;
// default -> throw new IllegalStateException("Unexpected value: "+page);
// };
return switch(page){
case 0 -> localTimelineFragment;
case 1 -> federatedTimelineFragment;
case 2 -> hashtagsFragment;
case 3 -> postsFragment;
case 4 -> newsFragment;
case 5 -> accountsFragment;
case 6 -> listTimelinesFragment;
case 0 -> hashtagsFragment;
case 1 -> postsFragment;
case 2 -> newsFragment;
case 3 -> accountsFragment;
default -> throw new IllegalStateException("Unexpected value: "+page);
};
}
@@ -358,4 +398,10 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
return position;
}
}
public void selectSearch(){
searchEdit.requestFocus();
onSearchEditFocusChanged(searchEdit, true);
getActivity().getSystemService(InputMethodManager.class).showSoftInput(searchEdit, 0);
}
}

View File

@@ -58,6 +58,7 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<Card> implements
imageRequests=result.stream()
.map(card->TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(150), V.dp(150)))
.collect(Collectors.toList());
if (getActivity() == null) return;
onDataLoaded(result, false);
}
})

View File

@@ -17,11 +17,12 @@ public class DiscoverPostsFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetTrendingStatuses(count)
currentRequest=new GetTrendingStatuses(offset, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, false);
if (getActivity() == null) return;
onDataLoaded(result, !result.isEmpty());
}
}).exec(accountID);
}

View File

@@ -3,7 +3,9 @@ package org.joinmastodon.android.fragments.discover;
import android.os.Bundle;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
import org.joinmastodon.android.fragments.FabStatusListFragment;
import org.joinmastodon.android.fragments.StatusListFragment;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
@@ -15,7 +17,7 @@ import java.util.stream.Collectors;
import me.grishka.appkit.api.SimpleCallback;
public class FederatedTimelineFragment extends StatusListFragment{
public class FederatedTimelineFragment extends FabStatusListFragment {
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.FEDERATED_TIMELINE);
private String maxID;
@@ -27,6 +29,7 @@ public class FederatedTimelineFragment extends StatusListFragment{
public void onSuccess(List<Status> result){
if(!result.isEmpty())
maxID=result.get(result.size()-1).id;
if (getActivity() == null) return;
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
}
})
@@ -36,6 +39,6 @@ public class FederatedTimelineFragment extends StatusListFragment{
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
bannerHelper.maybeAddBanner(contentWrap);
// bannerHelper.maybeAddBanner(contentWrap);
}
}

View File

@@ -3,7 +3,9 @@ package org.joinmastodon.android.fragments.discover;
import android.os.Bundle;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
import org.joinmastodon.android.fragments.FabStatusListFragment;
import org.joinmastodon.android.fragments.StatusListFragment;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
@@ -15,8 +17,8 @@ import java.util.stream.Collectors;
import me.grishka.appkit.api.SimpleCallback;
public class LocalTimelineFragment extends StatusListFragment{
private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
public class LocalTimelineFragment extends FabStatusListFragment {
// private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.LOCAL_TIMELINE);
private String maxID;
@Override
@@ -27,6 +29,7 @@ public class LocalTimelineFragment extends StatusListFragment{
public void onSuccess(List<Status> result){
if(!result.isEmpty())
maxID=result.get(result.size()-1).id;
if (getActivity() == null) return;
onDataLoaded(result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()), !result.isEmpty());
}
})
@@ -36,6 +39,6 @@ public class LocalTimelineFragment extends StatusListFragment{
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
bannerHelper.maybeAddBanner(contentWrap);
// bannerHelper.maybeAddBanner(contentWrap);
}
}

View File

@@ -14,6 +14,7 @@ import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.SearchResult;
import org.joinmastodon.android.model.SearchResults;
@@ -62,6 +63,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);
loadData();
resetEmptyText();
}
@Override
@@ -70,12 +72,16 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
imm=activity.getSystemService(InputMethodManager.class);
}
private void resetEmptyText() {
setEmptyText(R.string.sk_recent_searches_placeholder);
}
@Override
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
return switch(s.type){
case ACCOUNT -> Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account));
case HASHTAG -> Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag));
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true);
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true, null, Filter.FilterContext.PUBLIC);
};
}
@@ -119,6 +125,8 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
@Override
protected void doLoadData(int offset, int count){
if (getActivity() == null) return;
resetEmptyText();
if(isInRecentMode()){
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().getRecentSearches(sr->{
if(getActivity()==null)
@@ -128,11 +136,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
onDataLoaded(sr, false);
});
}else{
setEmptyText(R.string.sk_searching);
progressVisibilityListener.onProgressVisibilityChanged(true);
currentRequest=new GetSearchResults(currentQuery, null, true)
.setCallback(new Callback<>(){
@Override
public void onSuccess(SearchResults result){
setEmptyText(R.string.sk_no_results);
ArrayList<SearchResult> results=new ArrayList<>();
if(result.accounts!=null){
for(Account acc:result.accounts)
@@ -148,11 +158,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
}
prevDisplayItems=new ArrayList<>(displayItems);
unfilteredResults=results;
if (getActivity() == null) return;
onDataLoaded(filterSearchResults(results), false);
}
@Override
public void onError(ErrorResponse error){
resetEmptyText();
currentRequest=null;
Activity a=getActivity();
if(a==null)

View File

@@ -43,6 +43,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Hashtag> result){
if (getActivity() == null) return;
onDataLoaded(result, false);
}
})

View File

@@ -36,10 +36,10 @@ import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class CustomLoginFragment extends InstanceCatalogFragment {
public class CustomWelcomeFragment extends InstanceCatalogFragment {
private View headerView;
public CustomLoginFragment() {
public CustomWelcomeFragment() {
super(R.layout.fragment_welcome_custom, 1);
}
@@ -55,9 +55,9 @@ public class CustomLoginFragment extends InstanceCatalogFragment {
dataLoaded();
}
// @Override
@Override
protected void onUpdateToolbar(){
// super.onUpdateToolbar();
super.onUpdateToolbar();
if (!canGoBack()) {
ImageView toolbarLogo=new ImageView(getActivity());
@@ -137,9 +137,11 @@ public class CustomLoginFragment extends InstanceCatalogFragment {
headerView.findViewById(R.id.more).setVisibility(View.GONE);
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
((TextView) headerView.findViewById(R.id.username)).setText("@moshidon");
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
((TextView) headerView.findViewById(R.id.timestamp)).setText(R.string.time_now);
headerView.findViewById(R.id.separator).setVisibility(View.GONE);
headerView.findViewById(R.id.timestamp).setVisibility(View.GONE);
headerView.findViewById(R.id.unread_indicator).setVisibility(View.GONE);
((TextView) headerView.findViewById(R.id.username)).setText(R.string.mo_app_username);
((TextView) headerView.findViewById(R.id.name)).setText(R.string.mo_app_name);
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
@@ -168,7 +170,7 @@ public class CustomLoginFragment extends InstanceCatalogFragment {
return mergeAdapter;
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder> {
public InstancesAdapter(){
super(imgLoader);
}
@@ -204,11 +206,6 @@ public class CustomLoginFragment extends InstanceCatalogFragment {
public InstanceViewHolder(){
super(getActivity(), R.layout.item_instance_custom, list);
// itemView.setPadding(V.dp(16), V.dp(16), V.dp(16), V.dp(16));
// TypedValue value = new TypedValue();
// getActivity().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, value, true);
// itemView.setBackground(getActivity().getTheme().getDrawable(R.drawable.bg_search_field));
title=findViewById(R.id.title);
description=findViewById(R.id.description);
userCount=findViewById(R.id.user_count);

View File

@@ -5,6 +5,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -19,6 +20,7 @@ import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ElevationOnScrollListener;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.parceler.Parcels;
@@ -42,6 +44,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.FragmentRootLinearLayout;
import me.grishka.appkit.views.UsableRecyclerView;
import okhttp3.Call;
import okhttp3.Callback;
@@ -58,6 +61,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
private ArrayList<Item> items=new ArrayList<>();
private Call currentRequest;
private ItemsAdapter itemsAdapter;
private ElevationOnScrollListener onScrollListener;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -72,7 +76,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
setNavigationBarColor(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground));
instance=Parcels.unwrap(getArguments().getParcelable("instance"));
items.add(new Item("Mastodon for Android Privacy Policy", "joinmastodon.org", "https://joinmastodon.org/android/privacy", "https://joinmastodon.org/favicon-32x32.png"));
items.add(new Item("Mastodon for Android Privacy Policy", getString(R.string.privacy_policy_explanation), "joinmastodon.org", "https://joinmastodon.org/android/privacy", "https://joinmastodon.org/favicon-32x32.png"));
loadServerPrivacyPolicy();
}
@@ -93,18 +97,24 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
list.setLayoutManager(new LinearLayoutManager(getActivity()));
View headerView=inflater.inflate(R.layout.item_list_header_simple, list, false);
TextView text=headerView.findViewById(R.id.text);
text.setText(R.string.privacy_policy_subtitle);
text.setText(getString(R.string.privacy_policy_subtitle, instance.uri));
adapter=new MergeRecyclerAdapter();
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
adapter.addAdapter(itemsAdapter=new ItemsAdapter());
list.setAdapter(adapter);
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
btn=view.findViewById(R.id.btn_next);
btn.setOnClickListener(v->onButtonClick());
buttonBar=view.findViewById(R.id.button_bar);
Button backBtn=view.findViewById(R.id.btn_back);
backBtn.setText(getString(R.string.server_policy_disagree, instance.uri));
backBtn.setOnClickListener(v->{
setResult(false, null);
Nav.finish(this);
});
return view;
}
@@ -113,13 +123,17 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
super.onViewCreated(view, savedInstanceState);
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
}
// @Override
@Override
protected void onUpdateToolbar(){
// super.onUpdateToolbar();
getToolbar().setBackground(null);
super.onUpdateToolbar();
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
getToolbar().setElevation(0);
if(onScrollListener!=null){
onScrollListener.setViews(buttonBar, getToolbar());
}
}
protected void onButtonClick(){
@@ -158,7 +172,7 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
if(!response.isSuccessful())
return;
Document doc=Jsoup.parse(Objects.requireNonNull(body).byteStream(), Objects.requireNonNull(body.contentType()).charset(StandardCharsets.UTF_8).name(), req.url().toString());
final Item item=new Item(doc.title(), instance.uri, req.url().toString(), "https://"+instance.uri+"/favicon.ico");
final Item item=new Item(doc.title(), null, instance.uri, req.url().toString(), "https://"+instance.uri+"/favicon.ico");
Activity activity=getActivity();
if(activity!=null){
activity.runOnUiThread(()->{
@@ -192,16 +206,23 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
private class ItemViewHolder extends BindableViewHolder<Item> implements UsableRecyclerView.Clickable{
private final TextView title;
private final TextView subtitle;
public ItemViewHolder(){
super(getActivity(), R.layout.item_privacy_policy_link, list);
title=findViewById(R.id.title);
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
subtitle=findViewById(R.id.subtitle);
}
@Override
public void onBind(Item item){
title.setText(item.title);
if(TextUtils.isEmpty(item.subtitle)){
subtitle.setVisibility(View.GONE);
}else{
subtitle.setVisibility(View.VISIBLE);
subtitle.setText(item.subtitle);
}
}
@Override
@@ -211,10 +232,11 @@ public class GoogleMadeMeAddThisFragment extends ToolbarFragment{
}
private static class Item{
public String title, domain, url, faviconUrl;
public String title, subtitle, domain, url, faviconUrl;
public Item(String title, String domain, String url, String faviconUrl){
public Item(String title, String subtitle, String domain, String url, String faviconUrl){
this.title=title;
this.subtitle=subtitle;
this.domain=domain;
this.url=url;
this.faviconUrl=faviconUrl;

View File

@@ -1,14 +1,8 @@
package org.joinmastodon.android.fragments.onboarding;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
@@ -20,7 +14,6 @@ import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.RadioButton;
@@ -38,6 +31,7 @@ import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.FilterChipView;
import org.joinmastodon.android.utils.ElevationOnScrollListener;
import org.parceler.Parcels;
import java.util.ArrayList;
@@ -56,11 +50,7 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.OnBackPressedListener;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
@@ -80,6 +70,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
private LinearLayout filtersWrap;
private HorizontalScrollView filtersScroll;
private ImageButton backBtn, clearSearchBtn;
private View focusThing;
private FilterChipView categoryGeneral, categorySpecialInterests;
private List<FilterChipView> regionalFilters;
@@ -214,47 +205,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
setStatusBarColor(0);
topBar=view.findViewById(R.id.top_bar);
LayerDrawable topBg=(LayerDrawable) topBar.getBackground().mutate();
topBar.setBackground(topBg);
Drawable topOverlay=topBg.findDrawableByLayerId(R.id.color_overlay);
topOverlay.setAlpha(0);
LayerDrawable btmBg=(LayerDrawable) buttonBar.getBackground().mutate();
buttonBar.setBackground(btmBg);
Drawable btmOverlay=btmBg.findDrawableByLayerId(R.id.color_overlay);
btmOverlay.setAlpha(0);
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
private boolean isAtTop=true;
private Animator currentPanelsAnim;
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
boolean newAtTop=recyclerView.getChildCount()==0 || (recyclerView.getChildAdapterPosition(recyclerView.getChildAt(0))==0 && recyclerView.getChildAt(0).getTop()==recyclerView.getPaddingTop());
if(newAtTop!=isAtTop){
isAtTop=newAtTop;
if(currentPanelsAnim!=null)
currentPanelsAnim.cancel();
AnimatorSet set=new AnimatorSet();
set.playTogether(
ObjectAnimator.ofInt(topOverlay, "alpha", isAtTop ? 0 : 20),
ObjectAnimator.ofInt(btmOverlay, "alpha", isAtTop ? 0 : 20),
ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Z, isAtTop ? 0 : V.dp(3)),
ObjectAnimator.ofFloat(buttonBar, View.TRANSLATION_Z, isAtTop ? 0 : V.dp(3))
);
set.setDuration(150);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
currentPanelsAnim=null;
}
});
set.start();
currentPanelsAnim=set;
}
}
});
list.addOnScrollListener(new ElevationOnScrollListener(null, topBar, buttonBar));
searchEdit=view.findViewById(R.id.search_edit);
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
@@ -285,7 +236,13 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
FilterChipView langFilter=new FilterChipView(getActivity());
langFilter.setDrawableEnd(R.drawable.ic_baseline_arrow_drop_down_18);
langFilter.setText(R.string.server_filter_any_language);
if(currentLanguage==null){
langFilter.setText(R.string.server_filter_any_language);
}else{
Locale locale=Locale.forLanguageTag(currentLanguage);
langFilter.setText(locale.getDisplayLanguage(locale));
langFilter.setSelected(true);
}
langFilterMenu=new PopupMenu(getContext(), langFilter);
langFilter.setOnTouchListener(langFilterMenu.getDragToOpenListener());
langFilter.setOnClickListener(v->langFilterMenu.show());
@@ -301,8 +258,12 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
speedFilterMenu.getMenu().add(0, 2, 0, R.string.server_filter_manual_review);
speedFilter.setOnTouchListener(speedFilterMenu.getDragToOpenListener());
speedFilter.setOnClickListener(v->speedFilterMenu.show());
speedFilter.setText(R.string.server_filter_instant_signup);
speedFilter.setSelected(true);
speedFilter.setText(switch(currentSignupSpeedFilter){
case ANY -> R.string.server_filter_any_signup_speed;
case INSTANT -> R.string.server_filter_instant_signup;
case REVIEWED -> R.string.server_filter_manual_review;
});
speedFilter.setSelected(currentSignupSpeedFilter!=SignupSpeedFilter.ANY);
filtersWrap.addView(speedFilter, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
speedFilterMenu.setOnMenuItemClickListener(item->{
@@ -328,11 +289,13 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
categoryGeneral.setText(R.string.category_general);
categoryGeneral.setTag(CategoryChoice.GENERAL);
categoryGeneral.setOnClickListener(this::onCategoryFilterClick);
categoryGeneral.setSelected(categoryChoice==CategoryChoice.GENERAL);
filtersWrap.addView(categoryGeneral, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
categorySpecialInterests=new FilterChipView(getActivity());
categorySpecialInterests.setText(R.string.category_special_interests);
categorySpecialInterests.setTag(CategoryChoice.SPECIAL);
categorySpecialInterests.setOnClickListener(this::onCategoryFilterClick);
categorySpecialInterests.setSelected(categoryChoice==CategoryChoice.SPECIAL);
filtersWrap.addView(categorySpecialInterests, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
regionalFilters=Arrays.stream(CatalogInstance.Region.values()).map(r->{
@@ -351,6 +314,11 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
filtersWrap.addView(fv, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
return fv;
}).collect(Collectors.toList());
focusThing=view.findViewById(R.id.focus_thing);
focusThing.requestFocus();
view.findViewById(R.id.btn_random_instance).setOnClickListener(this::onPickRandomInstanceClick);
nextButton.setEnabled(chosenInstance!=null);
}
private void onRegionFilterClick(View v){
@@ -381,22 +349,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
updateFilteredList();
}
@Override
protected void onNextClick(View v){
if(chosenInstance==null){
String lang=Locale.getDefault().getLanguage();
List<CatalogInstance> instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general"))) && (lang.equals(ci.language) || (ci.languages!=null && ci.languages.contains(lang)))).collect(Collectors.toList());
if(instances.isEmpty()){
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
}
if(instances.isEmpty()){
return;
}
chosenInstance=instances.get(new Random().nextInt(instances.size()));
}
super.onNextClick(v);
}
@Override
protected void proceedWithAuthOrSignup(Instance instance){
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
@@ -413,6 +365,19 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
Nav.go(getActivity(), InstanceRulesFragment.class, args);
}
private void onPickRandomInstanceClick(View v){
String lang=Locale.getDefault().getLanguage();
List<CatalogInstance> instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general"))) && (lang.equals(ci.language) || (ci.languages!=null && ci.languages.contains(lang)))).collect(Collectors.toList());
if(instances.isEmpty()){
instances=data.stream().filter(ci->!ci.approvalRequired && ("general".equals(ci.category) || (ci.categories!=null && ci.categories.contains("general")))).collect(Collectors.toList());
}
if(instances.isEmpty()){
return;
}
chosenInstance=instances.get(new Random().nextInt(instances.size()));
onNextClick(v);
}
// private String getEmojiForCategory(String category){
// return switch(category){
// case "all" -> "💬";
@@ -550,7 +515,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
searchEdit.setCompoundDrawableTintList(ColorStateList.valueOf(0));
}else{
filtersScroll.setVisibility(View.VISIBLE);
searchEdit.clearFocus();
focusThing.requestFocus();
searchEdit.setText("");
lp.addRule(RelativeLayout.END_OF, R.id.btn_back);
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(searchEdit.getWindowToken(), 0);
@@ -562,7 +527,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
updateFilteredList();
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder> implements ImageLoaderRecyclerAdapter{
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
public InstancesAdapter(){
super(imgLoader);
}
@@ -588,22 +553,11 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
public int getItemViewType(int position){
return -1;
}
@Override
public int getImageCountForItem(int position){
return filteredData.get(position).thumbnailRequest!=null ? 1 : 0;
}
@Override
public ImageLoaderRequest getImageRequest(int position, int image){
return filteredData.get(position).thumbnailRequest;
}
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable, ImageLoaderViewHolder{
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.DisableableClickable{
private final TextView title, description;
private final RadioButton radioButton;
private final ImageView thumbnail;
private boolean enabled;
public InstanceViewHolder(){
@@ -611,15 +565,12 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
title=findViewById(R.id.title);
description=findViewById(R.id.description);
radioButton=findViewById(R.id.radiobtn);
thumbnail=findViewById(R.id.image);
}
@Override
public void onBind(CatalogInstance item){
title.setText(item.normalizedDomain);
radioButton.setChecked(chosenInstance==item);
if(item.thumbnailRequest==null)
thumbnail.setImageDrawable(null);
Instance realInstance=instancesCache.get(item.normalizedDomain);
float alpha;
if(realInstance!=null && !realInstance.registrations){
@@ -634,7 +585,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
title.setAlpha(alpha);
description.setAlpha(alpha);
radioButton.setAlpha(alpha);
thumbnail.setAlpha(alpha);
}
@Override
@@ -657,6 +607,9 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
adapter.notifyItemChanged(idx);
}
}
if(!nextButton.isEnabled()){
nextButton.setEnabled(true);
}
radioButton.setChecked(true);
if(chosenInstance==null)
nextButton.setEnabled(true);
@@ -664,16 +617,6 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
loadInstanceInfo(chosenInstance.domain, false);
}
@Override
public void setImage(int index, Drawable image){
thumbnail.setImageDrawable(image);
}
@Override
public void clearImage(int index){
setImage(index, null);
}
@Override
public boolean isEnabled(){
return enabled;
@@ -695,4 +638,5 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
return (this==GENERAL)==isGeneral;
}
}
}

View File

@@ -17,6 +17,7 @@ import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ElevationOnScrollListener;
import org.parceler.Parcels;
import androidx.annotation.NonNull;
@@ -28,6 +29,7 @@ import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.FragmentRootLinearLayout;
import me.grishka.appkit.views.UsableRecyclerView;
public class InstanceRulesFragment extends ToolbarFragment{
@@ -36,6 +38,9 @@ public class InstanceRulesFragment extends ToolbarFragment{
private Button btn;
private View buttonBar;
private Instance instance;
private ElevationOnScrollListener onScrollListener;
private static final int RULES_REQUEST=376;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -71,6 +76,8 @@ public class InstanceRulesFragment extends ToolbarFragment{
btn.setOnClickListener(v->onButtonClick());
buttonBar=view.findViewById(R.id.button_bar);
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
return view;
}
@@ -79,19 +86,31 @@ public class InstanceRulesFragment extends ToolbarFragment{
super.onViewCreated(view, savedInstanceState);
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
}
// @Override
@Override
protected void onUpdateToolbar(){
// super.onUpdateToolbar();
getToolbar().setBackground(null);
super.onUpdateToolbar();
getToolbar().setBackgroundResource(R.drawable.bg_onboarding_panel);
getToolbar().setElevation(0);
if(onScrollListener!=null){
onScrollListener.setViews(buttonBar, getToolbar());
}
}
protected void onButtonClick(){
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), GoogleMadeMeAddThisFragment.class, args);
Nav.goForResult(getActivity(), GoogleMadeMeAddThisFragment.class, args, RULES_REQUEST, this);
}
@Override
public void onFragmentResult(int reqCode, boolean success, Bundle result){
super.onFragmentResult(reqCode, success, result);
if(reqCode==RULES_REQUEST && !success){
Nav.finish(this);
}
}
@Override

View File

@@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
import org.joinmastodon.android.events.FinishReportFragmentsEvent;
import org.joinmastodon.android.fragments.StatusListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.displayitems.AudioStatusDisplayItem;
@@ -89,6 +90,7 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if (getActivity() == null) return;
onDataLoaded(result, !result.isEmpty());
}
})
@@ -237,7 +239,7 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
@Override
protected List<StatusDisplayItem> buildDisplayItems(Status s){
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false);
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, Filter.FilterContext.HOME);
for(StatusDisplayItem item:items){
if(item instanceof ImageStatusDisplayItem isdi){
isdi.horizontalInset=V.dp(40+32);

View File

@@ -164,6 +164,10 @@ public class Account extends BaseModel{
return '@'+acct;
}
public String getShortUsername() {
return '@'+acct.split("@")[0];
}
@Override
public String toString(){
return "Account{"+

View File

@@ -0,0 +1,55 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
import org.parceler.Parcel;
import java.time.Instant;
import java.util.List;
@Parcel
public class Announcement extends BaseModel implements DisplayItemsParent {
@RequiredField
public String id;
@RequiredField
public String content;
public Instant startsAt;
public Instant endsAt;
public boolean published;
public boolean allDay;
public Instant publishedAt;
public Instant updatedAt;
public boolean read;
public List<Emoji> emojis;
public List<Mention> mentions;
public List<Hashtag> tags;
@Override
public String toString() {
return "Announcement{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
", startsAt=" + startsAt +
", endsAt=" + endsAt +
", published=" + published +
", allDay=" + allDay +
", publishedAt=" + publishedAt +
", updatedAt=" + updatedAt +
", read=" + read +
", emojis=" + emojis +
", mentions=" + mentions +
", tags=" + tags +
'}';
}
public Status toStatus() {
Status s = Status.ofFake(id, content, publishedAt);
s.createdAt = startsAt != null ? startsAt : publishedAt;
if (updatedAt != null) s.editedAt = updatedAt;
return s;
}
@Override
public String getID() {
return id;
}
}

View File

@@ -14,9 +14,11 @@ import org.parceler.Parcel;
import org.parceler.ParcelConstructor;
import org.parceler.ParcelProperty;
import java.util.UUID;
@Parcel
public class Attachment extends BaseModel{
@RequiredField
// @RequiredField
public String id;
@RequiredField
public Type type;
@@ -45,26 +47,26 @@ public class Attachment extends BaseModel{
public int getWidth(){
if(meta==null)
return 0;
return 1920;
if(meta.width>0)
return meta.width;
if(meta.original!=null && meta.original.width>0)
return meta.original.width;
if(meta.small!=null && meta.small.width>0)
return meta.small.width;
return 0;
return 1920;
}
public int getHeight(){
if(meta==null)
return 0;
return 1080;
if(meta.height>0)
return meta.height;
if(meta.original!=null && meta.original.height>0)
return meta.original.height;
if(meta.small!=null && meta.small.height>0)
return meta.small.height;
return 0;
return 1080;
}
public double getDuration(){
@@ -85,6 +87,12 @@ public class Attachment extends BaseModel{
if(placeholder!=null)
blurhashPlaceholder=new BlurHashDrawable(placeholder, getWidth(), getHeight());
}
if (id == null) {
// akkoma servers doesn't provide IDs for attachments,
// but IDs are needed by the AudioPlayerService
id = "" + this.hashCode();
}
}
@Override

View File

@@ -6,21 +6,26 @@ import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.parceler.Parcel;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;
@Parcel
public class Filter extends BaseModel{
@RequiredField
public String id;
@RequiredField
public String title;
@RequiredField
public String phrase;
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
public Instant expiresAt;
public boolean irreversible;
public boolean wholeWord;
public FilterAction filterAction;
@SerializedName("context")
private List<FilterContext> _context;
@@ -58,6 +63,7 @@ public class Filter extends BaseModel{
public String toString(){
return "Filter{"+
"id='"+id+'\''+
", title='"+title+'\''+
", phrase='"+phrase+'\''+
", context="+context+
", expiresAt="+expiresAt+
@@ -76,4 +82,11 @@ public class Filter extends BaseModel{
@SerializedName("thread")
THREAD
}
public enum FilterAction{
@SerializedName("hide")
HIDE,
@SerializedName("warn")
WARN
}
}

View File

@@ -0,0 +1,8 @@
package org.joinmastodon.android.model;
import org.parceler.Parcel;
@Parcel
public class FilterResult extends BaseModel {
public Filter filter;
}

View File

@@ -27,7 +27,7 @@ public class Instance extends BaseModel{
/**
* Admin-defined description of the Mastodon site.
*/
@RequiredField
// @RequiredField
public String description;
/**
* A shorter description defined by the admin.
@@ -37,7 +37,7 @@ public class Instance extends BaseModel{
/**
* An email that may be contacted for any inquiries.
*/
@RequiredField
// @RequiredField
public String email;
/**
* The version of Mastodon installed on the instance.
@@ -84,6 +84,8 @@ public class Instance extends BaseModel{
public V2 v2;
public Pleroma pleroma;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
@@ -193,4 +195,9 @@ public class Instance extends BaseModel{
public boolean enabled;
}
}
@Parcel
public static class Pleroma extends BaseModel {
// metadata etc
}
}

View File

@@ -1,5 +1,7 @@
package org.joinmastodon.android.model;
import androidx.annotation.NonNull;
import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.api.RequiredField;
@@ -11,9 +13,9 @@ public class ListTimeline extends BaseModel {
public String id;
@RequiredField
public String title;
@RequiredField
public RepliesPolicy repliesPolicy;
@NonNull
@Override
public String toString() {
return "List{" +

View File

@@ -18,8 +18,8 @@ public class Notification extends BaseModel implements DisplayItemsParent{
public Instant createdAt;
@RequiredField
public Account account;
public Status status;
public Report report;
@Override
public void postprocess() throws ObjectValidationException{
@@ -48,6 +48,19 @@ public class Notification extends BaseModel implements DisplayItemsParent{
@SerializedName("poll")
POLL,
@SerializedName("status")
STATUS
STATUS,
@SerializedName("update")
UPDATE,
@SerializedName("admin.sign_up")
SIGN_UP,
@SerializedName("admin.report")
REPORT
}
@Parcel
public static class Report {
public String id;
public String comment;
public Account targetAccount;
}
}

View File

@@ -57,6 +57,11 @@ public class Poll extends BaseModel{
public String title;
public Integer votesCount;
public Option() {}
public Option(String title) {
this.title = title;
}
@Override
public String toString(){
return "Option{"+

View File

@@ -45,7 +45,13 @@ public class PushNotification extends BaseModel{
@SerializedName("poll")
POLL(R.string.notification_type_poll),
@SerializedName("status")
STATUS(R.string.sk_notification_type_status);
STATUS(R.string.sk_notification_type_status),
@SerializedName("update")
UPDATE(R.string.sk_notification_type_update),
@SerializedName("admin.sign_up")
SIGN_UP(R.string.sk_sign_ups),
@SerializedName("admin.report")
REPORT(R.string.sk_new_reports);
@StringRes
public final int localizedName;

View File

@@ -23,6 +23,7 @@ public class PushSubscription extends BaseModel implements Cloneable{
", endpoint='"+endpoint+'\''+
", alerts="+alerts+
", serverKey='"+serverKey+'\''+
", policy="+policy+
'}';
}
@@ -44,10 +45,19 @@ public class PushSubscription extends BaseModel implements Cloneable{
public boolean mention;
public boolean poll;
public boolean status;
public boolean update;
// set to true here because i didn't add any items for those to the settings
// (so i don't have to determine whether the user is an admin to show the items or not, and
// admins can still disable those through the android notifications settings)
@SerializedName("admin.sign_up")
public boolean adminSignUp = true;
@SerializedName("admin.report")
public boolean adminReport = true;
public static Alerts ofAll(){
Alerts alerts=new Alerts();
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=true;
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=alerts.update=true;
return alerts;
}
@@ -60,6 +70,9 @@ public class PushSubscription extends BaseModel implements Cloneable{
", mention="+mention+
", poll="+poll+
", status="+status+
", update="+update+
", adminSignUp="+adminSignUp+
", adminReport="+adminReport+
'}';
}

View File

@@ -0,0 +1,81 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.model.Poll.Option;
import org.parceler.Parcel;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
@Parcel
public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
@RequiredField
public String id;
@RequiredField
public Instant scheduledAt;
@RequiredField
public Params params;
@RequiredField
public List<Attachment> mediaAttachments;
@Override
public String getID() {
return id;
}
@Parcel
public static class Params {
@RequiredField
public String text;
public String spoilerText;
@RequiredField
public StatusPrivacy visibility;
public long inReplyToId;
public ScheduledPoll poll;
public boolean sensitive;
public boolean withRateLimit;
public String language;
public String idempotency;
public String applicationId;
public List<String> mediaIds;
}
@Parcel
public static class ScheduledPoll {
@RequiredField
public String expiresIn;
@RequiredField
public List<String> options;
public boolean multiple;
public boolean hideTotals;
public Poll toPoll() {
Poll p = new Poll();
p.voted = true;
p.emojis = List.of();
p.ownVotes = List.of();
p.multiple = multiple;
p.options = options.stream().map(Option::new).collect(Collectors.toList());
return p;
}
}
public Status toStatus() {
Status s = new Status();
s.id = id;
s.mediaAttachments = mediaAttachments;
s.createdAt = scheduledAt;
s.inReplyToId = params.inReplyToId > 0 ? "" + params.inReplyToId : null;
s.content = s.text = params.text;
s.spoilerText = params.spoilerText;
s.visibility = params.visibility;
s.language = params.language;
s.sensitive = params.sensitive;
s.mentions = List.of();
s.tags = List.of();
s.emojis = List.of();
if (params.poll != null) s.poll = params.poll.toPoll();
return s;
}
}

Some files were not shown because too many files have changed in this diff Show More